View Javadoc

1   package org.apache.maven.archetype.old;
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 org.apache.maven.archetype.ArchetypeGenerationRequest;
23  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor;
24  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptorBuilder;
25  import org.apache.maven.archetype.old.descriptor.TemplateDescriptor;
26  import org.apache.maven.artifact.repository.ArtifactRepository;
27  import org.apache.maven.archetype.common.ArchetypeArtifactManager;
28  import org.apache.maven.archetype.common.Constants;
29  import org.apache.maven.archetype.exception.UnknownArchetype;
30  import org.apache.maven.model.Build;
31  import org.apache.maven.model.Model;
32  import org.apache.maven.model.Parent;
33  import org.apache.maven.model.Resource;
34  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
35  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
36  import org.apache.velocity.VelocityContext;
37  import org.apache.velocity.context.Context;
38  import org.codehaus.plexus.logging.AbstractLogEnabled;
39  import org.codehaus.plexus.util.FileUtils;
40  import org.codehaus.plexus.util.IOUtil;
41  import org.codehaus.plexus.util.ReaderFactory;
42  import org.codehaus.plexus.util.StringUtils;
43  import org.codehaus.plexus.util.WriterFactory;
44  import org.codehaus.plexus.util.xml.XmlStreamReader;
45  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
46  import org.codehaus.plexus.velocity.VelocityComponent;
47  import org.dom4j.Document;
48  import org.dom4j.DocumentException;
49  import org.dom4j.Element;
50  import org.dom4j.Node;
51  import org.dom4j.io.SAXReader;
52  import org.dom4j.io.XMLWriter;
53  
54  import java.io.File;
55  import java.io.FileOutputStream;
56  import java.io.IOException;
57  import java.io.InputStream;
58  import java.io.OutputStream;
59  import java.io.OutputStreamWriter;
60  import java.io.Reader;
61  import java.io.StringWriter;
62  import java.io.Writer;
63  import java.net.URL;
64  import java.net.URLClassLoader;
65  import java.util.HashMap;
66  import java.util.Iterator;
67  import java.util.Map;
68  
69  /**
70   * @plexus.component
71   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
72   * @version $Id: DefaultOldArchetype.java 1162541 2011-08-28 15:48:08Z hboutemy $
73   */
74  public class DefaultOldArchetype
75      extends AbstractLogEnabled
76      implements OldArchetype
77  {
78      private static final String DEFAULT_TEST_RESOURCE_DIR = "/src/test/resources";
79  
80      private static final String DEFAULT_TEST_SOURCE_DIR = "/src/test/java";
81  
82      private static final String DEFAULT_RESOURCE_DIR = "/src/main/resources";
83  
84      private static final String DEFAULT_SOURCE_DIR = "/src/main/java";
85  
86      // ----------------------------------------------------------------------
87      // Components
88      // ----------------------------------------------------------------------
89  
90      /**
91       * @plexus.requirement
92       */
93      private VelocityComponent velocity;
94  
95      /** @plexus.requirement */
96      private ArchetypeArtifactManager archetypeArtifactManager;
97  
98      // ----------------------------------------------------------------------
99      // Implementation
100     // ----------------------------------------------------------------------
101 
102     // groupId = maven
103     // artifactId = maven-foo-archetype
104     // version = latest
105 
106     public void createArchetype( ArchetypeGenerationRequest request, ArtifactRepository archetypeRepository )
107         throws UnknownArchetype, ArchetypeNotFoundException, ArchetypeDescriptorException,
108         ArchetypeTemplateProcessingException
109     {
110         // ----------------------------------------------------------------------
111         // Download the archetype
112         // ----------------------------------------------------------------------
113 
114         File archetypeFile = archetypeArtifactManager.getArchetypeFile(
115                 request.getArchetypeGroupId(), request.getArchetypeArtifactId(), request.getArchetypeVersion(),
116                 archetypeRepository, request.getLocalRepository(), request.getRemoteArtifactRepositories() );
117 
118         createArchetype( request, archetypeFile );
119     }
120 
121     public void createArchetype( ArchetypeGenerationRequest request, File archetypeFile )
122         throws ArchetypeDescriptorException, ArchetypeTemplateProcessingException
123     {
124         Map<String, String> parameters = new HashMap<String, String>();
125 
126         parameters.put( "basedir", request.getOutputDirectory() );
127 
128         parameters.put( Constants.PACKAGE, request.getPackage() );
129 
130         parameters.put( "packageName", request.getPackage() );
131 
132         parameters.put( Constants.GROUP_ID, request.getGroupId() );
133 
134         parameters.put( Constants.ARTIFACT_ID, request.getArtifactId() );
135 
136         parameters.put( Constants.VERSION, request.getVersion() );
137 
138         // ---------------------------------------------------------------------
139         // Get Logger and display all parameters used
140         // ---------------------------------------------------------------------
141         if ( getLogger().isInfoEnabled() )
142         {
143             getLogger().info( "----------------------------------------------------------------------------" );
144 
145             getLogger().info( "Using following parameters for creating project from Old (1.x) Archetype: "
146                                   + request.getArchetypeArtifactId() + ":" + request.getArchetypeVersion() );
147 
148             getLogger().info( "----------------------------------------------------------------------------" );
149 
150             for ( Map.Entry<String, String> entry : parameters.entrySet() )
151             {
152                 String parameterName = entry.getKey();
153 
154                 String parameterValue = entry.getValue();
155 
156                 getLogger().info( "Parameter: " + parameterName + ", Value: " + parameterValue );
157             }
158         }
159 
160         // ----------------------------------------------------------------------
161         // Load the descriptor
162         // ----------------------------------------------------------------------
163 
164         ArchetypeDescriptorBuilder builder = new ArchetypeDescriptorBuilder();
165 
166         ArchetypeDescriptor descriptor;
167 
168         URLClassLoader archetypeJarLoader;
169 
170         InputStream is = null;
171 
172         try
173         {
174             URL[] urls = new URL[1];
175 
176             urls[0] = archetypeFile.toURL();
177 
178             archetypeJarLoader = new URLClassLoader( urls );
179 
180             is = getStream( ARCHETYPE_DESCRIPTOR, archetypeJarLoader );
181 
182             if ( is == null )
183             {
184                 is = getStream( ARCHETYPE_OLD_DESCRIPTOR, archetypeJarLoader );
185             }
186 
187             if ( is == null )
188             {
189                 throw new ArchetypeDescriptorException( "The " + ARCHETYPE_DESCRIPTOR + " descriptor cannot be found." );
190             }
191 
192             descriptor = builder.build( new XmlStreamReader( is ) );
193         }
194         catch ( IOException e )
195         {
196             throw new ArchetypeDescriptorException( "Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e );
197         }
198         catch ( XmlPullParserException e )
199         {
200             throw new ArchetypeDescriptorException( "Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e );
201         }
202         finally
203         {
204             IOUtil.close( is );
205         }
206 
207         // ----------------------------------------------------------------------
208         //
209         // ----------------------------------------------------------------------
210 
211         String artifactId = request.getArtifactId();
212 
213         File parentPomFile = new File( request.getOutputDirectory(), ARCHETYPE_POM );
214 
215         File outputDirectoryFile;
216 
217         boolean creating;
218         File pomFile;
219         if ( parentPomFile.exists() && descriptor.isAllowPartial() && artifactId == null )
220         {
221             outputDirectoryFile = new File( request.getOutputDirectory() );
222             creating = false;
223             pomFile = parentPomFile;
224         }
225         else
226         {
227             if ( artifactId == null )
228             {
229                 throw new ArchetypeTemplateProcessingException(
230                     "Artifact ID must be specified when creating a new project from an archetype." );
231             }
232 
233             outputDirectoryFile = new File( request.getOutputDirectory(), artifactId );
234             creating = true;
235 
236             if ( outputDirectoryFile.exists() )
237             {
238                 if ( descriptor.isAllowPartial() )
239                 {
240                     creating = false;
241                 }
242                 else
243                 {
244                     throw new ArchetypeTemplateProcessingException( "Directory "
245                         + outputDirectoryFile.getName() + " already exists - please run from a clean directory" );
246                 }
247             }
248 
249             pomFile = new File( outputDirectoryFile, ARCHETYPE_POM );
250         }
251 
252         if ( creating )
253         {
254             if ( request.getGroupId() == null )
255             {
256                 throw new ArchetypeTemplateProcessingException(
257                     "Group ID must be specified when creating a new project from an archetype." );
258             }
259 
260             if ( request.getVersion() == null )
261             {
262                 throw new ArchetypeTemplateProcessingException(
263                     "Version must be specified when creating a new project from an archetype." );
264             }
265         }
266 
267         String outputDirectory = outputDirectoryFile.getAbsolutePath();
268 
269         String packageName = request.getPackage();
270 
271         // ----------------------------------------------------------------------
272         // Set up the Velocity context
273         // ----------------------------------------------------------------------
274 
275         Context context = new VelocityContext();
276 
277         context.put( Constants.PACKAGE, packageName );
278 
279         for ( Map.Entry<String, String> entry : parameters.entrySet() )
280         {
281             context.put( entry.getKey(), entry.getValue() );
282         }
283 
284         // ----------------------------------------------------------------------
285         // Process the templates
286         // ----------------------------------------------------------------------
287 
288         ClassLoader old = Thread.currentThread().getContextClassLoader();
289 
290         Thread.currentThread().setContextClassLoader( archetypeJarLoader );
291 
292         Model parentModel = null;
293         if ( creating )
294         {
295             if ( parentPomFile.exists() )
296             {
297                 Reader fileReader = null;
298 
299                 try
300                 {
301                     fileReader = ReaderFactory.newXmlReader( parentPomFile );
302                     MavenXpp3Reader reader = new MavenXpp3Reader();
303                     parentModel = reader.read( fileReader );
304                     if ( !"pom".equals( parentModel.getPackaging() ) )
305                     {
306                         throw new ArchetypeTemplateProcessingException(
307                             "Unable to add module to the current project as it is not of packaging type 'pom'" );
308                     }
309                 }
310                 catch ( IOException e )
311                 {
312                     throw new ArchetypeTemplateProcessingException( "Unable to read parent POM", e );
313                 }
314                 catch ( XmlPullParserException e )
315                 {
316                     throw new ArchetypeTemplateProcessingException( "Unable to read parent POM", e );
317                 }
318                 finally
319                 {
320                     IOUtil.close( fileReader );
321                 }
322 
323                 parentModel.getModules().add( artifactId );
324             }
325         }
326 
327         try
328         {
329             processTemplates( pomFile, outputDirectory, context, descriptor, packageName, parentModel );
330         }
331         finally
332         {
333             Thread.currentThread().setContextClassLoader( old );
334         }
335 
336         if ( parentModel != null )
337         {
338 /*
339         // TODO: would be nice to just write out with the xpp3 writer again, except that it loses a bunch of info and
340         // reformats, so the module is just baked in as a string instead.
341             FileWriter fileWriter = null;
342 
343             try
344             {
345                 fileWriter = new FileWriter( parentPomFile );
346 
347                 MavenXpp3Writer writer = new MavenXpp3Writer();
348                 writer.write( fileWriter, parentModel );
349             }
350             catch ( IOException e )
351             {
352                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
353             }
354             finally
355             {
356                 IOUtil.close( fileWriter );
357             }
358 */
359             Reader fileReader = null;
360             boolean added;
361             StringWriter w = new StringWriter();
362             try
363             {
364                 fileReader = ReaderFactory.newXmlReader( parentPomFile );
365                 added = addModuleToParentPom( artifactId, fileReader, w );
366             }
367             catch ( IOException e )
368             {
369                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
370             }
371             catch ( DocumentException e )
372             {
373                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
374             }
375             finally
376             {
377                 IOUtil.close( fileReader );
378             }
379 
380             if ( added )
381             {
382                 Writer out = null;
383                 try
384                 {
385                     out = WriterFactory.newXmlWriter( parentPomFile );
386                     IOUtil.copy( w.toString(), out );
387                 }
388                 catch ( IOException e )
389                 {
390                     throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
391                 }
392                 finally
393                 {
394                     IOUtil.close( out );
395                 }
396             }
397         }
398 
399         // ----------------------------------------------------------------------
400         // Log message on OldArchetype creation
401         // ----------------------------------------------------------------------
402         if ( getLogger().isInfoEnabled() )
403         {
404             getLogger().info( "project created from Old (1.x) Archetype in dir: " + outputDirectory );
405         }
406 
407     }
408 
409     static boolean addModuleToParentPom( String artifactId, Reader fileReader, Writer fileWriter )
410         throws DocumentException, IOException, ArchetypeTemplateProcessingException
411     {
412         SAXReader reader = new SAXReader();
413         Document document = reader.read( fileReader );
414         Element project = document.getRootElement();
415 
416         String packaging = null;
417         Element packagingElement = project.element( "packaging" );
418         if ( packagingElement != null )
419         {
420             packaging = packagingElement.getStringValue();
421         }
422         if ( !"pom".equals( packaging ) )
423         {
424             throw new ArchetypeTemplateProcessingException(
425                 "Unable to add module to the current project as it is not of packaging type 'pom'" );
426         }
427 
428         Element modules = project.element( "modules" );
429         if ( modules == null )
430         {
431             modules = project.addText( "  " ).addElement( "modules" );
432             modules.setText( "\n  " );
433             project.addText( "\n" );
434         }
435         boolean found = false;
436         for ( Iterator<?> i = modules.elementIterator( "module" ); i.hasNext() && !found; )
437         {
438             Element module = (Element) i.next();
439             if ( module.getText().equals( artifactId ) )
440             {
441                 found = true;
442             }
443         }
444         if ( !found )
445         {
446             Node lastTextNode = null;
447             for ( Iterator<?> i = modules.nodeIterator(); i.hasNext(); )
448             {
449                 Node node = (Node) i.next();
450                 if ( node.getNodeType() == Node.ELEMENT_NODE )
451                 {
452                     lastTextNode = null;
453                 }
454                 else if ( node.getNodeType() == Node.TEXT_NODE )
455                 {
456                     lastTextNode = node;
457                 }
458             }
459 
460             if ( lastTextNode != null )
461             {
462                 modules.remove( lastTextNode );
463             }
464 
465             modules.addText( "\n    " );
466             modules.addElement( "module" ).setText( artifactId );
467             modules.addText( "\n  " );
468 
469             XMLWriter writer = new XMLWriter( fileWriter );
470             writer.write( document );
471         }
472         return !found;
473     }
474 
475     private void processTemplates( File pomFile, String outputDirectory, Context context,
476                                    ArchetypeDescriptor descriptor, String packageName, Model parentModel )
477         throws ArchetypeTemplateProcessingException
478     {
479         if ( !pomFile.exists() )
480         {
481             processTemplate( outputDirectory, context, ARCHETYPE_POM, new TemplateDescriptor(), false, null );
482         }
483 
484         // ---------------------------------------------------------------------
485         // Model generated for the new archetype, so process it now
486         // ---------------------------------------------------------------------
487 
488         Model generatedModel;
489         Reader pomReader = null;
490         try
491         {
492             pomReader = ReaderFactory.newXmlReader( pomFile );
493 
494             MavenXpp3Reader reader = new MavenXpp3Reader();
495 
496             generatedModel = reader.read( pomReader );
497         }
498         catch ( IOException e )
499         {
500             throw new ArchetypeTemplateProcessingException( "Error reading POM", e );
501         }
502         catch ( XmlPullParserException e )
503         {
504             throw new ArchetypeTemplateProcessingException( "Error reading POM", e );
505         }
506         finally
507         {
508             IOUtil.close( pomReader );
509         }
510 
511         if ( parentModel != null )
512         {
513             Parent parent = new Parent();
514             parent.setGroupId( parentModel.getGroupId() );
515             if ( parent.getGroupId() == null )
516             {
517                 parent.setGroupId( parentModel.getParent().getGroupId() );
518             }
519             parent.setArtifactId( parentModel.getArtifactId() );
520             parent.setVersion( parentModel.getVersion() );
521             if ( parent.getVersion() == null )
522             {
523                 parent.setVersion( parentModel.getParent().getVersion() );
524             }
525             generatedModel.setParent( parent );
526 
527             Writer pomWriter = null;
528             try
529             {
530                 pomWriter = WriterFactory.newXmlWriter( pomFile );
531 
532                 MavenXpp3Writer writer = new MavenXpp3Writer();
533                 writer.write( pomWriter, generatedModel );
534             }
535             catch ( IOException e )
536             {
537                 throw new ArchetypeTemplateProcessingException( "Error rewriting POM", e );
538             }
539             finally
540             {
541                 IOUtil.close( pomWriter );
542             }
543         }
544 
545         // XXX: Following POM processing block may be a candidate for
546         // refactoring out into service methods or moving to
547         // createProjectDirectoryStructure(outputDirectory)
548         Build build = generatedModel.getBuild();
549 
550         boolean overrideSrcDir = false;
551 
552         boolean overrideResourceDir = false;
553 
554         boolean overrideTestSrcDir = false;
555 
556         boolean overrideTestResourceDir = false;
557 
558         boolean foundBuildElement = build != null;
559 
560         if ( getLogger().isDebugEnabled() )
561         {
562             getLogger().debug(
563                 "********************* Debug info for resources created from generated Model ***********************" );
564             getLogger().debug( "Was build element found in generated POM?: " + foundBuildElement );
565         }
566 
567         // create source directory if specified in POM
568         if ( foundBuildElement && null != build.getSourceDirectory() )
569         {
570             getLogger().debug( "Overriding default source directory " );
571 
572             overrideSrcDir = true;
573 
574             String srcDirectory = build.getSourceDirectory();
575 
576             srcDirectory = StringUtils.replace( srcDirectory, "\\", "/" );
577 
578             FileUtils.mkdir( getOutputDirectory( outputDirectory, srcDirectory ) );
579         }
580 
581         // create script source directory if specified in POM
582         if ( foundBuildElement && null != build.getScriptSourceDirectory() )
583         {
584             getLogger().debug( "Overriding default script source directory " );
585 
586             String scriptSourceDirectory = build.getScriptSourceDirectory();
587 
588             scriptSourceDirectory = StringUtils.replace( scriptSourceDirectory, "\\", "/" );
589 
590             FileUtils.mkdir( getOutputDirectory( outputDirectory, scriptSourceDirectory ) );
591         }
592 
593         // create resource director(y/ies) if specified in POM
594         if ( foundBuildElement && build.getResources().size() > 0 )
595         {
596             getLogger().debug( "Overriding default resource directory " );
597 
598             overrideResourceDir = true;
599 
600             Iterator<?> resourceItr = build.getResources().iterator();
601 
602             while ( resourceItr.hasNext() )
603             {
604                 Resource resource = (Resource) resourceItr.next();
605 
606                 String resourceDirectory = resource.getDirectory();
607 
608                 resourceDirectory = StringUtils.replace( resourceDirectory, "\\", "/" );
609 
610                 FileUtils.mkdir( getOutputDirectory( outputDirectory, resourceDirectory ) );
611             }
612         }
613         // create test source directory if specified in POM
614         if ( foundBuildElement && null != build.getTestSourceDirectory() )
615         {
616             getLogger().debug( "Overriding default test directory " );
617 
618             overrideTestSrcDir = true;
619 
620             String testDirectory = build.getTestSourceDirectory();
621 
622             testDirectory = StringUtils.replace( testDirectory, "\\", "/" );
623 
624             FileUtils.mkdir( getOutputDirectory( outputDirectory, testDirectory ) );
625         }
626 
627         // create test resource directory if specified in POM
628         if ( foundBuildElement && build.getTestResources().size() > 0 )
629         {
630             getLogger().debug( "Overriding default test resource directory " );
631 
632             overrideTestResourceDir = true;
633 
634             Iterator<?> testResourceItr = build.getTestResources().iterator();
635 
636             while ( testResourceItr.hasNext() )
637             {
638                 Resource resource = (Resource) testResourceItr.next();
639 
640                 String testResourceDirectory = resource.getDirectory();
641 
642                 testResourceDirectory = StringUtils.replace( testResourceDirectory, "\\", "/" );
643 
644                 FileUtils.mkdir( getOutputDirectory( outputDirectory, testResourceDirectory ) );
645             }
646         }
647 
648         getLogger().debug(
649             "********************* End of debug info from resources from generated POM ***********************" );
650 
651         // ----------------------------------------------------------------------
652         // Main
653         // ----------------------------------------------------------------------
654 
655         if ( descriptor.getSources().size() > 0 )
656         {
657             if ( !overrideSrcDir )
658             {
659                 FileUtils.mkdir( outputDirectory + DEFAULT_SOURCE_DIR );
660                 processSources( outputDirectory, context, descriptor, packageName, DEFAULT_SOURCE_DIR );
661             }
662             else
663             {
664                 processSources( outputDirectory, context, descriptor, packageName, build.getSourceDirectory() );
665             }
666         }
667 
668         if ( descriptor.getResources().size() > 0 )
669         {
670             if ( !overrideResourceDir )
671             {
672                 FileUtils.mkdir( outputDirectory + DEFAULT_RESOURCE_DIR );
673             }
674             processResources( outputDirectory, context, descriptor, packageName );
675         }
676 
677         // ----------------------------------------------------------------------
678         // Test
679         // ----------------------------------------------------------------------
680 
681         if ( descriptor.getTestSources().size() > 0 )
682         {
683             if ( !overrideTestSrcDir )
684             {
685                 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_SOURCE_DIR );
686                 processTestSources( outputDirectory, context, descriptor, packageName, DEFAULT_TEST_SOURCE_DIR );
687             }
688             else
689             {
690                 processTestSources( outputDirectory, context, descriptor, packageName, build.getTestSourceDirectory() );
691             }
692         }
693 
694         if ( descriptor.getTestResources().size() > 0 )
695         {
696             if ( !overrideTestResourceDir )
697             {
698                 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_RESOURCE_DIR );
699             }
700             processTestResources( outputDirectory, context, descriptor, packageName );
701         }
702 
703         // ----------------------------------------------------------------------
704         // Site
705         // ----------------------------------------------------------------------
706 
707         if ( descriptor.getSiteResources().size() > 0 )
708         {
709             processSiteResources( outputDirectory, context, descriptor, packageName );
710         }
711     }
712 
713     private void processTemplate( String outputDirectory, Context context, String template,
714                                   TemplateDescriptor descriptor, boolean packageInFileName, String packageName )
715         throws ArchetypeTemplateProcessingException
716     {
717         processTemplate( outputDirectory, context, template, descriptor, packageInFileName, packageName, null );
718     }
719 
720     private String getOutputDirectory( String outputDirectory, String testResourceDirectory )
721     {
722         return outputDirectory
723             + ( testResourceDirectory.startsWith( "/" ) ? testResourceDirectory : "/" + testResourceDirectory );
724     }
725 
726     // ----------------------------------------------------------------------
727     //
728     // ----------------------------------------------------------------------
729 
730     protected void processSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
731                                    String packageName, String sourceDirectory )
732         throws ArchetypeTemplateProcessingException
733     {
734         for ( String template : descriptor.getSources() )
735         {
736             processTemplate( outputDirectory, context, template, descriptor.getSourceDescriptor( template ), true,
737                              packageName, sourceDirectory );
738         }
739     }
740 
741     protected void processTestSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
742                                        String packageName, String testSourceDirectory )
743         throws ArchetypeTemplateProcessingException
744     {
745         for ( String template : descriptor.getTestSources() )
746         {
747             processTemplate( outputDirectory, context, template, descriptor.getTestSourceDescriptor( template ), true,
748                              packageName, testSourceDirectory );
749         }
750     }
751 
752     protected void processResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
753                                      String packageName )
754         throws ArchetypeTemplateProcessingException
755     {
756         for ( String template : descriptor.getResources() )
757         {
758             processTemplate( outputDirectory, context, template, descriptor.getResourceDescriptor( template ), false,
759                              packageName );
760         }
761     }
762 
763     protected void processTestResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
764                                          String packageName )
765         throws ArchetypeTemplateProcessingException
766     {
767         for ( String template : descriptor.getTestResources() )
768         {
769             processTemplate( outputDirectory, context, template, descriptor.getTestResourceDescriptor( template ),
770                              false, packageName );
771         }
772     }
773 
774     protected void processSiteResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
775                                          String packageName )
776         throws ArchetypeTemplateProcessingException
777     {
778         for ( String template : descriptor.getSiteResources() )
779         {
780             processTemplate( outputDirectory, context, template, descriptor.getSiteResourceDescriptor( template ),
781                              false, packageName );
782         }
783     }
784 
785     protected void processTemplate( String outputDirectory, Context context, String template,
786                                     TemplateDescriptor descriptor, boolean packageInFileName, String packageName,
787                                     String sourceDirectory )
788         throws ArchetypeTemplateProcessingException
789     {
790         File f;
791 
792         template = StringUtils.replace( template, "\\", "/" );
793 
794         if ( packageInFileName && packageName != null )
795         {
796             String templateFileName = StringUtils.replace( template, "/", File.separator );
797 
798             String path = packageName.replace( '.', '/' );
799 
800             String filename = FileUtils.filename( templateFileName );
801 
802             String dirname = FileUtils.dirname( templateFileName ).replace( '\\', '/' );
803 
804             sourceDirectory = sourceDirectory.replace( '\\', '/' );
805             if ( sourceDirectory.startsWith( "/" ) )
806             {
807                 sourceDirectory = sourceDirectory.substring( 1 );
808             }
809 
810             if ( !dirname.startsWith( sourceDirectory ) )
811             {
812                 throw new ArchetypeTemplateProcessingException(
813                     "Template '" + template + "' not in directory '" + sourceDirectory + "'" );
814             }
815 
816             String extraPackages = dirname.substring( sourceDirectory.length() );
817             if ( extraPackages.startsWith( "/" ) )
818             {
819                 extraPackages = extraPackages.substring( 1 );
820             }
821             if ( extraPackages.length() > 0 )
822             {
823                 path += "/" + extraPackages;
824             }
825 
826             f = new File( new File( new File( outputDirectory, sourceDirectory ), path ), filename );
827         }
828         else
829         {
830             f = new File( outputDirectory, template );
831         }
832 
833         if ( !f.getParentFile().exists() )
834         {
835             f.getParentFile().mkdirs();
836         }
837 
838         if ( descriptor.isFiltered() )
839         {
840             Writer writer = null;
841             try
842             {
843                 StringWriter stringWriter = new StringWriter();
844 
845                 template = ARCHETYPE_RESOURCES + "/" + template;
846 
847                 velocity.getEngine().mergeTemplate( template, descriptor.getEncoding(), context, stringWriter );
848 
849                 writer = new OutputStreamWriter( new FileOutputStream( f ), descriptor.getEncoding() );
850 
851                 writer.write( StringUtils.unifyLineSeparators( stringWriter.toString() ) );
852 
853                 writer.flush();
854             }
855             catch ( Exception e )
856             {
857                 throw new ArchetypeTemplateProcessingException( "Error merging velocity templates", e );
858             }
859             finally
860             {
861                 IOUtil.close( writer );
862             }
863         }
864         else
865         {
866             InputStream is = getStream( ARCHETYPE_RESOURCES + "/" + template, null );
867 
868             OutputStream fos = null;
869 
870             try
871             {
872                 fos = new FileOutputStream( f );
873 
874                 IOUtil.copy( is, fos );
875             }
876             catch ( Exception e )
877             {
878                 throw new ArchetypeTemplateProcessingException( "Error copying file", e );
879             }
880             finally
881             {
882                 IOUtil.close( fos );
883 
884                 IOUtil.close( is );
885             }
886         }
887     }
888 
889     protected void createProjectDirectoryStructure( String outputDirectory )
890     {
891     }
892 
893     private InputStream getStream( String name, ClassLoader loader )
894     {
895         if ( loader == null )
896         {
897             return Thread.currentThread().getContextClassLoader().getResourceAsStream( name );
898         }
899         return loader.getResourceAsStream( name );
900     }
901 }