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