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