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.toURI().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         catch ( IOException e )
309         {
310             throw new ArchetypeTemplateProcessingException( "Unable to process template", e );
311         }
312         finally
313         {
314             Thread.currentThread().setContextClassLoader( old );
315         }
316 
317         if ( parentModel != null )
318         {
319 /*
320         // TODO: would be nice to just write out with the xpp3 writer again, except that it loses a bunch of info and
321         // reformats, so the module is just baked in as a string instead.
322             FileWriter fileWriter = null;
323 
324             try
325             {
326                 fileWriter = new FileWriter( parentPomFile );
327 
328                 MavenXpp3Writer writer = new MavenXpp3Writer();
329                 writer.write( fileWriter, parentModel );
330             }
331             catch ( IOException e )
332             {
333                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
334             }
335             finally
336             {
337                 IOUtil.close( fileWriter );
338             }
339 */
340             
341             boolean added;
342             StringWriter w = new StringWriter();
343             try ( Reader fileReader = ReaderFactory.newXmlReader( parentPomFile ) )
344             {
345                 added = addModuleToParentPom( artifactId, fileReader, w );
346             }
347             catch ( IOException | SAXException | ParserConfigurationException | TransformerException  e )
348             {
349                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
350             }
351 
352             if ( added )
353             {
354                 try ( Writer out = WriterFactory.newXmlWriter( parentPomFile ) )
355                 {
356                     IOUtil.copy( w.toString(), out );
357                 }
358                 catch ( IOException e )
359                 {
360                     throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
361                 }
362             }
363         }
364 
365         // ----------------------------------------------------------------------
366         // Log message on OldArchetype creation
367         // ----------------------------------------------------------------------
368         if ( getLogger().isInfoEnabled() )
369         {
370             getLogger().info( "project created from Old (1.x) Archetype in dir: " + outputDirectory );
371         }
372 
373     }
374 
375     private InputStream getDescriptorInputStream( ClassLoader archetypeJarLoader ) throws ArchetypeDescriptorException
376     {
377         InputStream is = getStream( ARCHETYPE_DESCRIPTOR, archetypeJarLoader );
378 
379         if ( is == null )
380         {
381             is = getStream( ARCHETYPE_OLD_DESCRIPTOR, archetypeJarLoader );
382         }
383 
384         if ( is == null )
385         {
386             throw new ArchetypeDescriptorException( "The " + ARCHETYPE_DESCRIPTOR
387                                                     + " descriptor cannot be found." );
388         }
389         
390         return is;
391     }
392 
393     static boolean addModuleToParentPom( String artifactId, Reader fileReader, Writer fileWriter )
394             throws ArchetypeTemplateProcessingException, InvalidPackaging, IOException, ParserConfigurationException,
395             SAXException, TransformerException
396     {
397         return PomUtils.addNewModule( artifactId, fileReader, fileWriter );
398     }
399 
400     private void processTemplates( File pomFile, String outputDirectory, Context context,
401                                    ArchetypeDescriptor descriptor, String packageName, Model parentModel )
402             throws ArchetypeTemplateProcessingException, IOException
403     {
404         if ( !pomFile.exists() )
405         {
406             processTemplate( outputDirectory, context, ARCHETYPE_POM, new TemplateDescriptor(), false, null );
407         }
408 
409         // ---------------------------------------------------------------------
410         // Model generated for the new archetype, so process it now
411         // ---------------------------------------------------------------------
412 
413         Model generatedModel;
414         
415         try ( Reader pomReader = ReaderFactory.newXmlReader( pomFile ) )
416         {
417             MavenXpp3Reader reader = new MavenXpp3Reader();
418 
419             generatedModel = reader.read( pomReader );
420         }
421         catch ( IOException | XmlPullParserException e )
422         {
423             throw new ArchetypeTemplateProcessingException( "Error reading POM", e );
424         }
425 
426         if ( parentModel != null )
427         {
428             Parent parent = new Parent();
429             parent.setGroupId( parentModel.getGroupId() );
430             if ( parent.getGroupId() == null )
431             {
432                 parent.setGroupId( parentModel.getParent().getGroupId() );
433             }
434             parent.setArtifactId( parentModel.getArtifactId() );
435             parent.setVersion( parentModel.getVersion() );
436             if ( parent.getVersion() == null )
437             {
438                 parent.setVersion( parentModel.getParent().getVersion() );
439             }
440             generatedModel.setParent( parent );
441 
442             try (  Writer pomWriter = WriterFactory.newXmlWriter( pomFile ) )
443             {
444                 MavenXpp3Writer writer = new MavenXpp3Writer();
445                 writer.write( pomWriter, generatedModel );
446             }
447             catch ( IOException e )
448             {
449                 throw new ArchetypeTemplateProcessingException( "Error rewriting POM", e );
450             }
451         }
452 
453         // XXX: Following POM processing block may be a candidate for
454         // refactoring out into service methods or moving to
455         // createProjectDirectoryStructure(outputDirectory)
456         Build build = generatedModel.getBuild();
457 
458         boolean overrideSrcDir = false;
459 
460         boolean overrideResourceDir = false;
461 
462         boolean overrideTestSrcDir = false;
463 
464         boolean overrideTestResourceDir = false;
465 
466         boolean foundBuildElement = build != null;
467 
468         if ( getLogger().isDebugEnabled() )
469         {
470             getLogger().debug(
471                 "********************* Debug info for resources created from generated Model ***********************" );
472             getLogger().debug( "Was build element found in generated POM?: " + foundBuildElement );
473         }
474 
475         // create source directory if specified in POM
476         if ( foundBuildElement && null != build.getSourceDirectory() )
477         {
478             getLogger().debug( "Overriding default source directory " );
479 
480             overrideSrcDir = true;
481 
482             String srcDirectory = build.getSourceDirectory();
483 
484             srcDirectory = StringUtils.replace( srcDirectory, "\\", "/" );
485 
486             FileUtils.mkdir( getOutputDirectory( outputDirectory, srcDirectory ) );
487         }
488 
489         // create script source directory if specified in POM
490         if ( foundBuildElement && null != build.getScriptSourceDirectory() )
491         {
492             getLogger().debug( "Overriding default script source directory " );
493 
494             String scriptSourceDirectory = build.getScriptSourceDirectory();
495 
496             scriptSourceDirectory = StringUtils.replace( scriptSourceDirectory, "\\", "/" );
497 
498             FileUtils.mkdir( getOutputDirectory( outputDirectory, scriptSourceDirectory ) );
499         }
500 
501         // create resource director(y/ies) if specified in POM
502         if ( foundBuildElement && build.getResources().size() > 0 )
503         {
504             getLogger().debug( "Overriding default resource directory " );
505 
506             overrideResourceDir = true;
507 
508             Iterator<?> resourceItr = build.getResources().iterator();
509 
510             while ( resourceItr.hasNext() )
511             {
512                 Resource resource = (Resource) resourceItr.next();
513 
514                 String resourceDirectory = resource.getDirectory();
515 
516                 resourceDirectory = StringUtils.replace( resourceDirectory, "\\", "/" );
517 
518                 FileUtils.mkdir( getOutputDirectory( outputDirectory, resourceDirectory ) );
519             }
520         }
521         // create test source directory if specified in POM
522         if ( foundBuildElement && null != build.getTestSourceDirectory() )
523         {
524             getLogger().debug( "Overriding default test directory " );
525 
526             overrideTestSrcDir = true;
527 
528             String testDirectory = build.getTestSourceDirectory();
529 
530             testDirectory = StringUtils.replace( testDirectory, "\\", "/" );
531 
532             FileUtils.mkdir( getOutputDirectory( outputDirectory, testDirectory ) );
533         }
534 
535         // create test resource directory if specified in POM
536         if ( foundBuildElement && build.getTestResources().size() > 0 )
537         {
538             getLogger().debug( "Overriding default test resource directory " );
539 
540             overrideTestResourceDir = true;
541 
542             Iterator<?> testResourceItr = build.getTestResources().iterator();
543 
544             while ( testResourceItr.hasNext() )
545             {
546                 Resource resource = (Resource) testResourceItr.next();
547 
548                 String testResourceDirectory = resource.getDirectory();
549 
550                 testResourceDirectory = StringUtils.replace( testResourceDirectory, "\\", "/" );
551 
552                 FileUtils.mkdir( getOutputDirectory( outputDirectory, testResourceDirectory ) );
553             }
554         }
555 
556         getLogger().debug(
557             "********************* End of debug info from resources from generated POM ***********************" );
558 
559         // ----------------------------------------------------------------------
560         // Main
561         // ----------------------------------------------------------------------
562 
563         if ( descriptor.getSources().size() > 0 )
564         {
565             if ( !overrideSrcDir )
566             {
567                 FileUtils.mkdir( outputDirectory + DEFAULT_SOURCE_DIR );
568                 processSources( outputDirectory, context, descriptor, packageName, DEFAULT_SOURCE_DIR );
569             }
570             else
571             {
572                 processSources( outputDirectory, context, descriptor, packageName, build.getSourceDirectory() );
573             }
574         }
575 
576         if ( descriptor.getResources().size() > 0 )
577         {
578             if ( !overrideResourceDir )
579             {
580                 FileUtils.mkdir( outputDirectory + DEFAULT_RESOURCE_DIR );
581             }
582             processResources( outputDirectory, context, descriptor, packageName );
583         }
584 
585         // ----------------------------------------------------------------------
586         // Test
587         // ----------------------------------------------------------------------
588 
589         if ( descriptor.getTestSources().size() > 0 )
590         {
591             if ( !overrideTestSrcDir )
592             {
593                 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_SOURCE_DIR );
594                 processTestSources( outputDirectory, context, descriptor, packageName, DEFAULT_TEST_SOURCE_DIR );
595             }
596             else
597             {
598                 processTestSources( outputDirectory, context, descriptor, packageName, build.getTestSourceDirectory() );
599             }
600         }
601 
602         if ( descriptor.getTestResources().size() > 0 )
603         {
604             if ( !overrideTestResourceDir )
605             {
606                 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_RESOURCE_DIR );
607             }
608             processTestResources( outputDirectory, context, descriptor, packageName );
609         }
610 
611         // ----------------------------------------------------------------------
612         // Site
613         // ----------------------------------------------------------------------
614 
615         if ( descriptor.getSiteResources().size() > 0 )
616         {
617             processSiteResources( outputDirectory, context, descriptor, packageName );
618         }
619     }
620 
621     private void processTemplate( String outputDirectory, Context context, String template,
622                                   TemplateDescriptor descriptor, boolean packageInFileName, String packageName )
623             throws ArchetypeTemplateProcessingException, IOException
624     {
625         processTemplate( outputDirectory, context, template, descriptor, packageInFileName, packageName, null );
626     }
627 
628     private String getOutputDirectory( String outputDirectory, String testResourceDirectory )
629     {
630         return outputDirectory
631             + ( testResourceDirectory.startsWith( "/" ) ? testResourceDirectory : "/" + testResourceDirectory );
632     }
633 
634     // ----------------------------------------------------------------------
635     //
636     // ----------------------------------------------------------------------
637 
638     protected void processSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
639                                    String packageName, String sourceDirectory )
640             throws ArchetypeTemplateProcessingException, IOException
641     {
642         for ( String template : descriptor.getSources() )
643         {
644             processTemplate( outputDirectory, context, template, descriptor.getSourceDescriptor( template ), true,
645                              packageName, sourceDirectory );
646         }
647     }
648 
649     protected void processTestSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
650                                        String packageName, String testSourceDirectory )
651             throws ArchetypeTemplateProcessingException, IOException
652     {
653         for ( String template : descriptor.getTestSources() )
654         {
655             processTemplate( outputDirectory, context, template, descriptor.getTestSourceDescriptor( template ), true,
656                              packageName, testSourceDirectory );
657         }
658     }
659 
660     protected void processResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
661                                      String packageName )
662         throws IOException, ArchetypeTemplateProcessingException
663     {
664         for ( String template : descriptor.getResources() )
665         {
666             processTemplate( outputDirectory, context, template, descriptor.getResourceDescriptor( template ), false,
667                              packageName );
668         }
669     }
670 
671     protected void processTestResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
672                                          String packageName )
673         throws IOException, ArchetypeTemplateProcessingException
674     {
675         for ( String template : descriptor.getTestResources() )
676         {
677             processTemplate( outputDirectory, context, template, descriptor.getTestResourceDescriptor( template ),
678                              false, packageName );
679         }
680     }
681 
682     protected void processSiteResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
683                                          String packageName )
684         throws IOException, ArchetypeTemplateProcessingException
685     {
686         for ( String template : descriptor.getSiteResources() )
687         {
688             processTemplate( outputDirectory, context, template, descriptor.getSiteResourceDescriptor( template ),
689                              false, packageName );
690         }
691     }
692 
693     protected void processTemplate( String outputDirectory, Context context, String template,
694                                     TemplateDescriptor descriptor, boolean packageInFileName, String packageName,
695                                     String sourceDirectory )
696         throws IOException, ArchetypeTemplateProcessingException
697     {
698         File f;
699 
700         template = StringUtils.replace( template, "\\", "/" );
701 
702         if ( packageInFileName && packageName != null )
703         {
704             String templateFileName = StringUtils.replace( template, "/", File.separator );
705 
706             String path = packageName.replace( '.', '/' );
707 
708             String filename = FileUtils.filename( templateFileName );
709 
710             String dirname = FileUtils.dirname( templateFileName ).replace( '\\', '/' );
711 
712             sourceDirectory = sourceDirectory.replace( '\\', '/' );
713             if ( sourceDirectory.startsWith( "/" ) )
714             {
715                 sourceDirectory = sourceDirectory.substring( 1 );
716             }
717 
718             if ( !dirname.startsWith( sourceDirectory ) )
719             {
720                 throw new ArchetypeTemplateProcessingException(
721                     "Template '" + template + "' not in directory '" + sourceDirectory + "'" );
722             }
723 
724             String extraPackages = dirname.substring( sourceDirectory.length() );
725             if ( extraPackages.startsWith( "/" ) )
726             {
727                 extraPackages = extraPackages.substring( 1 );
728             }
729             if ( extraPackages.length() > 0 )
730             {
731                 path += "/" + extraPackages;
732             }
733 
734             f = new File( new File( new File( outputDirectory, sourceDirectory ), path ), filename );
735         }
736         else
737         {
738             f = new File( outputDirectory, template );
739         }
740 
741         if ( !f.getParentFile().exists() )
742         {
743             f.getParentFile().mkdirs();
744         }
745 
746         if ( !f.exists() && !f.createNewFile() )
747         {
748             getLogger().warn( "Could not create new file \"" + f.getPath() + "\" or the file already exists." );
749         }
750 
751         if ( descriptor.isFiltered() )
752         {
753             try ( Writer writer = new OutputStreamWriter( new FileOutputStream( f ), descriptor.getEncoding() ) )
754             {
755                 StringWriter stringWriter = new StringWriter();
756 
757                 template = ARCHETYPE_RESOURCES + "/" + template;
758 
759                 velocity.getEngine().mergeTemplate( template, descriptor.getEncoding(), context, stringWriter );
760 
761                 writer.write( StringUtils.unifyLineSeparators( stringWriter.toString() ) );
762             }
763             catch ( Exception e )
764             {
765                 throw new ArchetypeTemplateProcessingException( "Error merging velocity templates", e );
766             }
767         }
768         else
769         {
770             try ( InputStream is = getStream( ARCHETYPE_RESOURCES + "/" + template, null );
771                   OutputStream fos = new FileOutputStream( f ) )
772             {
773                 IOUtil.copy( is, fos );
774             }
775             catch ( Exception e )
776             {
777                 throw new ArchetypeTemplateProcessingException( "Error copying file", e );
778             }
779         }
780     }
781 
782     protected void createProjectDirectoryStructure( String outputDirectory )
783     {
784     }
785 
786     private InputStream getStream( String name, ClassLoader loader )
787     {
788         if ( loader == null )
789         {
790             return Thread.currentThread().getContextClassLoader().getResourceAsStream( name );
791         }
792         return loader.getResourceAsStream( name );
793     }
794 }