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