View Javadoc

1   package org.apache.maven.plugin.ant;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.OutputStreamWriter;
26  import java.net.URL;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Properties;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.model.Repository;
36  import org.apache.maven.model.Resource;
37  import org.apache.maven.project.MavenProject;
38  import org.apache.maven.settings.Settings;
39  import org.apache.tools.ant.Main;
40  import org.codehaus.plexus.util.FileUtils;
41  import org.codehaus.plexus.util.IOUtil;
42  import org.codehaus.plexus.util.StringUtils;
43  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
44  import org.codehaus.plexus.util.xml.XMLWriter;
45  import org.codehaus.plexus.util.xml.XmlWriterUtil;
46  
47  /**
48   * Write Ant build files from <code>Maven Project</code> for <a href="http://ant.apache.org">Ant</a> 1.6.2 or above:
49   * <ul>
50   * <li>build.xml</li>
51   * <li>maven-build.xml</li>
52   * <li>maven-build.properties</li>
53   * </ul>
54   *
55   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
56   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
57   * @version $Id: AntBuildWriter.java 794220 2009-07-15 10:34:34Z bentmann $
58   */
59  public class AntBuildWriter
60  {
61      /**
62       * The default line indenter
63       */
64      protected static final int DEFAULT_INDENTATION_SIZE = XmlWriterUtil.DEFAULT_INDENTATION_SIZE;
65  
66      /**
67       * The default build file name (build.xml)
68       */
69      protected static final String DEFAULT_BUILD_FILENAME = Main.DEFAULT_BUILD_FILENAME;
70  
71      /**
72       * The default generated build file name
73       */
74      protected static final String DEFAULT_MAVEN_BUILD_FILENAME = "maven-build.xml";
75  
76      /**
77       * The default build properties file name
78       */
79      protected static final String DEFAULT_MAVEN_PROPERTIES_FILENAME = "maven-build.properties";
80  
81      private MavenProject project;
82  
83      private ArtifactResolverWrapper artifactResolverWrapper;
84  
85      private File localRepository;
86  
87      private Settings settings;
88  
89      private boolean overwrite;
90  
91      /**
92       * @param project
93       * @param artifactResolverWrapper
94       * @param settings
95       * @param overwrite
96       */
97      public AntBuildWriter( MavenProject project, ArtifactResolverWrapper artifactResolverWrapper, Settings settings,
98                            boolean overwrite )
99      {
100         this.project = project;
101         this.artifactResolverWrapper = artifactResolverWrapper;
102         this.localRepository = new File( artifactResolverWrapper.getLocalRepository().getBasedir() );
103         this.settings = settings;
104         this.overwrite = overwrite;
105     }
106 
107     /**
108      * Generate Ant build XML files
109      *
110      * @throws IOException
111      */
112     protected void writeBuildXmls()
113         throws IOException
114     {
115         writeGeneratedBuildXml();
116         writeBuildXml();
117     }
118 
119     /**
120      * Generate <code>maven-build.properties</code> only for a non-POM project
121      *
122      * @see #DEFAULT_MAVEN_PROPERTIES_FILENAME
123      * @throws IOException
124      */
125     protected void writeBuildProperties()
126         throws IOException
127     {
128         if ( AntBuildWriterUtil.isPomPackaging( project ) )
129         {
130             return;
131         }
132 
133         Properties properties = new Properties();
134 
135         // ----------------------------------------------------------------------
136         // Build properties
137         // ----------------------------------------------------------------------
138 
139         addProperty( properties, "maven.build.finalName", AntBuildWriterUtil.toRelative( project.getBasedir(), project
140             .getBuild().getFinalName() ) );
141 
142         // target
143         addProperty( properties, "maven.build.dir", AntBuildWriterUtil.toRelative( project.getBasedir(), project.getBuild()
144             .getDirectory() ) );
145         addProperty( properties, "project.build.directory", "${maven.build.dir}" );
146 
147         // ${maven.build.dir}/classes
148         addProperty( properties, "maven.build.outputDir", "${maven.build.dir}/"
149             + AntBuildWriterUtil.toRelative( new File( project.getBasedir(), properties.getProperty( "maven.build.dir" ) ),
150                                     project.getBuild().getOutputDirectory() ) );
151         addProperty( properties, "project.build.outputDirectory", "${maven.build.outputDir}" );
152 
153         // src/main/java
154         if ( !project.getCompileSourceRoots().isEmpty() )
155         {
156             String[] compileSourceRoots = (String[]) project.getCompileSourceRoots().toArray( new String[0] );
157             for ( int i = 0; i < compileSourceRoots.length; i++ )
158             {
159                 addProperty( properties, "maven.build.srcDir." + i, AntBuildWriterUtil.toRelative( project.getBasedir(),
160                                                                                           compileSourceRoots[i] ) );
161             }
162         }
163         // src/main/resources
164         if ( project.getBuild().getResources() != null )
165         {
166             Resource[] array = (Resource[]) project.getBuild().getResources().toArray( new Resource[0] );
167             for ( int i = 0; i < array.length; i++ )
168             {
169                 addProperty( properties, "maven.build.resourceDir." + i, AntBuildWriterUtil.toRelative( project.getBasedir(),
170                                                                                                array[i].getDirectory() ) );
171             }
172         }
173 
174         // ${maven.build.dir}/test-classes
175         addProperty( properties, "maven.build.testOutputDir", "${maven.build.dir}/"
176             + AntBuildWriterUtil.toRelative( new File( project.getBasedir(), properties.getProperty( "maven.build.dir" ) ),
177                                     project.getBuild().getTestOutputDirectory() ) );
178         // src/test/java
179         if ( !project.getTestCompileSourceRoots().isEmpty() )
180         {
181             String[] compileSourceRoots = (String[]) project.getTestCompileSourceRoots().toArray( new String[0] );
182             for ( int i = 0; i < compileSourceRoots.length; i++ )
183             {
184                 addProperty( properties, "maven.build.testDir." + i, AntBuildWriterUtil.toRelative( project.getBasedir(),
185                                                                                            compileSourceRoots[i] ) );
186             }
187         }
188         // src/test/resources
189         if ( project.getBuild().getTestResources() != null )
190         {
191             Resource[] array = (Resource[]) project.getBuild().getTestResources().toArray( new Resource[0] );
192             for ( int i = 0; i < array.length; i++ )
193             {
194                 addProperty( properties, "maven.build.testResourceDir." + i, AntBuildWriterUtil
195                     .toRelative( project.getBasedir(), array[i].getDirectory() ) );
196             }
197         }
198 
199         addProperty( properties, "maven.test.reports", "${maven.build.dir}/test-reports" );
200 
201         addProperty( properties, "maven.reporting.outputDirectory", "${maven.build.dir}/site" );
202 
203         // ----------------------------------------------------------------------
204         // Settings properties
205         // ----------------------------------------------------------------------
206 
207         addProperty( properties, "maven.settings.offline", String.valueOf( settings.isOffline() ) );
208         addProperty( properties, "maven.settings.interactiveMode", String.valueOf( settings.isInteractiveMode() ) );
209         addProperty( properties, "maven.repo.local", getLocalRepositoryPath() );
210 
211         // ----------------------------------------------------------------------
212         // Project properties
213         // ----------------------------------------------------------------------
214 
215         if ( project.getProperties() != null )
216         {
217             for ( Iterator it = project.getProperties().entrySet().iterator(); it.hasNext(); )
218             {
219                 Map.Entry property = (Map.Entry) it.next();
220                 addProperty( properties, property.getKey().toString(), property.getValue().toString() );
221             }
222         }
223 
224         FileOutputStream os =
225             new FileOutputStream( new File( project.getBasedir(), DEFAULT_MAVEN_PROPERTIES_FILENAME ) );
226         try
227         {
228             properties.store( os, "Generated by Maven Ant Plugin - DO NOT EDIT THIS FILE!" );
229         }
230         finally
231         {
232             IOUtil.close( os );
233         }
234     }
235 
236     /**
237      * Generate an <code>maven-build.xml</code>
238      *
239      * @see #DEFAULT_MAVEN_BUILD_FILENAME
240      * @throws IOException
241      */
242     private void writeGeneratedBuildXml()
243         throws IOException
244     {
245         // TODO: parameter
246         File outputFile = new File( project.getBasedir(), DEFAULT_MAVEN_BUILD_FILENAME );
247 
248         String encoding = "UTF-8";
249 
250         OutputStreamWriter w = new OutputStreamWriter( new FileOutputStream( outputFile ), encoding );
251 
252         XMLWriter writer = new PrettyPrintXMLWriter( w, StringUtils.repeat( " ", DEFAULT_INDENTATION_SIZE ), encoding,
253                                                      null );
254 
255         // ----------------------------------------------------------------------
256         // <!-- comments -->
257         // ----------------------------------------------------------------------
258 
259         AntBuildWriterUtil.writeHeader( writer );
260 
261         // ----------------------------------------------------------------------
262         // <project/>
263         // ----------------------------------------------------------------------
264 
265         writer.startElement( "project" );
266         writer.addAttribute( "name", project.getArtifactId() + "-from-maven" );
267         writer.addAttribute( "default", "package" );
268         writer.addAttribute( "basedir", "." );
269 
270         XmlWriterUtil.writeLineBreak( writer );
271 
272         // ----------------------------------------------------------------------
273         // <property/>
274         // ----------------------------------------------------------------------
275 
276         writeProperties( writer );
277 
278         // ----------------------------------------------------------------------
279         // <path/>
280         // ----------------------------------------------------------------------
281 
282         writeBuildPathDefinition( writer );
283 
284         // ----------------------------------------------------------------------
285         // <target name="clean" />
286         // ----------------------------------------------------------------------
287 
288         writeCleanTarget( writer );
289 
290         // ----------------------------------------------------------------------
291         // <target name="compile" />
292         // ----------------------------------------------------------------------
293 
294         List compileSourceRoots = AntBuildWriterUtil.removeEmptyCompileSourceRoots( project.getCompileSourceRoots() );
295         writeCompileTarget( writer, compileSourceRoots );
296 
297         // ----------------------------------------------------------------------
298         // <target name="compile-tests" />
299         // ----------------------------------------------------------------------
300 
301         List testCompileSourceRoots = AntBuildWriterUtil.removeEmptyCompileSourceRoots( project
302             .getTestCompileSourceRoots() );
303         writeCompileTestsTarget( writer, testCompileSourceRoots );
304 
305         // ----------------------------------------------------------------------
306         // <target name="test" />
307         // ----------------------------------------------------------------------
308 
309         writeTestTargets( writer, testCompileSourceRoots );
310 
311         // ----------------------------------------------------------------------
312         // <target name="javadoc" />
313         // ----------------------------------------------------------------------
314         writeJavadocTarget( writer );
315 
316         // ----------------------------------------------------------------------
317         // <target name="package" />
318         // ----------------------------------------------------------------------
319         writePackageTarget( writer );
320 
321         // ----------------------------------------------------------------------
322         // <target name="get-deps" />
323         // ----------------------------------------------------------------------
324         writeGetDepsTarget( writer );
325 
326         XmlWriterUtil.writeLineBreak( writer );
327 
328         writer.endElement(); // project
329 
330         XmlWriterUtil.writeLineBreak( writer );
331 
332         IOUtil.close( w );
333     }
334 
335     /**
336      * Generate an generic <code>build.xml</code> if not already exist
337      *
338      * @see #DEFAULT_BUILD_FILENAME
339      * @throws IOException
340      */
341     private void writeBuildXml()
342         throws IOException
343     {
344         File outputFile = new File( project.getBasedir(), DEFAULT_BUILD_FILENAME );
345 
346         if ( outputFile.exists() && !overwrite )
347         {
348             return;
349         }
350 
351         String encoding = "UTF-8";
352 
353         OutputStreamWriter w = new OutputStreamWriter( new FileOutputStream( outputFile ), encoding );
354 
355         XMLWriter writer = new PrettyPrintXMLWriter( w, StringUtils.repeat( " ", DEFAULT_INDENTATION_SIZE ), encoding,
356                                                      null );
357 
358         // ----------------------------------------------------------------------
359         // <!-- comments -->
360         // ----------------------------------------------------------------------
361 
362         AntBuildWriterUtil.writeAntVersionHeader( writer );
363 
364         // ----------------------------------------------------------------------
365         // <project/>
366         // ----------------------------------------------------------------------
367 
368         writer.startElement( "project" );
369         writer.addAttribute( "name", project.getArtifactId() );
370         writer.addAttribute( "default", "package" );
371         writer.addAttribute( "basedir", "." );
372 
373         XmlWriterUtil.writeLineBreak( writer );
374 
375         XmlWriterUtil.writeCommentText( writer, "Import " + DEFAULT_MAVEN_BUILD_FILENAME
376             + " into the current project", 1 );
377 
378         writer.startElement( "import" );
379         writer.addAttribute( "file", DEFAULT_MAVEN_BUILD_FILENAME );
380         writer.endElement(); // import
381 
382         XmlWriterUtil.writeLineBreak( writer, 1, 1 );
383 
384         XmlWriterUtil.writeCommentText( writer, "Help target", 1 );
385 
386         writer.startElement( "target" );
387         writer.addAttribute( "name", "help" );
388 
389         writer.startElement( "echo" );
390         writer.addAttribute( "message", "Please run: $ant -projecthelp" );
391         writer.endElement(); // echo
392 
393         writer.endElement(); // target
394 
395         XmlWriterUtil.writeLineBreak( writer, 2 );
396 
397         writer.endElement(); // project
398 
399         XmlWriterUtil.writeLineBreak( writer );
400 
401         IOUtil.close( w );
402     }
403 
404     /**
405      * Write properties in the writer only for a non-POM project.
406      *
407      * @param writer
408      */
409     private void writeProperties( XMLWriter writer )
410     {
411         if ( AntBuildWriterUtil.isPomPackaging( project ) )
412         {
413             return;
414         }
415 
416         // TODO: optional in m1
417         // TODO: USD properties
418         XmlWriterUtil.writeCommentText( writer, "Build environment properties", 1 );
419 
420         // ----------------------------------------------------------------------
421         // File properties to override local properties
422         // ----------------------------------------------------------------------
423 
424         writer.startElement( "property" );
425         writer.addAttribute( "file", "${user.home}/.m2/maven.properties" );
426         writer.endElement(); // property
427 
428         writer.startElement( "property" );
429         writer.addAttribute( "file", DEFAULT_MAVEN_PROPERTIES_FILENAME );
430         writer.endElement(); // property
431 
432         // ----------------------------------------------------------------------
433         // Build properties
434         // ----------------------------------------------------------------------
435 
436         XmlWriterUtil.writeLineBreak( writer, 2, 1 );
437 
438         writer.startElement( "property" );
439         writer.addAttribute( "name", "maven.build.finalName" );
440         writer.addAttribute( "value", project.getBuild().getFinalName() );
441         writer.endElement(); // property
442 
443         writer.startElement( "property" );
444         writer.addAttribute( "name", "maven.build.dir" );
445         writer.addAttribute( "value", AntBuildWriterUtil.toRelative( project.getBasedir(), project.getBuild().getDirectory() ) );
446         writer.endElement(); // property
447 
448         writer.startElement( "property" );
449         writer.addAttribute( "name", "maven.build.outputDir" );
450         writer.addAttribute( "value", "${maven.build.dir}/"
451             + AntBuildWriterUtil.toRelative( new File( project.getBuild().getDirectory() ), project.getBuild()
452                 .getOutputDirectory() ) );
453         writer.endElement(); // property
454 
455         if ( !project.getCompileSourceRoots().isEmpty() )
456         {
457             String[] compileSourceRoots = (String[]) project.getCompileSourceRoots().toArray( new String[0] );
458             for ( int i = 0; i < compileSourceRoots.length; i++ )
459             {
460                 writer.startElement( "property" );
461                 writer.addAttribute( "name", "maven.build.srcDir." + i );
462                 writer.addAttribute( "value", AntBuildWriterUtil.toRelative( project.getBasedir(), compileSourceRoots[i] ) );
463                 writer.endElement(); // property
464             }
465         }
466 
467         if ( project.getBuild().getResources() != null )
468         {
469             Resource[] array = (Resource[]) project.getBuild().getResources().toArray( new Resource[0] );
470             for ( int i = 0; i < array.length; i++ )
471             {
472                 writer.startElement( "property" );
473                 writer.addAttribute( "name", "maven.build.resourceDir." + i );
474                 writer.addAttribute( "value", AntBuildWriterUtil.toRelative( project.getBasedir(), array[i].getDirectory() ) );
475                 writer.endElement(); // property
476             }
477         }
478 
479         writer.startElement( "property" );
480         writer.addAttribute( "name", "maven.build.testOutputDir" );
481         writer.addAttribute( "value", "${maven.build.dir}/"
482             + AntBuildWriterUtil.toRelative( new File( project.getBuild().getDirectory() ), project.getBuild()
483                 .getTestOutputDirectory() ) );
484         writer.endElement(); // property
485 
486         if ( !project.getTestCompileSourceRoots().isEmpty() )
487         {
488             String[] compileSourceRoots = (String[]) project.getTestCompileSourceRoots().toArray( new String[0] );
489             for ( int i = 0; i < compileSourceRoots.length; i++ )
490             {
491                 writer.startElement( "property" );
492                 writer.addAttribute( "name", "maven.build.testDir." + i );
493                 writer.addAttribute( "value", AntBuildWriterUtil.toRelative( project.getBasedir(), compileSourceRoots[i] ) );
494                 writer.endElement(); // property
495             }
496         }
497 
498         if ( project.getBuild().getTestResources() != null )
499         {
500             Resource[] array = (Resource[]) project.getBuild().getTestResources().toArray( new Resource[0] );
501             for ( int i = 0; i < array.length; i++ )
502             {
503                 writer.startElement( "property" );
504                 writer.addAttribute( "name", "maven.build.testResourceDir." + i );
505                 writer.addAttribute( "value", AntBuildWriterUtil.toRelative( project.getBasedir(), array[i].getDirectory() ) );
506                 writer.endElement(); // property
507             }
508         }
509 
510         writer.startElement( "property" );
511         writer.addAttribute( "name", "maven.test.reports" );
512         writer.addAttribute( "value", "${maven.build.dir}/test-reports" );
513         writer.endElement(); // property
514 
515         String reportingOutputDir = project.getReporting().getOutputDirectory();
516         // workaround for MNG-3475
517         if ( !new File( reportingOutputDir ).isAbsolute() )
518         {
519             reportingOutputDir = new File( project.getBasedir(), reportingOutputDir ).getAbsolutePath();
520         }
521         writer.startElement( "property" );
522         writer.addAttribute( "name", "maven.reporting.outputDirectory" );
523         writer.addAttribute( "value", "${maven.build.dir}/"
524             + AntBuildWriterUtil.toRelative( new File( project.getBuild().getDirectory() ), reportingOutputDir ) );
525         writer.endElement(); // property
526 
527         // ----------------------------------------------------------------------
528         // Setting properties
529         // ----------------------------------------------------------------------
530 
531         XmlWriterUtil.writeLineBreak( writer, 2, 1 );
532 
533         writer.startElement( "property" );
534         writer.addAttribute( "name", "maven.repo.local" );
535         writer.addAttribute( "value", "${user.home}/.m2/repository" );
536         writer.endElement(); // property
537 
538         writer.startElement( "property" );
539         writer.addAttribute( "name", "maven.settings.offline" );
540         writer.addAttribute( "value", String.valueOf( settings.isOffline() ) );
541         writer.endElement(); // property
542 
543         writer.startElement( "property" );
544         writer.addAttribute( "name", "maven.settings.interactiveMode" );
545         writer.addAttribute( "value", String.valueOf( settings.isInteractiveMode() ) );
546         writer.endElement(); // property
547 
548         XmlWriterUtil.writeLineBreak( writer );
549     }
550 
551     /**
552      * Check if the local repository is in the default location:
553      * <code>${user.home}/.m2/repository</code>. If that is the case then return
554      * the path with the system property "user.home" in it. If not then just
555      * return the absolute path to the local repository.
556      */
557     private String getLocalRepositoryPath()
558     {
559         String userHome = System.getProperty( "user.home" );
560         String defaultPath = ( userHome + "/.m2/repository" ).replace( '\\', '/' );
561         String actualPath = localRepository.getAbsolutePath().replace( '\\', '/' );
562         if ( actualPath.equals( defaultPath ) )
563         {
564             return "${user.home}/.m2/repository";
565         }
566         else
567         {
568             return localRepository.getAbsolutePath();
569         }
570     }
571 
572     /**
573      * Write path definition in the writer only for a non-POM project.
574      *
575      * @param writer
576      */
577     private void writeBuildPathDefinition( XMLWriter writer )
578     {
579         if ( AntBuildWriterUtil.isPomPackaging( project ) )
580         {
581             return;
582         }
583 
584         XmlWriterUtil.writeCommentText( writer, "Defining classpaths", 1 );
585 
586         writer.startElement( "path" );
587         writer.addAttribute( "id", "build.classpath" );
588         writer.startElement( "fileset" );
589         writer.addAttribute( "dir", "${maven.repo.local}" );
590         if ( !project.getCompileArtifacts().isEmpty() )
591         {
592             for ( Iterator i = project.getCompileArtifacts().iterator(); i.hasNext(); )
593             {
594                 Artifact artifact = (Artifact) i.next();
595                 String path = artifactResolverWrapper.getLocalArtifactPath( artifact );
596                 writer.startElement( "include" );
597                 writer.addAttribute( "name", path );
598                 writer.endElement(); // include
599             }
600         }
601         else
602         {
603             writer.startElement( "include" );
604             writer.addAttribute( "name", "*.jar" );
605             writer.endElement(); // include
606         }
607         writer.endElement(); // fileset
608         writer.endElement(); // path
609 
610         writer.startElement( "path" );
611         writer.addAttribute( "id", "build.test.classpath" );
612         writer.startElement( "fileset" );
613         writer.addAttribute( "dir", "${maven.repo.local}" );
614         if ( !project.getTestArtifacts().isEmpty() )
615         {
616             for ( Iterator i = project.getTestArtifacts().iterator(); i.hasNext(); )
617             {
618                 Artifact artifact = (Artifact) i.next();
619                 String path = artifactResolverWrapper.getLocalArtifactPath( artifact );
620                 writer.startElement( "include" );
621                 writer.addAttribute( "name", path );
622                 writer.endElement(); // include
623             }
624         }
625         else
626         {
627             writer.startElement( "include" );
628             writer.addAttribute( "name", "*.jar" );
629             writer.endElement(); // include
630         }
631         writer.endElement(); // fileset
632         writer.endElement(); // path
633 
634         XmlWriterUtil.writeLineBreak( writer );
635     }
636 
637     /**
638      * Write clean target in the writer depending the packaging of the project.
639      *
640      * @param writer
641      */
642     private void writeCleanTarget( XMLWriter writer )
643     {
644         XmlWriterUtil.writeCommentText( writer, "Cleaning up target", 1 );
645 
646         writer.startElement( "target" );
647         writer.addAttribute( "name", "clean" );
648         writer.addAttribute( "description", "Clean the output directory" );
649 
650         if ( AntBuildWriterUtil.isPomPackaging( project ) )
651         {
652             if ( project.getModules() != null )
653             {
654                 for ( Iterator it = project.getModules().iterator(); it.hasNext(); )
655                 {
656                     String moduleSubPath = (String) it.next();
657                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "clean" );
658                 }
659             }
660         }
661         else
662         {
663             writer.startElement( "delete" );
664             writer.addAttribute( "dir", "${maven.build.dir}" );
665             writer.endElement(); // delete
666         }
667 
668         writer.endElement(); // target
669 
670         XmlWriterUtil.writeLineBreak( writer );
671     }
672 
673     /**
674      * Write compile target in the writer depending the packaging of the project.
675      *
676      * @param writer
677      * @param compileSourceRoots
678      * @throws IOException if any
679      */
680     private void writeCompileTarget( XMLWriter writer, List compileSourceRoots )
681         throws IOException
682     {
683         XmlWriterUtil.writeCommentText( writer, "Compilation target", 1 );
684 
685         if ( AntBuildWriterUtil.isPomPackaging( project ) )
686         {
687             writer.startElement( "target" );
688             writer.addAttribute( "name", "compile" );
689             writer.addAttribute( "description", "Compile the code" );
690             if ( project.getModules() != null )
691             {
692                 for ( Iterator it = project.getModules().iterator(); it.hasNext(); )
693                 {
694                     String moduleSubPath = (String) it.next();
695                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "compile" );
696                 }
697             }
698             writer.endElement(); // target
699         }
700         else
701         {
702             writer.startElement( "target" );
703             writer.addAttribute( "name", "compile" );
704             writer.addAttribute( "depends", "get-deps" );
705             writer.addAttribute( "description", "Compile the code" );
706 
707             writeCompileTasks( writer, "${maven.build.outputDir}", compileSourceRoots,
708                                project.getBuild().getResources(), null, false );
709 
710             writer.endElement(); // target
711         }
712 
713         XmlWriterUtil.writeLineBreak( writer );
714     }
715 
716     /**
717      * Write compile-test target in the writer depending the packaging of the project.
718      *
719      * @param writer
720      * @param testCompileSourceRoots
721      * @throws IOException if any
722      */
723     private void writeCompileTestsTarget( XMLWriter writer, List testCompileSourceRoots )
724         throws IOException
725     {
726         XmlWriterUtil.writeCommentText( writer, "Test-compilation target", 1 );
727 
728         if ( AntBuildWriterUtil.isPomPackaging( project ) )
729         {
730             writer.startElement( "target" );
731             writer.addAttribute( "name", "compile-tests" );
732             writer.addAttribute( "description", "Compile the test code" );
733             if ( project.getModules() != null )
734             {
735                 for ( Iterator it = project.getModules().iterator(); it.hasNext(); )
736                 {
737                     String moduleSubPath = (String) it.next();
738                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "compile-tests" );
739                 }
740             }
741             writer.endElement(); // target
742         }
743         else
744         {
745             writer.startElement( "target" );
746             writer.addAttribute( "name", "compile-tests" );
747             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "compile", 2 );
748             AntBuildWriterUtil.addWrapAttribute( writer, "target", "description", "Compile the test code", 2 );
749             AntBuildWriterUtil.addWrapAttribute( writer, "target", "unless", "maven.test.skip", 2 );
750 
751             writeCompileTasks( writer, "${maven.build.testOutputDir}", testCompileSourceRoots,
752                                project.getBuild().getTestResources(), "${maven.build.outputDir}", true );
753 
754             writer.endElement(); // target
755         }
756 
757         XmlWriterUtil.writeLineBreak( writer );
758     }
759 
760     /**
761      * Write test target in the writer depending the packaging of the project.
762      *
763      * @param writer
764      * @param testCompileSourceRoots
765      */
766     private void writeTestTargets( XMLWriter writer, List testCompileSourceRoots )
767         throws IOException
768     {
769         XmlWriterUtil.writeCommentText( writer, "Run all tests", 1 );
770 
771         if ( AntBuildWriterUtil.isPomPackaging( project ) )
772         {
773             writer.startElement( "target" );
774             writer.addAttribute( "name", "test" );
775             writer.addAttribute( "description", "Run the test cases" );
776             if ( project.getModules() != null )
777             {
778                 for ( Iterator it = project.getModules().iterator(); it.hasNext(); )
779                 {
780                     String moduleSubPath = (String) it.next();
781                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "test" );
782                 }
783             }
784             writer.endElement(); // target
785         }
786         else
787         {
788             writer.startElement( "target" );
789             writer.addAttribute( "name", "test" );
790             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "compile-tests, junit-missing", 2 );
791             AntBuildWriterUtil.addWrapAttribute( writer, "target", "unless", "junit.skipped", 2 );
792             AntBuildWriterUtil.addWrapAttribute( writer, "target", "description", "Run the test cases", 2 );
793 
794             if ( !testCompileSourceRoots.isEmpty() )
795             {
796                 writer.startElement( "mkdir" );
797                 writer.addAttribute( "dir", "${maven.test.reports}" );
798                 writer.endElement(); // mkdir
799 
800                 writer.startElement( "junit" );
801                 writer.addAttribute( "printSummary", "yes" );
802                 writer.addAttribute( "haltonerror", "true" );
803                 writer.addAttribute( "haltonfailure", "true" );
804                 writer.addAttribute( "fork", "true" );
805                 writer.addAttribute( "dir", "." );
806 
807                 writer.startElement( "sysproperty" );
808                 writer.addAttribute( "key", "basedir" );
809                 writer.addAttribute( "value", "." );
810                 writer.endElement(); // sysproperty
811 
812                 writer.startElement( "formatter" );
813                 writer.addAttribute( "type", "xml" );
814                 writer.endElement(); // formatter
815 
816                 writer.startElement( "formatter" );
817                 writer.addAttribute( "type", "plain" );
818                 writer.addAttribute( "usefile", "false" );
819                 writer.endElement(); // formatter
820 
821                 writer.startElement( "classpath" );
822                 writer.startElement( "path" );
823                 writer.addAttribute( "refid", "build.test.classpath" );
824                 writer.endElement(); // path
825                 writer.startElement( "pathelement" );
826                 writer.addAttribute( "location", "${maven.build.outputDir}" );
827                 writer.endElement(); // pathelement
828                 writer.startElement( "pathelement" );
829                 writer.addAttribute( "location", "${maven.build.testOutputDir}" );
830                 writer.endElement(); // pathelement
831                 writer.endElement(); // classpath
832 
833                 writer.startElement( "batchtest" );
834                 writer.addAttribute( "todir", "${maven.test.reports}" );
835                 writer.addAttribute( "unless", "test" );
836 
837                 List includes = getTestIncludes();
838                 List excludes = getTestExcludes();
839 
840                 writeTestFilesets( writer, testCompileSourceRoots, includes, excludes );
841 
842                 writer.endElement(); // batchtest
843 
844                 writer.startElement( "batchtest" );
845                 writer.addAttribute( "todir", "${maven.test.reports}" );
846                 writer.addAttribute( "if", "test" );
847 
848                 includes = Arrays.asList( new String[] { "**/${test}.java" } );
849 
850                 writeTestFilesets( writer, testCompileSourceRoots, includes, excludes );
851 
852                 writer.endElement(); // batchtest
853 
854                 writer.endElement(); // junit
855             }
856             writer.endElement(); // target
857 
858             XmlWriterUtil.writeLineBreak( writer, 2, 1 );
859 
860             writer.startElement( "target" );
861             writer.addAttribute( "name", "test-junit-present" );
862 
863             writer.startElement( "available" );
864             writer.addAttribute( "classname", "junit.framework.Test" );
865             writer.addAttribute( "property", "junit.present" );
866             writer.endElement(); // available
867 
868             writer.endElement(); // target
869 
870             XmlWriterUtil.writeLineBreak( writer, 2, 1 );
871 
872             writer.startElement( "target" );
873             writer.addAttribute( "name", "test-junit-status" );
874             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "test-junit-present", 2 );
875             writer.startElement( "condition" );
876             writer.addAttribute( "property", "junit.missing" );
877             writer.startElement( "and" );
878             writer.startElement( "isfalse" );
879             writer.addAttribute( "value", "${junit.present}" );
880             writer.endElement(); // isfalse
881             writer.startElement( "isfalse" );
882             writer.addAttribute( "value", "${maven.test.skip}" );
883             writer.endElement(); // isfalse
884             writer.endElement(); // and
885             writer.endElement(); // condition
886             writer.startElement( "condition" );
887             writer.addAttribute( "property", "junit.skipped" );
888             writer.startElement( "or" );
889             writer.startElement( "isfalse" );
890             writer.addAttribute( "value", "${junit.present}" );
891             writer.endElement(); // isfalse
892             writer.startElement( "istrue" );
893             writer.addAttribute( "value", "${maven.test.skip}" );
894             writer.endElement(); // istrue
895             writer.endElement(); // or
896             writer.endElement(); // condition
897             writer.endElement(); // target
898 
899             XmlWriterUtil.writeLineBreak( writer, 2, 1 );
900 
901             writer.startElement( "target" );
902             writer.addAttribute( "name", "junit-missing" );
903             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "test-junit-status", 2 );
904             AntBuildWriterUtil.addWrapAttribute( writer, "target", "if", "junit.missing", 2 );
905 
906             writer.startElement( "echo" );
907             writer.writeText( StringUtils.repeat( "=", 35 ) + " WARNING " + StringUtils.repeat( "=", 35 ) );
908             writer.endElement(); // echo
909 
910             writer.startElement( "echo" );
911             writer.writeText( " JUnit is not present in your $ANT_HOME/lib directory. Tests not executed." );
912             writer.endElement(); // echo
913 
914             writer.startElement( "echo" );
915             writer.writeText( StringUtils.repeat( "=", 79 ) );
916             writer.endElement(); // echo
917 
918             writer.endElement(); // target
919         }
920 
921         XmlWriterUtil.writeLineBreak( writer );
922     }
923 
924     /**
925      * Gets the include patterns for the unit tests.
926      *
927      * @return A list of strings with include patterns, might be empty but never <code>null</code>.
928      */
929     private List getTestIncludes()
930         throws IOException
931     {
932         List includes = getSelectorList( AntBuildWriterUtil.getMavenSurefirePluginOptions( project, "includes", null ) );
933         if ( includes == null || includes.isEmpty() )
934         {
935             includes = Arrays.asList( new String[] { "**/Test*.java", "**/*Test.java", "**/*TestCase.java" } );
936         }
937         return includes;
938     }
939 
940     /**
941      * Gets the exclude patterns for the unit tests.
942      *
943      * @return A list of strings with exclude patterns, might be empty but never <code>null</code>.
944      */
945     private List getTestExcludes()
946         throws IOException
947     {
948         List excludes = getSelectorList( AntBuildWriterUtil.getMavenSurefirePluginOptions( project, "excludes", null ) );
949         if ( excludes == null || excludes.isEmpty() )
950         {
951             excludes = Arrays.asList( new String[] { "**/*Abstract*Test.java" } );
952         }
953         return excludes;
954     }
955 
956     /**
957      * Write the <code>&lt;fileset&gt;</code> elements for the test compile source roots.
958      *
959      * @param writer
960      * @param testCompileSourceRoots
961      * @param includes
962      * @param excludes
963      */
964     private void writeTestFilesets( XMLWriter writer, List testCompileSourceRoots, List includes, List excludes )
965     {
966         for ( int i = 0; i < testCompileSourceRoots.size(); i++ )
967         {
968             writer.startElement( "fileset" );
969             writer.addAttribute( "dir", "${maven.build.testDir." + i + "}" );
970             // TODO: m1 allows additional test exclusions via maven.ant.excludeTests
971             AntBuildWriterUtil.writeIncludesExcludes( writer, includes, excludes );
972             writer.endElement(); // fileset
973         }
974     }
975 
976     /**
977      * Write javadoc target in the writer depending the packaging of the project.
978      *
979      * @param writer
980      * @throws IOException if any
981      */
982     private void writeJavadocTarget( XMLWriter writer )
983         throws IOException
984     {
985         XmlWriterUtil.writeCommentText( writer, "Javadoc target", 1 );
986 
987         writer.startElement( "target" );
988         writer.addAttribute( "name", "javadoc" );
989         writer.addAttribute( "description", "Generates the Javadoc of the application" );
990 
991         if ( AntBuildWriterUtil.isPomPackaging( project ) )
992         {
993             if ( project.getModules() != null )
994             {
995                 for ( Iterator it = project.getModules().iterator(); it.hasNext(); )
996                 {
997                     String moduleSubPath = (String) it.next();
998                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "javadoc" );
999                 }
1000             }
1001         }
1002         else
1003         {
1004             AntBuildWriterUtil.writeJavadocTask( writer, project, artifactResolverWrapper );
1005         }
1006 
1007         writer.endElement(); // target
1008 
1009         XmlWriterUtil.writeLineBreak( writer );
1010     }
1011 
1012     /**
1013      * Write package target in the writer depending the packaging of the project.
1014      *
1015      * @param writer
1016      * @throws IOException if any
1017      */
1018     private void writePackageTarget( XMLWriter writer )
1019         throws IOException
1020     {
1021         String synonym = null; // type of the package we are creating (for example jar)
1022         XmlWriterUtil.writeCommentText( writer, "Package target", 1 );
1023 
1024         writer.startElement( "target" );
1025         writer.addAttribute( "name", "package" );
1026 
1027         if ( !AntBuildWriterUtil.isPomPackaging( project ) )
1028         {
1029             writer.addAttribute( "depends", "compile,test" );
1030         }
1031         writer.addAttribute( "description", "Package the application" );
1032 
1033         if ( AntBuildWriterUtil.isPomPackaging( project ) )
1034         {
1035             if ( project.getModules() != null )
1036             {
1037                 for ( Iterator it = project.getModules().iterator(); it.hasNext(); )
1038                 {
1039                     String moduleSubPath = (String) it.next();
1040                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "package" );
1041                 }
1042             }
1043         }
1044         else
1045         {
1046             if ( AntBuildWriterUtil.isJarPackaging( project ) )
1047             {
1048                 AntBuildWriterUtil.writeJarTask( writer, project );
1049                 synonym = "jar";
1050             }
1051             else if ( AntBuildWriterUtil.isEarPackaging( project ) )
1052             {
1053                 AntBuildWriterUtil.writeEarTask( writer, project, artifactResolverWrapper );
1054                 synonym = "ear";
1055             }
1056             else if ( AntBuildWriterUtil.isWarPackaging( project ) )
1057             {
1058                 AntBuildWriterUtil.writeWarTask( writer, project, artifactResolverWrapper );
1059                 synonym = "war";
1060             }
1061             else
1062             {
1063                 writer.startElement( "echo" );
1064                 writer.addAttribute( "message", "No Ant task exists for the packaging '" + project.getPackaging()
1065                     + "'. " + "You could overrided the Ant package target in your build.xml." );
1066                 writer.endElement(); // echo
1067             }
1068         }
1069 
1070         writer.endElement(); // target
1071 
1072         XmlWriterUtil.writeLineBreak( writer );
1073 
1074         if ( synonym != null )
1075         {
1076             XmlWriterUtil.writeCommentText( writer,
1077                                                  "A dummy target for the package named after the type it creates", 1 );
1078             writer.startElement( "target" );
1079             writer.addAttribute( "name", synonym );
1080             writer.addAttribute( "depends", "package" );
1081             writer.addAttribute( "description", "Builds the " + synonym + " for the application" );
1082             writer.endElement(); //target
1083 
1084             XmlWriterUtil.writeLineBreak( writer );
1085         }
1086     }
1087 
1088     private void writeCompileTasks( XMLWriter writer, String outputDirectory, List compileSourceRoots,
1089                                    List resources, String additionalClassesDirectory, boolean isTest )
1090         throws IOException
1091     {
1092         writer.startElement( "mkdir" );
1093         writer.addAttribute( "dir", outputDirectory );
1094         writer.endElement(); // mkdir
1095 
1096         if ( !compileSourceRoots.isEmpty() )
1097         {
1098             writer.startElement( "javac" );
1099             writer.addAttribute( "destdir", outputDirectory );
1100             Map[] includes = AntBuildWriterUtil.getMavenCompilerPluginOptions( project, "includes", null );
1101             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "includes", getCommaSeparatedList( includes,
1102                                                                                                      "include" ), 3 );
1103             Map[] excludes = AntBuildWriterUtil.getMavenCompilerPluginOptions( project, "excludes", null );
1104             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "excludes", getCommaSeparatedList( excludes,
1105                                                                                                      "exclude" ), 3 );
1106             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "encoding", AntBuildWriterUtil
1107                 .getMavenCompilerPluginBasicOption( project, "encoding", null ), 3 );
1108             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "nowarn", AntBuildWriterUtil
1109                 .getMavenCompilerPluginBasicOption( project, "showWarnings", "false" ), 3 );
1110             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "debug", AntBuildWriterUtil
1111                 .getMavenCompilerPluginBasicOption( project, "debug", "true" ), 3 );
1112             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "optimize", AntBuildWriterUtil
1113                 .getMavenCompilerPluginBasicOption( project, "optimize", "false" ), 3 );
1114             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "deprecation", AntBuildWriterUtil
1115                 .getMavenCompilerPluginBasicOption( project, "showDeprecation", "true" ), 3 );
1116             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "target", AntBuildWriterUtil
1117                 .getMavenCompilerPluginBasicOption( project, "target", "1.1" ), 3 );
1118             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "verbose", AntBuildWriterUtil
1119                 .getMavenCompilerPluginBasicOption( project, "verbose", "false" ), 3 );
1120             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "fork", AntBuildWriterUtil
1121                 .getMavenCompilerPluginBasicOption( project, "fork", "false" ), 3 );
1122             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "memoryMaximumSize", AntBuildWriterUtil
1123                 .getMavenCompilerPluginBasicOption( project, "meminitial", null ), 3 );
1124             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "memoryInitialSize", AntBuildWriterUtil
1125                 .getMavenCompilerPluginBasicOption( project, "maxmem", null ), 3 );
1126             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "source", AntBuildWriterUtil
1127                 .getMavenCompilerPluginBasicOption( project, "source", "1.3" ), 3 );
1128 
1129             String[] compileSourceRootsArray = (String[]) compileSourceRoots.toArray( new String[0] );
1130             for ( int i = 0; i < compileSourceRootsArray.length; i++ )
1131             {
1132                 writer.startElement( "src" );
1133                 writer.startElement( "pathelement" );
1134                 if ( isTest )
1135                 {
1136                     writer.addAttribute( "location", "${maven.build.testDir." + i + "}" );
1137                 }
1138                 else
1139                 {
1140                     writer.addAttribute( "location", "${maven.build.srcDir." + i + "}" );
1141                 }
1142                 writer.endElement(); // pathelement
1143                 writer.endElement(); // src
1144             }
1145 
1146             if ( additionalClassesDirectory == null )
1147             {
1148                 writer.startElement( "classpath" );
1149                 if ( isTest )
1150                 {
1151                     writer.addAttribute( "refid", "build.test.classpath" );
1152                 }
1153                 else
1154                 {
1155                     writer.addAttribute( "refid", "build.classpath" );
1156                 }
1157                 writer.endElement(); // classpath
1158             }
1159             else
1160             {
1161                 writer.startElement( "classpath" );
1162                 writer.startElement( "path" );
1163                 if ( isTest )
1164                 {
1165                     writer.addAttribute( "refid", "build.test.classpath" );
1166                 }
1167                 else
1168                 {
1169                     writer.addAttribute( "refid", "build.classpath" );
1170                 }
1171                 writer.endElement(); // path
1172                 writer.startElement( "pathelement" );
1173                 writer.addAttribute( "location", additionalClassesDirectory );
1174                 writer.endElement(); // pathelement
1175                 writer.endElement(); // classpath
1176             }
1177 
1178             writer.endElement(); // javac
1179         }
1180 
1181         Resource[] array = (Resource[]) resources.toArray( new Resource[0] );
1182         for ( int i = 0; i < array.length; i++ )
1183         {
1184             Resource resource = array[i];
1185 
1186             if ( new File( resource.getDirectory() ).exists() )
1187             {
1188                 String outputDir = outputDirectory;
1189                 if ( resource.getTargetPath() != null && resource.getTargetPath().length() > 0 )
1190                 {
1191                     outputDir = outputDir + "/" + resource.getTargetPath();
1192 
1193                     writer.startElement( "mkdir" );
1194                     writer.addAttribute( "dir", outputDir );
1195                     writer.endElement(); // mkdir
1196                 }
1197 
1198                 writer.startElement( "copy" );
1199                 writer.addAttribute( "todir", outputDir );
1200 
1201                 writer.startElement( "fileset" );
1202                 if ( isTest )
1203                 {
1204                     writer.addAttribute( "dir", "${maven.build.testResourceDir." + i + "}" );
1205                 }
1206                 else
1207                 {
1208                     writer.addAttribute( "dir", "${maven.build.resourceDir." + i + "}" );
1209                 }
1210 
1211                 AntBuildWriterUtil.writeIncludesExcludes( writer, resource.getIncludes(), resource.getExcludes() );
1212 
1213                 writer.endElement(); // fileset
1214 
1215                 writer.endElement(); // copy
1216             }
1217         }
1218     }
1219 
1220     /**
1221      * Write get-deps target in the writer only for a non-POM project
1222      *
1223      * @param writer
1224      */
1225     private void writeGetDepsTarget( XMLWriter writer )
1226     {
1227         if ( AntBuildWriterUtil.isPomPackaging( project ) )
1228         {
1229             return;
1230         }
1231 
1232         XmlWriterUtil.writeCommentText( writer, "Download dependencies target", 1 );
1233 
1234         writer.startElement( "target" );
1235         writer.addAttribute( "name", "test-offline" );
1236 
1237         writer.startElement( "condition" );
1238         writer.addAttribute( "property", "maven.mode.offline" );
1239         writer.startElement( "equals" );
1240         writer.addAttribute( "arg1", "${maven.settings.offline}" );
1241         writer.addAttribute( "arg2", "true" );
1242         writer.endElement(); // equals
1243         writer.endElement(); // condition
1244         writer.endElement(); // target
1245 
1246         XmlWriterUtil.writeLineBreak( writer, 2, 1 );
1247 
1248         writer.startElement( "target" );
1249         writer.addAttribute( "name", "get-deps" );
1250         AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "test-offline", 2 );
1251         AntBuildWriterUtil.addWrapAttribute( writer, "target", "description", "Download all dependencies", 2 );
1252         AntBuildWriterUtil.addWrapAttribute( writer, "target", "unless", "maven.mode.offline", 2 ); // TODO: check, and differs from m1
1253 
1254         writer.startElement( "mkdir" );
1255         writer.addAttribute( "dir", "${maven.repo.local}" );
1256         writer.endElement(); // mkdir
1257 
1258         String basedir = project.getBasedir().getAbsolutePath();
1259 
1260         // TODO: proxy - probably better to use wagon!
1261         for ( Iterator i = project.getTestArtifacts().iterator(); i.hasNext(); )
1262         {
1263             Artifact artifact = (Artifact) i.next();
1264 
1265             // TODO: should the artifacthandler be used instead?
1266             String path = artifactResolverWrapper.getLocalArtifactPath( artifact );
1267 
1268             if ( !new File( path ).exists() )
1269             {
1270                 File parentDirs = new File( path ).getParentFile();
1271                 if ( parentDirs != null )
1272                 {
1273                     writer.startElement( "mkdir" );
1274                     // Replace \ with / in the parent dir path
1275                     writer.addAttribute( "dir", "${maven.repo.local}/" + parentDirs.getPath().replace( '\\', '/' ) );
1276                     writer.endElement(); // mkdir
1277                 }
1278 
1279                 for ( Iterator j = project.getRepositories().iterator(); j.hasNext(); )
1280                 {
1281                     Repository repository = (Repository) j.next();
1282                     String url = repository.getUrl();
1283 
1284                     String localDir = getProjectRepoDirectory( url, basedir );
1285                     if ( localDir != null )
1286                     {
1287                         if ( localDir.length() > 0 && !localDir.endsWith( "/" ) )
1288                         {
1289                             localDir += '/';
1290                         }
1291 
1292                         writer.startElement( "copy" );
1293                         writer.addAttribute( "file", localDir + path );
1294                         AntBuildWriterUtil.addWrapAttribute( writer, "copy", "tofile", "${maven.repo.local}/" + path, 3 );
1295                         AntBuildWriterUtil.addWrapAttribute( writer, "copy", "failonerror", "false", 3 );
1296                         writer.endElement(); // copy
1297                     }
1298                     else
1299                     {
1300                         writer.startElement( "get" );
1301                         writer.addAttribute( "src", url + '/' + path );
1302                         AntBuildWriterUtil.addWrapAttribute( writer, "get", "dest", "${maven.repo.local}/" + path, 3 );
1303                         AntBuildWriterUtil.addWrapAttribute( writer, "get", "usetimestamp", "false", 3 );
1304                         AntBuildWriterUtil.addWrapAttribute( writer, "get", "ignoreerrors", "true", 3 );
1305                         writer.endElement(); // get
1306                     }
1307                 }
1308             }
1309         }
1310 
1311         writer.endElement(); // target
1312 
1313         XmlWriterUtil.writeLineBreak( writer );
1314     }
1315 
1316     /**
1317      * Gets the relative path to a repository that is rooted in the project. The returned path (if any) will always use
1318      * the forward slash ('/') as the directory separator. For example, the path "target/it-repo" will be returned for a
1319      * repository constructed from the URL "file://${basedir}/target/it-repo".
1320      * 
1321      * @param repoUrl The URL to the repository, must not be <code>null</code>.
1322      * @param projectDir The absolute path to the base directory of the project, must not be <code>null</code>
1323      * @return The path to the repository (relative to the project base directory) or <code>null</code> if the
1324      *         repository is not rooted in the project.
1325      */
1326     static String getProjectRepoDirectory( String repoUrl, String projectDir )
1327     {
1328         try
1329         {
1330             /*
1331              * NOTE: The usual way of constructing repo URLs rooted in the project is "file://${basedir}" or
1332              * "file:/${basedir}". None of these forms delivers a valid URL on both Unix and Windows (even ignoring URL
1333              * encoding), one platform will end up with the first directory of the path being interpreted as the host
1334              * name...
1335              */
1336             if ( repoUrl.regionMatches( true, 0, "file://", 0, 7 ) )
1337             {
1338                 String temp = repoUrl.substring( 7 );
1339                 if ( !temp.startsWith( "/" ) && !temp.regionMatches( true, 0, "localhost/", 0, 10 ) )
1340                 {
1341                     repoUrl = "file:///" + temp;
1342                 }
1343             }
1344             String path = FileUtils.toFile( new URL( repoUrl ) ).getPath();
1345             if ( path.startsWith( projectDir ) )
1346             {
1347                 path = path.substring( projectDir.length() ).replace( '\\', '/' );
1348                 if ( path.startsWith( "/" ) )
1349                 {
1350                     path = path.substring( 1 );
1351                 }
1352                 if ( path.endsWith( "/" ) )
1353                 {
1354                     path = path.substring( 0, path.length() - 1 );
1355                 }
1356                 return path;
1357             }
1358         }
1359         catch ( Exception e )
1360         {
1361             // not a "file:" URL or simply malformed
1362         }
1363         return null;
1364     }
1365 
1366     // ----------------------------------------------------------------------
1367     // Convenience methods
1368     // ----------------------------------------------------------------------
1369 
1370     /**
1371      * Put a property in properties defined by a name and a value
1372      *
1373      * @param properties not null
1374      * @param name
1375      * @param value not null
1376      */
1377     private static void addProperty( Properties properties, String name, String value )
1378     {
1379         properties.put( name, StringUtils.isNotEmpty( value ) ? value : "" );
1380     }
1381 
1382     /**
1383      * @param includes an array of includes or exludes map
1384      * @param key a key wanted in the map, like <code>include</code> or <code>exclude</code>
1385      * @return a String with comma-separated value of a key in each map
1386      */
1387     private static String getCommaSeparatedList( Map[] includes, String key )
1388     {
1389         if ( ( includes == null ) || ( includes.length == 0 ) )
1390         {
1391             return null;
1392         }
1393 
1394         StringBuffer sb = new StringBuffer();
1395         for ( int i = 0; i < includes.length; i++ )
1396         {
1397             String s = (String) includes[i].get( key );
1398             if ( StringUtils.isEmpty( s ) )
1399             {
1400                 continue;
1401             }
1402 
1403             sb.append( s );
1404 
1405             if ( i < ( includes.length - 1 ) )
1406             {
1407                 sb.append( "," );
1408             }
1409         }
1410 
1411         if ( sb.length() == 0 )
1412         {
1413             return null;
1414         }
1415 
1416         return sb.toString();
1417     }
1418 
1419     /**
1420      * Flattens the specified file selector options into a simple string list. For instance, the input
1421      *
1422      * <pre>
1423      * [ {include=&quot;*Test.java&quot;}, {include=&quot;*TestCase.java&quot;} ]
1424      * </pre>
1425      *
1426      * is converted to
1427      *
1428      * <pre>
1429      * [ &quot;*Test.java&quot;, &quot;*TestCase.java&quot; ]
1430      * </pre>
1431      *
1432      * @param options The file selector options to flatten, may be <code>null</code>.
1433      * @return The string list, might be empty but never <code>null</code>.
1434      */
1435     private static List getSelectorList( Map[] options )
1436     {
1437         List list = new ArrayList();
1438         if ( options != null && options.length > 0 )
1439         {
1440             for ( int i = 0; i < options.length; i++ )
1441             {
1442                 Map option = options[i];
1443                 list.addAll( option.values() );
1444             }
1445         }
1446         return list;
1447     }
1448 
1449 }