View Javadoc
1   package org.apache.maven.archetype.creator;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.OutputStream;
29  import java.io.Reader;
30  import java.io.Writer;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Properties;
40  import java.util.Set;
41  
42  import org.apache.commons.collections.CollectionUtils;
43  import org.apache.maven.archetype.ArchetypeCreationRequest;
44  import org.apache.maven.archetype.ArchetypeCreationResult;
45  import org.apache.maven.archetype.common.ArchetypeFilesResolver;
46  import org.apache.maven.archetype.common.Constants;
47  import org.apache.maven.archetype.common.PomManager;
48  import org.apache.maven.archetype.common.util.FileCharsetDetector;
49  import org.apache.maven.archetype.common.util.ListScanner;
50  import org.apache.maven.archetype.common.util.PathUtils;
51  import org.apache.maven.archetype.exception.TemplateCreationException;
52  import org.apache.maven.archetype.metadata.ArchetypeDescriptor;
53  import org.apache.maven.archetype.metadata.FileSet;
54  import org.apache.maven.archetype.metadata.ModuleDescriptor;
55  import org.apache.maven.archetype.metadata.RequiredProperty;
56  import org.apache.maven.archetype.metadata.io.xpp3.ArchetypeDescriptorXpp3Writer;
57  import org.apache.maven.model.Build;
58  import org.apache.maven.model.Dependency;
59  import org.apache.maven.model.Extension;
60  import org.apache.maven.model.Model;
61  import org.apache.maven.model.Plugin;
62  import org.apache.maven.model.PluginManagement;
63  import org.apache.maven.model.Profile;
64  import org.apache.maven.model.Resource;
65  import org.apache.maven.project.MavenProject;
66  import org.apache.maven.project.ProjectBuildingRequest;
67  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
68  import org.apache.maven.shared.invoker.InvocationRequest;
69  import org.apache.maven.shared.invoker.InvocationResult;
70  import org.apache.maven.shared.invoker.Invoker;
71  import org.codehaus.plexus.component.annotations.Component;
72  import org.codehaus.plexus.component.annotations.Requirement;
73  import org.codehaus.plexus.logging.AbstractLogEnabled;
74  import org.codehaus.plexus.util.DirectoryScanner;
75  import org.codehaus.plexus.util.FileUtils;
76  import org.codehaus.plexus.util.IOUtil;
77  import org.codehaus.plexus.util.ReaderFactory;
78  import org.codehaus.plexus.util.StringUtils;
79  import org.codehaus.plexus.util.WriterFactory;
80  import org.codehaus.plexus.util.xml.Xpp3Dom;
81  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
82  
83  /**
84   * Create a 2.x Archetype project from a project. Since 2.0-alpha-5, an integration-test named "basic" is created along
85   * the archetype itself to provide immediate test when building the archetype.
86   */
87  @Component( role = ArchetypeCreator.class, hint = "fileset" )
88  public class FilesetArchetypeCreator
89      extends AbstractLogEnabled
90      implements ArchetypeCreator
91  {
92      private static final String DEFAULT_OUTPUT_DIRECTORY =
93          "target" + File.separator + "generated-sources" + File.separator + "archetype";
94  
95      @Requirement
96      private ArchetypeFilesResolver archetypeFilesResolver;
97  
98      @Requirement
99      private PomManager pomManager;
100     
101     @Requirement
102     private Invoker invoker;
103 
104     @Override
105     public void createArchetype( ArchetypeCreationRequest request, ArchetypeCreationResult result )
106     {
107         MavenProject project = request.getProject();
108         List<String> languages = request.getLanguages();
109         List<String> filtereds = request.getFiltereds();
110         String defaultEncoding = request.getDefaultEncoding();
111         boolean preserveCData = request.isPreserveCData();
112         boolean keepParent = request.isKeepParent();
113         boolean partialArchetype = request.isPartialArchetype();
114         File outputDirectory = request.getOutputDirectory();
115         File basedir = project.getBasedir();
116 
117         Properties properties = new Properties();
118         Properties configurationProperties = new Properties();
119         if ( request.getProperties() != null )
120         {
121             properties.putAll( request.getProperties() );
122             configurationProperties.putAll( request.getProperties() );
123         }
124 
125         extractPropertiesFromProject( project, properties, configurationProperties, request.getPackageName() );
126 
127         if ( outputDirectory == null )
128         {
129             getLogger().debug( "No output directory defined, using default: " + DEFAULT_OUTPUT_DIRECTORY );
130             outputDirectory = FileUtils.resolveFile( basedir, DEFAULT_OUTPUT_DIRECTORY );
131         }
132         outputDirectory.mkdirs();
133 
134         getLogger().debug( "Creating archetype in " + outputDirectory );
135 
136         try
137         {
138             File archetypePomFile = createArchetypeProjectPom( project, request.getProjectBuildingRequest(),
139                                                                configurationProperties, outputDirectory );
140 
141             File archetypeResourcesDirectory = new File( outputDirectory, getTemplateOutputDirectory() );
142 
143             File archetypeFilesDirectory = new File( archetypeResourcesDirectory, Constants.ARCHETYPE_RESOURCES );
144             getLogger().debug( "Archetype's files output directory " + archetypeFilesDirectory );
145 
146             File archetypeDescriptorFile = new File( archetypeResourcesDirectory, Constants.ARCHETYPE_DESCRIPTOR );
147             archetypeDescriptorFile.getParentFile().mkdirs();
148 
149             File archetypePostGenerationScript =
150                 new File( archetypeResourcesDirectory, Constants.ARCHETYPE_POST_GENERATION_SCRIPT );
151             archetypePostGenerationScript.getParentFile().mkdirs();
152 
153             if ( request.getProject().getBuild() != null && CollectionUtils.isNotEmpty(
154                 request.getProject().getBuild().getResources() ) )
155             {
156                 for ( Resource resource : request.getProject().getBuild().getResources() )
157                 {
158                     File inputFile = new File(
159                         resource.getDirectory() + File.separator + Constants.ARCHETYPE_POST_GENERATION_SCRIPT );
160                     if ( inputFile.exists() )
161                     {
162                         FileUtils.copyFile( inputFile, archetypePostGenerationScript );
163                     }
164                 }
165             }
166 
167             getLogger().debug( "Starting archetype's descriptor " + project.getArtifactId() );
168             ArchetypeDescriptor archetypeDescriptor = new ArchetypeDescriptor();
169 
170             archetypeDescriptor.setName( project.getArtifactId() );
171             archetypeDescriptor.setPartial( partialArchetype );
172 
173             addRequiredProperties( archetypeDescriptor, properties );
174 
175             // TODO ensure reverseProperties contains NO dotted properties
176             Properties reverseProperties = getReversedProperties( archetypeDescriptor, properties );
177             // reverseProperties.remove( Constants.GROUP_ID );
178 
179             // TODO ensure pomReversedProperties contains NO dotted properties
180             Properties pomReversedProperties = getReversedProperties( archetypeDescriptor, properties );
181             // pomReversedProperties.remove( Constants.PACKAGE );
182 
183             String packageName = configurationProperties.getProperty( Constants.PACKAGE );
184 
185             Model pom = pomManager.readPom( project.getFile() );
186 
187             List<String> excludePatterns =
188                 configurationProperties.getProperty( Constants.EXCLUDE_PATTERNS ) != null
189                     ? Arrays.asList(
190                     StringUtils.split( configurationProperties.getProperty( Constants.EXCLUDE_PATTERNS ), "," ) )
191                     : Collections.<String>emptyList();
192 
193             List<String> fileNames = resolveFileNames( pom, basedir, excludePatterns );
194             if ( getLogger().isDebugEnabled() )
195             {
196                 getLogger().debug( "Scanned for files " + fileNames.size() );
197 
198                 for ( String name : fileNames )
199                 {
200                     getLogger().debug( "- " + name );
201                 }
202             }
203 
204             List<FileSet> filesets = resolveFileSets( packageName, fileNames, languages, filtereds, defaultEncoding );
205             getLogger().debug( "Resolved filesets for " + archetypeDescriptor.getName() );
206 
207             archetypeDescriptor.setFileSets( filesets );
208 
209             createArchetypeFiles( reverseProperties, filesets, packageName, basedir, archetypeFilesDirectory,
210                                   defaultEncoding, excludePatterns );
211             getLogger().debug( "Created files for " + archetypeDescriptor.getName() );
212 
213             setParentArtifactId( reverseProperties, configurationProperties.getProperty( Constants.ARTIFACT_ID ) );
214 
215             for ( String moduleId : pom.getModules() )
216             {
217                 String rootArtifactId = configurationProperties.getProperty( Constants.ARTIFACT_ID );
218                 String moduleIdDirectory = moduleId;
219 
220                 if ( moduleId.indexOf( rootArtifactId ) >= 0 )
221                 {
222                     moduleIdDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
223                 }
224 
225                 getLogger().debug( "Creating module " + moduleId );
226 
227                 ModuleDescriptor moduleDescriptor =
228                     createModule( reverseProperties, rootArtifactId, moduleId, packageName,
229                                   FileUtils.resolveFile( basedir, moduleId ),
230                                   new File( archetypeFilesDirectory, moduleIdDirectory ), languages, filtereds,
231                                   defaultEncoding, preserveCData, keepParent );
232 
233                 archetypeDescriptor.addModule( moduleDescriptor );
234 
235                 getLogger().debug(
236                     "Added module " + moduleDescriptor.getName() + " in " + archetypeDescriptor.getName() );
237             }
238 
239             restoreParentArtifactId( reverseProperties, null );
240             restoreArtifactId( reverseProperties, configurationProperties.getProperty( Constants.ARTIFACT_ID ) );
241 
242             createPoms( pom, configurationProperties.getProperty( Constants.ARTIFACT_ID ),
243                         configurationProperties.getProperty( Constants.ARTIFACT_ID ), archetypeFilesDirectory, basedir,
244                         project.getFile(), pomReversedProperties, preserveCData, keepParent );
245             getLogger().debug( "Created Archetype " + archetypeDescriptor.getName() + " template pom(s)" );
246 
247             
248             try ( Writer out = WriterFactory.newXmlWriter( archetypeDescriptorFile ) )
249             {
250                 ArchetypeDescriptorXpp3Writer writer = new ArchetypeDescriptorXpp3Writer();
251 
252                 writer.write( out, archetypeDescriptor );
253 
254                 getLogger().debug( "Archetype " + archetypeDescriptor.getName() + " descriptor written" );
255             }
256 
257             createArchetypeBasicIt( archetypeDescriptor, outputDirectory );
258 
259             // Copy archetype integration tests.
260             File archetypeIntegrationTestInputFolder =
261                 new File( basedir, Constants.SRC + File.separator + "it" + File.separator + "projects" );
262             File archetypeIntegrationTestOutputFolder = new File( outputDirectory,
263                                                                   Constants.SRC + File.separator + Constants.TEST
264                                                                       + File.separator + Constants.RESOURCES
265                                                                       + File.separator + "projects" );
266 
267             if ( archetypeIntegrationTestInputFolder.exists() )
268             {
269                 getLogger().info( "Copying: " + archetypeIntegrationTestInputFolder.getAbsolutePath() + " into "
270                                       + archetypeIntegrationTestOutputFolder.getAbsolutePath() );
271 
272                 FileUtils.copyDirectoryStructure( archetypeIntegrationTestInputFolder,
273                                                   archetypeIntegrationTestOutputFolder );
274 
275             }
276             InvocationRequest internalRequest = new DefaultInvocationRequest();
277             internalRequest.setPomFile( archetypePomFile );
278             internalRequest.setUserSettingsFile( request.getSettingsFile() );
279             internalRequest.setGoals( Collections.singletonList( request.getPostPhase() ) );
280             if ( request.getLocalRepository() != null )
281             {
282                 internalRequest.setLocalRepositoryDirectory( new File( request.getLocalRepository().getBasedir() ) );
283             }
284             
285             String httpsProtocols = System.getProperty( "https.protocols" );
286             if ( httpsProtocols != null )
287             {
288                 Properties userProperties = new Properties();
289                 userProperties.setProperty( "https.protocols", httpsProtocols );
290                 internalRequest.setProperties( userProperties );
291             }
292 
293             InvocationResult invokerResult = invoker.execute( internalRequest );
294             if ( invokerResult.getExitCode() != 0 )
295             {
296                 if ( invokerResult.getExecutionException() != null )
297                 {
298                     throw invokerResult.getExecutionException();
299                 }
300                 else
301                 {
302                     throw new Exception( "Invoker process ended with result different than 0!" );
303                 }
304             }
305 
306         }
307         catch ( Exception e )
308         {
309             result.setCause( e );
310         }
311     }
312 
313     /**
314      * Create an archetype IT, ie goals.txt and archetype.properties in src/test/resources/projects/basic.
315      *
316      * @param archetypeDescriptor
317      * @param generatedSourcesDirectory
318      * @throws IOException
319      * @since 2.0-alpha-5
320      */
321     private void createArchetypeBasicIt( ArchetypeDescriptor archetypeDescriptor, File generatedSourcesDirectory )
322         throws IOException
323     {
324         String basic =
325             Constants.SRC + File.separator + Constants.TEST + File.separator + Constants.RESOURCES + File.separator
326                 + "projects" + File.separator + "basic";
327         File basicItDirectory = new File( generatedSourcesDirectory, basic );
328         basicItDirectory.mkdirs();
329 
330         try ( InputStream in = FilesetArchetypeCreator.class.getResourceAsStream( "archetype.properties" );
331               OutputStream out = new FileOutputStream( new File( basicItDirectory, "archetype.properties" ) ) )
332         {
333             Properties archetypeProperties = new Properties();
334             archetypeProperties.load( in );
335 
336             for ( RequiredProperty req : archetypeDescriptor.getRequiredProperties() )
337             {
338                 archetypeProperties.put( req.getKey(), req.getDefaultValue() );
339             }
340 
341             archetypeProperties.store( out, null );
342         }
343 
344         copyResource( "goal.txt", new File( basicItDirectory, "goal.txt" ) );
345 
346         getLogger().debug( "Added basic integration test" );
347     }
348 
349     private void extractPropertiesFromProject( MavenProject project, Properties properties,
350                                                Properties configurationProperties, String packageName )
351     {
352         if ( !properties.containsKey( Constants.GROUP_ID ) )
353         {
354             properties.setProperty( Constants.GROUP_ID, project.getGroupId() );
355         }
356         configurationProperties.setProperty( Constants.GROUP_ID, properties.getProperty( Constants.GROUP_ID ) );
357 
358         if ( !properties.containsKey( Constants.ARTIFACT_ID ) )
359         {
360             properties.setProperty( Constants.ARTIFACT_ID, project.getArtifactId() );
361         }
362         configurationProperties.setProperty( Constants.ARTIFACT_ID, properties.getProperty( Constants.ARTIFACT_ID ) );
363 
364         if ( !properties.containsKey( Constants.VERSION ) )
365         {
366             properties.setProperty( Constants.VERSION, project.getVersion() );
367         }
368         configurationProperties.setProperty( Constants.VERSION, properties.getProperty( Constants.VERSION ) );
369 
370         if ( packageName != null )
371         {
372             properties.setProperty( Constants.PACKAGE, packageName );
373         }
374         else if ( !properties.containsKey( Constants.PACKAGE ) )
375         {
376             properties.setProperty( Constants.PACKAGE, project.getGroupId() );
377         }
378         configurationProperties.setProperty( Constants.PACKAGE, properties.getProperty( Constants.PACKAGE ) );
379     }
380 
381     /**
382      * Create the archetype project pom.xml file, that will be used to build the archetype.
383      */
384     private File createArchetypeProjectPom( MavenProject project, ProjectBuildingRequest buildingRequest,
385                                             Properties configurationProperties, File projectDir )
386         throws TemplateCreationException, IOException
387     {
388         Model model = new Model();
389         model.setModelVersion( "4.0.0" );
390         // these values should be retrieved from the request with sensible defaults
391         model.setGroupId( configurationProperties.getProperty( Constants.ARCHETYPE_GROUP_ID, project.getGroupId() ) );
392         model.setArtifactId(
393             configurationProperties.getProperty( Constants.ARCHETYPE_ARTIFACT_ID, project.getArtifactId() ) );
394         model.setVersion( configurationProperties.getProperty( Constants.ARCHETYPE_VERSION, project.getVersion() ) );
395         model.setPackaging( "maven-archetype" );
396         model.setName(
397             configurationProperties.getProperty( Constants.ARCHETYPE_ARTIFACT_ID, project.getArtifactId() ) );
398         model.setUrl( configurationProperties.getProperty( Constants.ARCHETYPE_URL, project.getUrl() ) );
399         model.setDescription(
400             configurationProperties.getProperty( Constants.ARCHETYPE_DESCRIPTION, project.getDescription() ) );
401         model.setLicenses( project.getLicenses() );
402         model.setDevelopers( project.getDevelopers() );
403         model.setScm( project.getScm() );
404         Build build = new Build();
405         model.setBuild( build );
406 
407         // In many cases where we are behind a firewall making Archetypes for work mates we want
408         // to simply be able to deploy the archetypes once we have created them. In order to do
409         // this we want to utilize information from the project we are creating the archetype from.
410         // This will be a fully working project that has been testing and inherits from a POM
411         // that contains deployment information, along with any extensions required for deployment.
412         // We don't want to create archetypes that cannot be deployed after we create them. People
413         // might want to edit the archetype POM but they should not have too.
414 
415         if ( project.getParent() != null )
416         {
417             MavenProject p = project.getParent();
418 
419             if ( p.getDistributionManagement() != null )
420             {
421                 model.setDistributionManagement( p.getDistributionManagement() );
422             }
423 
424             if ( p.getBuildExtensions() != null )
425             {
426                 for ( Extension be : p.getBuildExtensions() )
427                 {
428                     model.getBuild().addExtension( be );
429                 }
430             }
431         }
432 
433         Extension extension = new Extension();
434         extension.setGroupId( "org.apache.maven.archetype" );
435         extension.setArtifactId( "archetype-packaging" );
436         extension.setVersion( getArchetypeVersion() );
437         model.getBuild().addExtension( extension );
438 
439         Plugin plugin = new Plugin();
440         plugin.setGroupId( "org.apache.maven.plugins" );
441         plugin.setArtifactId( "maven-archetype-plugin" );
442         plugin.setVersion( getArchetypeVersion() );
443 
444         PluginManagement pluginManagement = new PluginManagement();
445         pluginManagement.addPlugin( plugin );
446         model.getBuild().setPluginManagement( pluginManagement );
447 
448         getLogger().debug( "Creating archetype's pom" );
449 
450         File archetypePomFile = new File( projectDir, Constants.ARCHETYPE_POM );
451 
452         archetypePomFile.getParentFile().mkdirs();
453 
454         copyResource( "pom-prototype.xml", archetypePomFile );
455 
456         pomManager.writePom( model, archetypePomFile, archetypePomFile );
457 
458         return archetypePomFile;
459     }
460 
461     private void copyResource( String name, File destination )
462         throws IOException
463     {
464         try ( InputStream in = FilesetArchetypeCreator.class.getResourceAsStream( name );
465               OutputStream out = new FileOutputStream( destination ) )
466         {
467             IOUtil.copy( in, out );
468         }
469     }
470 
471     private void addRequiredProperties( ArchetypeDescriptor archetypeDescriptor, Properties properties )
472     {
473         Properties requiredProperties = new Properties();
474         requiredProperties.putAll( properties );
475         requiredProperties.remove( Constants.ARCHETYPE_GROUP_ID );
476         requiredProperties.remove( Constants.ARCHETYPE_ARTIFACT_ID );
477         requiredProperties.remove( Constants.ARCHETYPE_VERSION );
478         requiredProperties.remove( Constants.GROUP_ID );
479         requiredProperties.remove( Constants.ARTIFACT_ID );
480         requiredProperties.remove( Constants.VERSION );
481         requiredProperties.remove( Constants.PACKAGE );
482         requiredProperties.remove( Constants.EXCLUDE_PATTERNS );
483 
484         for ( Iterator<?> propertiesIterator = requiredProperties.keySet().iterator(); propertiesIterator.hasNext(); )
485         {
486             String propertyKey = (String) propertiesIterator.next();
487 
488             RequiredProperty requiredProperty = new RequiredProperty();
489             requiredProperty.setKey( propertyKey );
490             requiredProperty.setDefaultValue( requiredProperties.getProperty( propertyKey ) );
491 
492             archetypeDescriptor.addRequiredProperty( requiredProperty );
493 
494             getLogger().debug(
495                 "Adding requiredProperty " + propertyKey + "=" + requiredProperties.getProperty( propertyKey )
496                     + " to archetype's descriptor" );
497         }
498     }
499 
500     private void createModulePoms( Properties pomReversedProperties, String rootArtifactId, String packageName,
501                                    File basedir, File archetypeFilesDirectory, boolean preserveCData,
502                                    boolean keepParent )
503         throws FileNotFoundException, IOException, XmlPullParserException
504     {
505         Model pom = pomManager.readPom( FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ) );
506 
507         String parentArtifactId = pomReversedProperties.getProperty( Constants.PARENT_ARTIFACT_ID );
508         String artifactId = pom.getArtifactId();
509         setParentArtifactId( pomReversedProperties, pomReversedProperties.getProperty( Constants.ARTIFACT_ID ) );
510         setArtifactId( pomReversedProperties, pom.getArtifactId() );
511 
512         for ( String subModuleId : pom.getModules() )
513         {
514             String subModuleIdDirectory = subModuleId;
515 
516             if ( subModuleId.indexOf( rootArtifactId ) >= 0 )
517             {
518                 subModuleIdDirectory = StringUtils.replace( subModuleId, rootArtifactId, "__rootArtifactId__" );
519             }
520 
521             createModulePoms( pomReversedProperties, rootArtifactId, packageName,
522                               FileUtils.resolveFile( basedir, subModuleId ),
523                               FileUtils.resolveFile( archetypeFilesDirectory, subModuleIdDirectory ), preserveCData,
524                               keepParent );
525         }
526 
527         createModulePom( pom, rootArtifactId, archetypeFilesDirectory, pomReversedProperties,
528                          FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ), preserveCData, keepParent );
529 
530         restoreParentArtifactId( pomReversedProperties, parentArtifactId );
531         restoreArtifactId( pomReversedProperties, artifactId );
532     }
533 
534     private void createPoms( Model pom, String rootArtifactId, String artifactId, File archetypeFilesDirectory,
535                              File basedir, File rootPom, Properties pomReversedProperties, boolean preserveCData,
536                              boolean keepParent )
537         throws IOException, FileNotFoundException, XmlPullParserException
538     {
539         setArtifactId( pomReversedProperties, pom.getArtifactId() );
540 
541         for ( String moduleId : pom.getModules() )
542         {
543             String moduleIdDirectory = moduleId;
544 
545             if ( moduleId.indexOf( rootArtifactId ) >= 0 )
546             {
547                 moduleIdDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
548             }
549 
550             createModulePoms( pomReversedProperties, rootArtifactId, moduleId,
551                               FileUtils.resolveFile( basedir, moduleId ),
552                               new File( archetypeFilesDirectory, moduleIdDirectory ), preserveCData, keepParent );
553         }
554 
555         restoreParentArtifactId( pomReversedProperties, null );
556         restoreArtifactId( pomReversedProperties, artifactId );
557 
558         createArchetypePom( pom, archetypeFilesDirectory, pomReversedProperties, rootPom, preserveCData, keepParent );
559     }
560 
561     private String getPackageInPathFormat( String aPackage )
562     {
563         return StringUtils.replace( aPackage, ".", "/" );
564     }
565 
566     private void rewriteReferences( Model pom, String rootArtifactId, String groupId )
567     {
568         // rewrite Dependencies
569         if ( pom.getDependencies() != null && !pom.getDependencies().isEmpty() )
570         {
571             for ( Dependency dependency : pom.getDependencies() )
572             {
573                 rewriteDependencyReferences( dependency, rootArtifactId, groupId );
574             }
575         }
576 
577         // rewrite DependencyManagement
578         if ( pom.getDependencyManagement() != null && pom.getDependencyManagement().getDependencies() != null
579             && !pom.getDependencyManagement().getDependencies().isEmpty() )
580         {
581             for ( Dependency dependency : pom.getDependencyManagement().getDependencies() )
582             {
583                 rewriteDependencyReferences( dependency, rootArtifactId, groupId );
584             }
585         }
586 
587         // rewrite Plugins
588         if ( pom.getBuild() != null && pom.getBuild().getPlugins() != null && !pom.getBuild().getPlugins().isEmpty() )
589         {
590             for ( Plugin plugin : pom.getBuild().getPlugins() )
591             {
592                 rewritePluginReferences( plugin, rootArtifactId, groupId );
593             }
594         }
595 
596         // rewrite PluginManagement
597         if ( pom.getBuild() != null && pom.getBuild().getPluginManagement() != null
598             && pom.getBuild().getPluginManagement().getPlugins() != null
599             && !pom.getBuild().getPluginManagement().getPlugins().isEmpty() )
600         {
601             for ( Plugin plugin : pom.getBuild().getPluginManagement().getPlugins() )
602             {
603                 rewritePluginReferences( plugin, rootArtifactId, groupId );
604             }
605         }
606 
607         // rewrite Profiles
608         if ( pom.getProfiles() != null )
609         {
610             for ( Profile profile : pom.getProfiles() )
611             {
612                 // rewrite Dependencies
613                 if ( profile.getDependencies() != null && !profile.getDependencies().isEmpty() )
614                 {
615                     for ( Dependency dependency : profile.getDependencies() )
616                     {
617                         rewriteDependencyReferences( dependency, rootArtifactId, groupId );
618                     }
619                 }
620 
621                 // rewrite DependencyManagement
622                 if ( profile.getDependencyManagement() != null
623                     && profile.getDependencyManagement().getDependencies() != null
624                     && !profile.getDependencyManagement().getDependencies().isEmpty() )
625                 {
626                     for ( Dependency dependency : profile.getDependencyManagement().getDependencies() )
627                     {
628                         rewriteDependencyReferences( dependency, rootArtifactId, groupId );
629                     }
630                 }
631 
632                 // rewrite Plugins
633                 if ( profile.getBuild() != null && profile.getBuild().getPlugins() != null
634                     && !profile.getBuild().getPlugins().isEmpty() )
635                 {
636                     for ( Plugin plugin : profile.getBuild().getPlugins() )
637                     {
638                         rewritePluginReferences( plugin, rootArtifactId, groupId );
639                     }
640                 }
641 
642                 // rewrite PluginManagement
643                 if ( profile.getBuild() != null && profile.getBuild().getPluginManagement() != null
644                     && profile.getBuild().getPluginManagement().getPlugins() != null
645                     && !profile.getBuild().getPluginManagement().getPlugins().isEmpty() )
646                 {
647                     for ( Plugin plugin : profile.getBuild().getPluginManagement().getPlugins() )
648                     {
649                         rewritePluginReferences( plugin, rootArtifactId, groupId );
650                     }
651                 }
652             }
653         }
654     }
655 
656     private void rewriteDependencyReferences( Dependency dependency, String rootArtifactId, String groupId )
657     {
658         if ( dependency.getArtifactId() != null && dependency.getArtifactId().indexOf( rootArtifactId ) >= 0 )
659         {
660             if ( dependency.getGroupId() != null )
661             {
662                 dependency.setGroupId(
663                     StringUtils.replace( dependency.getGroupId(), groupId, "${" + Constants.GROUP_ID + "}" ) );
664             }
665 
666             dependency.setArtifactId(
667                 StringUtils.replace( dependency.getArtifactId(), rootArtifactId, "${rootArtifactId}" ) );
668 
669             if ( dependency.getVersion() != null )
670             {
671                 dependency.setVersion( "${" + Constants.VERSION + "}" );
672             }
673         }
674     }
675 
676     private void rewritePluginReferences( Plugin plugin, String rootArtifactId, String groupId )
677     {
678         if ( plugin.getArtifactId() != null && plugin.getArtifactId().indexOf( rootArtifactId ) >= 0 )
679         {
680             if ( plugin.getGroupId() != null )
681             {
682                 String g = StringUtils.replace( plugin.getGroupId(), groupId, "${" + Constants.GROUP_ID + "}" );
683                 plugin.setGroupId( g );
684             }
685 
686             plugin.setArtifactId( StringUtils.replace( plugin.getArtifactId(), rootArtifactId, "${rootArtifactId}" ) );
687 
688             if ( plugin.getVersion() != null )
689             {
690                 plugin.setVersion( "${" + Constants.VERSION + "}" );
691             }
692         }
693 
694         if ( plugin.getArtifactId() != null && "maven-ear-plugin".equals( plugin.getArtifactId() ) )
695         {
696             rewriteEARPluginReferences( plugin, rootArtifactId, groupId );
697         }
698     }
699 
700     private void rewriteEARPluginReferences( Plugin plugin, String rootArtifactId, String groupId )
701     {
702         Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
703         if ( configuration != null )
704         {
705             Xpp3Dom[] modules = configuration.getChild( "modules" ).getChildren();
706             for ( int i = 0; i < modules.length; i++ )
707             {
708                 Xpp3Dom module = modules[i];
709                 Xpp3Dom moduleGroupId = module.getChild( "groupId" );
710                 Xpp3Dom moduleArtifactId = module.getChild( "artifactId" );
711                 Xpp3Dom moduleBundleFileName = module.getChild( "bundleFileName" );
712                 Xpp3Dom moduleModuleId = module.getChild( "moduleId" );
713                 Xpp3Dom moduleContextRoot = module.getChild( "contextRoot" );
714 
715                 if ( moduleGroupId != null )
716                 {
717                     moduleGroupId.setValue(
718                         StringUtils.replace( moduleGroupId.getValue(), groupId, "${" + Constants.GROUP_ID + "}" ) );
719                 }
720 
721                 if ( moduleArtifactId != null )
722                 {
723                     moduleArtifactId.setValue(
724                         StringUtils.replace( moduleArtifactId.getValue(), rootArtifactId, "${rootArtifactId}" ) );
725                 }
726 
727                 if ( moduleBundleFileName != null )
728                 {
729                     moduleBundleFileName.setValue(
730                         StringUtils.replace( moduleBundleFileName.getValue(), rootArtifactId, "${rootArtifactId}" ) );
731                 }
732 
733                 if ( moduleModuleId != null )
734                 {
735                     moduleModuleId.setValue(
736                         StringUtils.replace( moduleModuleId.getValue(), rootArtifactId, "${rootArtifactId}" ) );
737                 }
738 
739                 if ( moduleContextRoot != null )
740                 {
741                     moduleContextRoot.setValue(
742                         StringUtils.replace( moduleContextRoot.getValue(), rootArtifactId, "${rootArtifactId}" ) );
743                 }
744             }
745         }
746     }
747 
748     private void setArtifactId( Properties properties, String artifactId )
749     {
750         properties.setProperty( Constants.ARTIFACT_ID, artifactId );
751     }
752 
753     private List<String> concatenateToList( List<String> toConcatenate, String with )
754     {
755         List<String> result = new ArrayList<>( toConcatenate.size() );
756 
757         for ( String concatenate : toConcatenate )
758         {
759             result.add( ( ( with.length() > 0 ) ? ( with + "/" + concatenate ) : concatenate ) );
760         }
761 
762         return result;
763     }
764 
765     private List<String> addLists( List<String> list, List<String> other )
766     {
767         List<String> result = new ArrayList<>( list.size() + other.size() );
768         result.addAll( list );
769         result.addAll( other );
770         return result;
771     }
772 
773     private void copyFiles( File basedir, File archetypeFilesDirectory, String directory, List<String> fileSetResources,
774                             boolean packaged, String packageName, Properties reverseProperties )
775         throws IOException
776     {
777         String packageAsDirectory = StringUtils.replace( packageName, ".", File.separator );
778 
779         getLogger().debug( "Package as Directory: Package:" + packageName + "->" + packageAsDirectory );
780 
781         for ( String inputFileName : fileSetResources )
782         {
783             String outputFileName = packaged
784                 ? StringUtils.replace( inputFileName, packageAsDirectory + File.separator, "" )
785                 : inputFileName;
786             getLogger().debug( "InputFileName:" + inputFileName );
787             getLogger().debug( "OutputFileName:" + outputFileName );
788 
789             reverseProperties.remove( "archetype.languages" );
790 
791             String reversedOutputFilename = getReversedFilename( outputFileName, reverseProperties );
792 
793             File outputFile = new File( archetypeFilesDirectory, reversedOutputFilename );
794 
795             File inputFile = new File( basedir, inputFileName );
796 
797             outputFile.getParentFile().mkdirs();
798 
799             FileUtils.copyFile( inputFile, outputFile );
800         }
801     }
802 
803     private void createArchetypeFiles( Properties reverseProperties, List<FileSet> fileSets, String packageName,
804                                        File basedir, File archetypeFilesDirectory, String defaultEncoding,
805                                        List<String> excludePatterns )
806         throws IOException
807     {
808         getLogger().debug( "Creating Archetype/Module files from " + basedir + " to " + archetypeFilesDirectory );
809 
810         for ( FileSet fileSet : fileSets )
811         {
812             DirectoryScanner scanner = new DirectoryScanner();
813             scanner.setBasedir( basedir );
814             scanner.setIncludes( concatenateToList( fileSet.getIncludes(), fileSet.getDirectory() ).toArray(
815                 new String[fileSet.getIncludes().size()] ) );
816             scanner.setExcludes( addLists( fileSet.getExcludes(), excludePatterns ).toArray(
817                 new String[fileSet.getExcludes().size()] ) );
818             scanner.addDefaultExcludes();
819             getLogger().debug( "Using fileset " + fileSet );
820             scanner.scan();
821 
822             List<String> fileSetResources = Arrays.asList( scanner.getIncludedFiles() );
823             getLogger().debug( "Scanned " + fileSetResources.size() + " resources" );
824 
825             if ( fileSet.isFiltered() )
826             {
827                 processFileSet( basedir, archetypeFilesDirectory, fileSet.getDirectory(), fileSetResources,
828                                 fileSet.isPackaged(), packageName, reverseProperties, defaultEncoding );
829                 getLogger().debug( "Processed " + fileSet.getDirectory() + " files" );
830             }
831             else
832             {
833                 copyFiles( basedir, archetypeFilesDirectory, fileSet.getDirectory(), fileSetResources,
834                            fileSet.isPackaged(), packageName, reverseProperties );
835                 getLogger().debug( "Copied " + fileSet.getDirectory() + " files" );
836             }
837         }
838     }
839 
840     private void createArchetypePom( Model pom, File archetypeFilesDirectory, Properties pomReversedProperties,
841                                      File initialPomFile, boolean preserveCData, boolean keepParent )
842         throws IOException
843     {
844         File outputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM );
845 
846         if ( preserveCData )
847         {
848             getLogger().debug( "Preserving CDATA parts of pom" );
849             File inputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM + ".tmp" );
850 
851             FileUtils.copyFile( initialPomFile, inputFile );
852 
853             try ( Reader in = ReaderFactory.newXmlReader( inputFile );
854                   Writer out = WriterFactory.newXmlWriter( outputFile ) )
855             {
856                 String initialcontent = IOUtil.toString( in );
857 
858                 String content = getReversedContent( initialcontent, pomReversedProperties );
859 
860                 outputFile.getParentFile().mkdirs();
861 
862                 IOUtil.copy( content, out );
863             }
864 
865             inputFile.delete();
866         }
867         else
868         {
869             if ( !keepParent )
870             {
871                 pom.setParent( null );
872             }
873 
874             pom.setModules( null );
875             pom.setGroupId( "${" + Constants.GROUP_ID + "}" );
876             pom.setArtifactId( "${" + Constants.ARTIFACT_ID + "}" );
877             pom.setVersion( "${" + Constants.VERSION + "}" );
878             pom.setName( getReversedPlainContent( pom.getName(), pomReversedProperties ) );
879             pom.setDescription( getReversedPlainContent( pom.getDescription(), pomReversedProperties ) );
880             pom.setUrl( getReversedPlainContent( pom.getUrl(), pomReversedProperties ) );
881 
882             rewriteReferences( pom, pomReversedProperties.getProperty( Constants.ARTIFACT_ID ),
883                                pomReversedProperties.getProperty( Constants.GROUP_ID ) );
884 
885             pomManager.writePom( pom, outputFile, initialPomFile );
886         }
887 
888         try ( Reader in = ReaderFactory.newXmlReader( initialPomFile ) )
889         {
890             String initialcontent = IOUtil.toString( in );
891 
892             Iterator<?> properties = pomReversedProperties.keySet().iterator();
893             while ( properties.hasNext() )
894             {
895                 String property = (String) properties.next();
896 
897                 if ( initialcontent.indexOf( "${" + property + "}" ) > 0 )
898                 {
899                     getLogger().warn(
900                         "Archetype uses ${" + property + "} for internal processing, but file " + initialPomFile
901                             + " contains this property already" );
902                 }
903             }
904         }
905     }
906 
907     private FileSet createFileSet( final List<String> excludes, final boolean packaged, final boolean filtered,
908                                    final String group, final List<String> includes, String defaultEncoding )
909     {
910         FileSet fileSet = new FileSet();
911 
912         fileSet.setDirectory( group );
913         fileSet.setPackaged( packaged );
914         fileSet.setFiltered( filtered );
915         fileSet.setIncludes( includes );
916         fileSet.setExcludes( excludes );
917         fileSet.setEncoding( defaultEncoding );
918 
919         getLogger().debug( "Created Fileset " + fileSet );
920 
921         return fileSet;
922     }
923 
924     private List<FileSet> createFileSets( List<String> files, int level, boolean packaged, String packageName,
925                                           boolean filtered, String defaultEncoding )
926     {
927         List<FileSet> fileSets = new ArrayList<>();
928 
929         if ( !files.isEmpty() )
930         {
931             getLogger().debug( "Creating filesets" + ( packaged ? ( " packaged (" + packageName + ")" ) : "" ) + (
932                 filtered
933                     ? " filtered"
934                     : "" ) + " at level " + level );
935             if ( level == 0 )
936             {
937                 List<String> includes = new ArrayList<>( files );
938                 List<String> excludes = new ArrayList<>();
939 
940                 if ( !includes.isEmpty() )
941                 {
942                     fileSets.add( createFileSet( excludes, packaged, filtered, "", includes, defaultEncoding ) );
943                 }
944             }
945             else
946             {
947                 Map<String, List<String>> groups = getGroupsMap( files, level );
948 
949                 for ( String group : groups.keySet() )
950                 {
951                     getLogger().debug( "Creating filesets for group " + group );
952 
953                     if ( !packaged )
954                     {
955                         fileSets.add( getUnpackagedFileSet( filtered, group, groups.get( group ), defaultEncoding ) );
956                     }
957                     else
958                     {
959                         fileSets.addAll(
960                             getPackagedFileSets( filtered, group, groups.get( group ), packageName, defaultEncoding ) );
961                     }
962                 }
963             } // end if
964 
965             getLogger().debug( "Resolved fileSets " + fileSets );
966         } // end if
967 
968         return fileSets;
969     }
970 
971     private ModuleDescriptor createModule( Properties reverseProperties, String rootArtifactId, String moduleId,
972                                            String packageName, File basedir, File archetypeFilesDirectory,
973                                            List<String> languages, List<String> filtereds, String defaultEncoding,
974                                            boolean preserveCData, boolean keepParent )
975         throws IOException, XmlPullParserException
976     {
977         ModuleDescriptor archetypeDescriptor = new ModuleDescriptor();
978         getLogger().debug( "Starting module's descriptor " + moduleId );
979 
980         archetypeFilesDirectory.mkdirs();
981         getLogger().debug( "Module's files output directory " + archetypeFilesDirectory );
982 
983         Model pom = pomManager.readPom( FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ) );
984         String replacementId = pom.getArtifactId();
985         String moduleDirectory = pom.getArtifactId();
986 
987         if ( replacementId.indexOf( rootArtifactId ) >= 0 )
988         {
989             replacementId = StringUtils.replace( replacementId, rootArtifactId, "${rootArtifactId}" );
990             moduleDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
991         }
992 
993         if ( moduleId.indexOf( rootArtifactId ) >= 0 )
994         {
995             moduleDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
996         }
997 
998         archetypeDescriptor.setName( replacementId );
999         archetypeDescriptor.setId( replacementId );
1000         archetypeDescriptor.setDir( moduleDirectory );
1001 
1002         setArtifactId( reverseProperties, pom.getArtifactId() );
1003 
1004         List<String> excludePatterns =
1005             reverseProperties.getProperty( Constants.EXCLUDE_PATTERNS ) != null
1006                 ? Arrays.asList( StringUtils.split( reverseProperties.getProperty( Constants.EXCLUDE_PATTERNS ), "," ) )
1007                 : Collections.<String>emptyList();
1008 
1009         List<String> fileNames = resolveFileNames( pom, basedir, excludePatterns );
1010 
1011         List<FileSet> filesets = resolveFileSets( packageName, fileNames, languages, filtereds, defaultEncoding );
1012         getLogger().debug( "Resolved filesets for module " + archetypeDescriptor.getName() );
1013 
1014         archetypeDescriptor.setFileSets( filesets );
1015 
1016         createArchetypeFiles( reverseProperties, filesets, packageName, basedir, archetypeFilesDirectory,
1017                               defaultEncoding, excludePatterns );
1018         getLogger().debug( "Created files for module " + archetypeDescriptor.getName() );
1019 
1020         String parentArtifactId = reverseProperties.getProperty( Constants.PARENT_ARTIFACT_ID );
1021         setParentArtifactId( reverseProperties, pom.getArtifactId() );
1022 
1023         for ( String subModuleId : pom.getModules() )
1024         {
1025             String subModuleIdDirectory = subModuleId;
1026             if ( subModuleId.indexOf( rootArtifactId ) >= 0 )
1027             {
1028                 subModuleIdDirectory = StringUtils.replace( subModuleId, rootArtifactId, "__rootArtifactId__" );
1029             }
1030 
1031             getLogger().debug( "Creating module " + subModuleId );
1032 
1033             ModuleDescriptor moduleDescriptor =
1034                 createModule( reverseProperties, rootArtifactId, subModuleId, packageName,
1035                               FileUtils.resolveFile( basedir, subModuleId ),
1036                               FileUtils.resolveFile( archetypeFilesDirectory, subModuleIdDirectory ), languages,
1037                               filtereds, defaultEncoding, preserveCData, keepParent );
1038 
1039             archetypeDescriptor.addModule( moduleDescriptor );
1040 
1041             getLogger().debug( "Added module " + moduleDescriptor.getName() + " in " + archetypeDescriptor.getName() );
1042         }
1043 
1044         restoreParentArtifactId( reverseProperties, parentArtifactId );
1045         restoreArtifactId( reverseProperties, pom.getArtifactId() );
1046 
1047         getLogger().debug( "Created Module " + archetypeDescriptor.getName() + " pom" );
1048 
1049         return archetypeDescriptor;
1050     }
1051 
1052     private void createModulePom( Model pom, String rootArtifactId, File archetypeFilesDirectory,
1053                                   Properties pomReversedProperties, File initialPomFile, boolean preserveCData,
1054                                   boolean keepParent )
1055         throws IOException
1056     {
1057         File outputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM );
1058 
1059         if ( preserveCData )
1060         {
1061             getLogger().debug( "Preserving CDATA parts of pom" );
1062             File inputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM + ".tmp" );
1063 
1064             FileUtils.copyFile( initialPomFile, inputFile );
1065 
1066             try ( Reader in = ReaderFactory.newXmlReader( inputFile );
1067                   Writer out = WriterFactory.newXmlWriter( outputFile ) )
1068             {
1069                 String initialcontent = IOUtil.toString( in );
1070 
1071                 String content = getReversedContent( initialcontent, pomReversedProperties );
1072 
1073                 outputFile.getParentFile().mkdirs();
1074 
1075                 IOUtil.copy( content, out );
1076             }
1077 
1078             inputFile.delete();
1079         }
1080         else
1081         {
1082             if ( pom.getParent() != null )
1083             {
1084                 pom.getParent().setGroupId( StringUtils.replace( pom.getParent().getGroupId(),
1085                                                                  pomReversedProperties.getProperty(
1086                                                                      Constants.GROUP_ID ),
1087                                                                  "${" + Constants.GROUP_ID + "}" ) );
1088                 if ( pom.getParent().getArtifactId() != null
1089                     && pom.getParent().getArtifactId().indexOf( rootArtifactId ) >= 0 )
1090                 {
1091                     pom.getParent().setArtifactId(
1092                         StringUtils.replace( pom.getParent().getArtifactId(), rootArtifactId, "${rootArtifactId}" ) );
1093                 }
1094                 if ( pom.getParent().getVersion() != null )
1095                 {
1096                     pom.getParent().setVersion( "${" + Constants.VERSION + "}" );
1097                 }
1098             }
1099             pom.setModules( null );
1100 
1101             if ( pom.getGroupId() != null )
1102             {
1103                 pom.setGroupId(
1104                     StringUtils.replace( pom.getGroupId(), pomReversedProperties.getProperty( Constants.GROUP_ID ),
1105                                          "${" + Constants.GROUP_ID + "}" ) );
1106             }
1107 
1108             pom.setArtifactId( "${" + Constants.ARTIFACT_ID + "}" );
1109 
1110             if ( pom.getVersion() != null )
1111             {
1112                 pom.setVersion( "${" + Constants.VERSION + "}" );
1113             }
1114 
1115             pom.setName( getReversedPlainContent( pom.getName(), pomReversedProperties ) );
1116             pom.setDescription( getReversedPlainContent( pom.getDescription(), pomReversedProperties ) );
1117             pom.setUrl( getReversedPlainContent( pom.getUrl(), pomReversedProperties ) );
1118 
1119             rewriteReferences( pom, rootArtifactId, pomReversedProperties.getProperty( Constants.GROUP_ID ) );
1120 
1121             pomManager.writePom( pom, outputFile, initialPomFile );
1122         }
1123 
1124         try ( Reader in = ReaderFactory.newXmlReader( initialPomFile ) )
1125         {
1126             String initialcontent = IOUtil.toString( in );
1127 
1128             for ( Iterator<?> properties = pomReversedProperties.keySet().iterator(); properties.hasNext(); )
1129             {
1130                 String property = (String) properties.next();
1131 
1132                 if ( initialcontent.indexOf( "${" + property + "}" ) > 0 )
1133                 {
1134                     getLogger().warn(
1135                         "OldArchetype uses ${" + property + "} for internal processing, but file " + initialPomFile
1136                             + " contains this property already" );
1137                 }
1138             }
1139         }
1140     }
1141 
1142     private Set<String> getExtensions( List<String> files )
1143     {
1144         Set<String> extensions = new HashSet<>();
1145 
1146         for ( String file : files )
1147         {
1148             extensions.add( FileUtils.extension( file ) );
1149         }
1150 
1151         return extensions;
1152     }
1153 
1154     private Map<String, List<String>> getGroupsMap( final List<String> files, final int level )
1155     {
1156         Map<String, List<String>> groups = new HashMap<>();
1157 
1158         for ( String file : files )
1159         {
1160             String directory = PathUtils.getDirectory( file, level );
1161             // make all groups have unix style
1162             directory = StringUtils.replace( directory, File.separator, "/" );
1163 
1164             if ( !groups.containsKey( directory ) )
1165             {
1166                 groups.put( directory, new ArrayList<String>() );
1167             }
1168 
1169             List<String> group = groups.get( directory );
1170 
1171             String innerPath = file.substring( directory.length() + 1 );
1172             // make all groups have unix style
1173             innerPath = StringUtils.replace( innerPath, File.separator, "/" );
1174 
1175             group.add( innerPath );
1176         }
1177 
1178         getLogger().debug( "Sorted " + groups.size() + " groups in " + files.size() + " files" );
1179         getLogger().debug( "Sorted Files: " + files );
1180 
1181         return groups;
1182     }
1183 
1184     private FileSet getPackagedFileSet( final boolean filtered, final Set<String> packagedExtensions,
1185                                         final String group, final Set<String> unpackagedExtensions,
1186                                         final List<String> unpackagedFiles, String defaultEncoding )
1187     {
1188         List<String> includes = new ArrayList<>();
1189         List<String> excludes = new ArrayList<>();
1190 
1191         for ( String extension : packagedExtensions )
1192         {
1193             includes.add( "**/*." + extension );
1194 
1195             if ( unpackagedExtensions.contains( extension ) )
1196             {
1197                 excludes.addAll( archetypeFilesResolver.getFilesWithExtension( unpackagedFiles, extension ) );
1198             }
1199         }
1200 
1201         return createFileSet( excludes, true, filtered, group, includes, defaultEncoding );
1202     }
1203 
1204     private List<FileSet> getPackagedFileSets( final boolean filtered, final String group,
1205                                                final List<String> groupFiles, final String packageName,
1206                                                String defaultEncoding )
1207     {
1208         String packageAsDir = StringUtils.replace( packageName, ".", "/" );
1209 
1210         List<FileSet> packagedFileSets = new ArrayList<>();
1211         List<String> packagedFiles = archetypeFilesResolver.getPackagedFiles( groupFiles, packageAsDir );
1212         getLogger().debug( "Found packaged Files:" + packagedFiles );
1213 
1214         List<String> unpackagedFiles = archetypeFilesResolver.getUnpackagedFiles( groupFiles, packageAsDir );
1215         getLogger().debug( "Found unpackaged Files:" + unpackagedFiles );
1216 
1217         Set<String> packagedExtensions = getExtensions( packagedFiles );
1218         getLogger().debug( "Found packaged extensions " + packagedExtensions );
1219 
1220         Set<String> unpackagedExtensions = getExtensions( unpackagedFiles );
1221 
1222         if ( !packagedExtensions.isEmpty() )
1223         {
1224             packagedFileSets.add(
1225                 getPackagedFileSet( filtered, packagedExtensions, group, unpackagedExtensions, unpackagedFiles,
1226                                     defaultEncoding ) );
1227         }
1228 
1229         if ( !unpackagedExtensions.isEmpty() )
1230         {
1231             getLogger().debug( "Found unpackaged extensions " + unpackagedExtensions );
1232 
1233             packagedFileSets.add(
1234                 getUnpackagedFileSet( filtered, unpackagedExtensions, unpackagedFiles, group, packagedExtensions,
1235                                       defaultEncoding ) );
1236         }
1237 
1238         return packagedFileSets;
1239     }
1240 
1241     private void setParentArtifactId( Properties properties, String parentArtifactId )
1242     {
1243         properties.setProperty( Constants.PARENT_ARTIFACT_ID, parentArtifactId );
1244     }
1245 
1246     private void processFileSet( File basedir, File archetypeFilesDirectory, String directory,
1247                                  List<String> fileSetResources, boolean packaged, String packageName,
1248                                  Properties reverseProperties, String defaultEncoding )
1249         throws IOException
1250     {
1251         String packageAsDirectory = StringUtils.replace( packageName, ".", File.separator );
1252 
1253         getLogger().debug( "Package as Directory: Package:" + packageName + "->" + packageAsDirectory );
1254 
1255         for ( String inputFileName : fileSetResources )
1256         {
1257             String initialFilename = packaged
1258                 ? StringUtils.replace( inputFileName, packageAsDirectory + File.separator, "" )
1259                 : inputFileName;
1260 
1261             getLogger().debug( "InputFileName:" + inputFileName );
1262 
1263             File inputFile = new File( basedir, inputFileName );
1264 
1265             FileCharsetDetector detector = new FileCharsetDetector( inputFile );
1266 
1267             String fileEncoding = detector.isFound() ? detector.getCharset() : defaultEncoding;
1268 
1269             String initialcontent = IOUtil.toString( new FileInputStream( inputFile ), fileEncoding );
1270 
1271             for ( Iterator<?> properties = reverseProperties.keySet().iterator(); properties.hasNext(); )
1272             {
1273                 String property = (String) properties.next();
1274 
1275                 if ( initialcontent.indexOf( "${" + property + "}" ) > 0 )
1276                 {
1277                     getLogger().warn(
1278                         "Archetype uses ${" + property + "} for internal processing, but file " + inputFile
1279                             + " contains this property already" );
1280                 }
1281             }
1282 
1283             String content = getReversedContent( initialcontent, reverseProperties );
1284             String outputFilename = getReversedFilename( initialFilename, reverseProperties );
1285 
1286             getLogger().debug( "OutputFileName:" + outputFilename );
1287 
1288             File outputFile = new File( archetypeFilesDirectory, outputFilename );
1289             outputFile.getParentFile().mkdirs();
1290 
1291             org.apache.commons.io.IOUtils.write( content, new FileOutputStream( outputFile ), fileEncoding );
1292         }
1293     }
1294 
1295     private Properties getReversedProperties( ArchetypeDescriptor archetypeDescriptor, Properties properties )
1296     {
1297         Properties reversedProperties = new Properties();
1298 
1299         reversedProperties.putAll( properties );
1300         reversedProperties.remove( Constants.ARCHETYPE_GROUP_ID );
1301         reversedProperties.remove( Constants.ARCHETYPE_ARTIFACT_ID );
1302         reversedProperties.remove( Constants.ARCHETYPE_VERSION );
1303 
1304         String packageName = properties.getProperty( Constants.PACKAGE );
1305         String packageInPathFormat = getPackageInPathFormat( packageName );
1306         if ( !packageInPathFormat.equals( packageName ) )
1307         {
1308             reversedProperties.setProperty( Constants.PACKAGE_IN_PATH_FORMAT, packageInPathFormat );
1309         }
1310 
1311         // TODO check that reversed properties are all different and no one is a substring of another?
1312         // to avoid wrong variable replacements
1313 
1314         return reversedProperties;
1315     }
1316 
1317     private List<String> resolveFileNames( final Model pom, final File basedir, List<String> excludePatterns )
1318         throws IOException
1319     {
1320         getLogger().debug( "Resolving files for " + pom.getId() + " in " + basedir );
1321 
1322         StringBuilder buff = new StringBuilder( "pom.xml*,archetype.properties*,target/**," );
1323         for ( String module : pom.getModules() )
1324         {
1325             buff.append( ',' ).append( module ).append( "/**" );
1326         }
1327 
1328         for ( String defaultExclude : ListScanner.DEFAULTEXCLUDES )
1329         {
1330             buff.append( ',' ).append( defaultExclude ).append( "/**" );
1331         }
1332 
1333         for ( String excludePattern : excludePatterns )
1334         {
1335             buff.append( ',' ).append( excludePattern );
1336         }
1337 
1338         String excludes = PathUtils.convertPathForOS( buff.toString() );
1339 
1340         List<String> fileNames = FileUtils.getFileNames( basedir, "**,.*,**/.*", excludes, false );
1341 
1342         getLogger().debug( "Resolved " + fileNames.size() + " files" );
1343         getLogger().debug( "Resolved Files:" + fileNames );
1344 
1345         return fileNames;
1346     }
1347 
1348     private List<FileSet> resolveFileSets( String packageName, List<String> fileNames, List<String> languages,
1349                                            List<String> filtereds, String defaultEncoding )
1350     {
1351         List<FileSet> resolvedFileSets = new ArrayList<>();
1352         getLogger().debug(
1353             "Resolving filesets with package=" + packageName + ", languages=" + languages + " and extentions="
1354                 + filtereds );
1355 
1356         List<String> files = new ArrayList<>( fileNames );
1357 
1358         StringBuilder languageIncludes = new StringBuilder();
1359 
1360         for ( String language : languages )
1361         {
1362             languageIncludes.append( ( ( languageIncludes.length() == 0 ) ? "" : "," ) + language + "/**" );
1363         }
1364 
1365         getLogger().debug( "Using languages includes " + languageIncludes );
1366 
1367         StringBuilder filteredIncludes = new StringBuilder();
1368         for ( String filtered : filtereds )
1369         {
1370             filteredIncludes.append(
1371                 ( ( filteredIncludes.length() == 0 ) ? "" : "," ) + "**/" + ( filtered.startsWith( "." ) ? "" : "*." )
1372                     + filtered );
1373         }
1374 
1375         getLogger().debug( "Using filtered includes " + filteredIncludes );
1376 
1377         /* sourcesMainFiles */
1378         List<String> sourcesMainFiles =
1379             archetypeFilesResolver.findSourcesMainFiles( files, languageIncludes.toString() );
1380         if ( !sourcesMainFiles.isEmpty() )
1381         {
1382             files.removeAll( sourcesMainFiles );
1383 
1384             List<String> filteredFiles =
1385                 archetypeFilesResolver.getFilteredFiles( sourcesMainFiles, filteredIncludes.toString() );
1386             sourcesMainFiles.removeAll( filteredFiles );
1387 
1388             List<String> unfilteredFiles = sourcesMainFiles;
1389             if ( !filteredFiles.isEmpty() )
1390             {
1391                 resolvedFileSets.addAll( createFileSets( filteredFiles, 3, true, packageName, true, defaultEncoding ) );
1392             }
1393 
1394             if ( !unfilteredFiles.isEmpty() )
1395             {
1396                 resolvedFileSets.addAll(
1397                     createFileSets( unfilteredFiles, 3, true, packageName, false, defaultEncoding ) );
1398             }
1399         }
1400 
1401         /* resourcesMainFiles */
1402         List<String> resourcesMainFiles =
1403             archetypeFilesResolver.findResourcesMainFiles( files, languageIncludes.toString() );
1404         if ( !resourcesMainFiles.isEmpty() )
1405         {
1406             files.removeAll( resourcesMainFiles );
1407 
1408             List<String> filteredFiles =
1409                 archetypeFilesResolver.getFilteredFiles( resourcesMainFiles, filteredIncludes.toString() );
1410             resourcesMainFiles.removeAll( filteredFiles );
1411 
1412             List<String> unfilteredFiles = resourcesMainFiles;
1413             if ( !filteredFiles.isEmpty() )
1414             {
1415                 resolvedFileSets.addAll(
1416                     createFileSets( filteredFiles, 3, false, packageName, true, defaultEncoding ) );
1417             }
1418             if ( !unfilteredFiles.isEmpty() )
1419             {
1420                 resolvedFileSets.addAll(
1421                     createFileSets( unfilteredFiles, 3, false, packageName, false, defaultEncoding ) );
1422             }
1423         }
1424 
1425         /* sourcesTestFiles */
1426         List<String> sourcesTestFiles =
1427             archetypeFilesResolver.findSourcesTestFiles( files, languageIncludes.toString() );
1428         if ( !sourcesTestFiles.isEmpty() )
1429         {
1430             files.removeAll( sourcesTestFiles );
1431 
1432             List<String> filteredFiles =
1433                 archetypeFilesResolver.getFilteredFiles( sourcesTestFiles, filteredIncludes.toString() );
1434             sourcesTestFiles.removeAll( filteredFiles );
1435 
1436             List<String> unfilteredFiles = sourcesTestFiles;
1437             if ( !filteredFiles.isEmpty() )
1438             {
1439                 resolvedFileSets.addAll( createFileSets( filteredFiles, 3, true, packageName, true, defaultEncoding ) );
1440             }
1441             if ( !unfilteredFiles.isEmpty() )
1442             {
1443                 resolvedFileSets.addAll(
1444                     createFileSets( unfilteredFiles, 3, true, packageName, false, defaultEncoding ) );
1445             }
1446         }
1447 
1448         /* ressourcesTestFiles */
1449         List<String> resourcesTestFiles =
1450             archetypeFilesResolver.findResourcesTestFiles( files, languageIncludes.toString() );
1451         if ( !resourcesTestFiles.isEmpty() )
1452         {
1453             files.removeAll( resourcesTestFiles );
1454 
1455             List<String> filteredFiles =
1456                 archetypeFilesResolver.getFilteredFiles( resourcesTestFiles, filteredIncludes.toString() );
1457             resourcesTestFiles.removeAll( filteredFiles );
1458 
1459             List<String> unfilteredFiles = resourcesTestFiles;
1460             if ( !filteredFiles.isEmpty() )
1461             {
1462                 resolvedFileSets.addAll(
1463                     createFileSets( filteredFiles, 3, false, packageName, true, defaultEncoding ) );
1464             }
1465             if ( !unfilteredFiles.isEmpty() )
1466             {
1467                 resolvedFileSets.addAll(
1468                     createFileSets( unfilteredFiles, 3, false, packageName, false, defaultEncoding ) );
1469             }
1470         }
1471 
1472         /* siteFiles */
1473         List<String> siteFiles = archetypeFilesResolver.findSiteFiles( files, languageIncludes.toString() );
1474         if ( !siteFiles.isEmpty() )
1475         {
1476             files.removeAll( siteFiles );
1477 
1478             List<String> filteredFiles =
1479                 archetypeFilesResolver.getFilteredFiles( siteFiles, filteredIncludes.toString() );
1480             siteFiles.removeAll( filteredFiles );
1481 
1482             List<String> unfilteredFiles = siteFiles;
1483             if ( !filteredFiles.isEmpty() )
1484             {
1485                 resolvedFileSets.addAll(
1486                     createFileSets( filteredFiles, 2, false, packageName, true, defaultEncoding ) );
1487             }
1488             if ( !unfilteredFiles.isEmpty() )
1489             {
1490                 resolvedFileSets.addAll(
1491                     createFileSets( unfilteredFiles, 2, false, packageName, false, defaultEncoding ) );
1492             }
1493         }
1494 
1495         /* thirdLevelSourcesfiles */
1496         List<String> thirdLevelSourcesfiles =
1497             archetypeFilesResolver.findOtherSources( 3, files, languageIncludes.toString() );
1498         if ( !thirdLevelSourcesfiles.isEmpty() )
1499         {
1500             files.removeAll( thirdLevelSourcesfiles );
1501 
1502             List<String> filteredFiles =
1503                 archetypeFilesResolver.getFilteredFiles( thirdLevelSourcesfiles, filteredIncludes.toString() );
1504             thirdLevelSourcesfiles.removeAll( filteredFiles );
1505 
1506             List<String> unfilteredFiles = thirdLevelSourcesfiles;
1507             if ( !filteredFiles.isEmpty() )
1508             {
1509                 resolvedFileSets.addAll( createFileSets( filteredFiles, 3, true, packageName, true, defaultEncoding ) );
1510             }
1511             if ( !unfilteredFiles.isEmpty() )
1512             {
1513                 resolvedFileSets.addAll(
1514                     createFileSets( unfilteredFiles, 3, true, packageName, false, defaultEncoding ) );
1515             }
1516 
1517             /* thirdLevelResourcesfiles */
1518             List<String> thirdLevelResourcesfiles =
1519                 archetypeFilesResolver.findOtherResources( 3, files, thirdLevelSourcesfiles,
1520                                                            languageIncludes.toString() );
1521             if ( !thirdLevelResourcesfiles.isEmpty() )
1522             {
1523                 files.removeAll( thirdLevelResourcesfiles );
1524                 filteredFiles =
1525                     archetypeFilesResolver.getFilteredFiles( thirdLevelResourcesfiles, filteredIncludes.toString() );
1526                 thirdLevelResourcesfiles.removeAll( filteredFiles );
1527                 unfilteredFiles = thirdLevelResourcesfiles;
1528                 if ( !filteredFiles.isEmpty() )
1529                 {
1530                     resolvedFileSets.addAll(
1531                         createFileSets( filteredFiles, 3, false, packageName, true, defaultEncoding ) );
1532                 }
1533                 if ( !unfilteredFiles.isEmpty() )
1534                 {
1535                     resolvedFileSets.addAll(
1536                         createFileSets( unfilteredFiles, 3, false, packageName, false, defaultEncoding ) );
1537                 }
1538             }
1539         } // end if
1540 
1541         /* secondLevelSourcesfiles */
1542         List<String> secondLevelSourcesfiles =
1543             archetypeFilesResolver.findOtherSources( 2, files, languageIncludes.toString() );
1544         if ( !secondLevelSourcesfiles.isEmpty() )
1545         {
1546             files.removeAll( secondLevelSourcesfiles );
1547 
1548             List<String> filteredFiles =
1549                 archetypeFilesResolver.getFilteredFiles( secondLevelSourcesfiles, filteredIncludes.toString() );
1550             secondLevelSourcesfiles.removeAll( filteredFiles );
1551 
1552             List<String> unfilteredFiles = secondLevelSourcesfiles;
1553             if ( !filteredFiles.isEmpty() )
1554             {
1555                 resolvedFileSets.addAll( createFileSets( filteredFiles, 2, true, packageName, true, defaultEncoding ) );
1556             }
1557             if ( !unfilteredFiles.isEmpty() )
1558             {
1559                 resolvedFileSets.addAll(
1560                     createFileSets( unfilteredFiles, 2, true, packageName, false, defaultEncoding ) );
1561             }
1562         }
1563 
1564         /* secondLevelResourcesfiles */
1565         List<String> secondLevelResourcesfiles =
1566             archetypeFilesResolver.findOtherResources( 2, files, languageIncludes.toString() );
1567         if ( !secondLevelResourcesfiles.isEmpty() )
1568         {
1569             files.removeAll( secondLevelResourcesfiles );
1570 
1571             List<String> filteredFiles =
1572                 archetypeFilesResolver.getFilteredFiles( secondLevelResourcesfiles, filteredIncludes.toString() );
1573             secondLevelResourcesfiles.removeAll( filteredFiles );
1574 
1575             List<String> unfilteredFiles = secondLevelResourcesfiles;
1576             if ( !filteredFiles.isEmpty() )
1577             {
1578                 resolvedFileSets.addAll(
1579                     createFileSets( filteredFiles, 2, false, packageName, true, defaultEncoding ) );
1580             }
1581             if ( !unfilteredFiles.isEmpty() )
1582             {
1583                 resolvedFileSets.addAll(
1584                     createFileSets( unfilteredFiles, 2, false, packageName, false, defaultEncoding ) );
1585             }
1586         }
1587 
1588         /* rootResourcesfiles */
1589         List<String> rootResourcesfiles =
1590             archetypeFilesResolver.findOtherResources( 0, files, languageIncludes.toString() );
1591         if ( !rootResourcesfiles.isEmpty() )
1592         {
1593             files.removeAll( rootResourcesfiles );
1594 
1595             List<String> filteredFiles =
1596                 archetypeFilesResolver.getFilteredFiles( rootResourcesfiles, filteredIncludes.toString() );
1597             rootResourcesfiles.removeAll( filteredFiles );
1598 
1599             List<String> unfilteredFiles = rootResourcesfiles;
1600             if ( !filteredFiles.isEmpty() )
1601             {
1602                 resolvedFileSets.addAll(
1603                     createFileSets( filteredFiles, 0, false, packageName, true, defaultEncoding ) );
1604             }
1605             if ( !unfilteredFiles.isEmpty() )
1606             {
1607                 resolvedFileSets.addAll(
1608                     createFileSets( unfilteredFiles, 0, false, packageName, false, defaultEncoding ) );
1609             }
1610         }
1611 
1612         /**/
1613         if ( !files.isEmpty() )
1614         {
1615             getLogger().info( "Ignored files: " + files );
1616         }
1617 
1618         return resolvedFileSets;
1619     }
1620 
1621     private void restoreArtifactId( Properties properties, String artifactId )
1622     {
1623         if ( StringUtils.isEmpty( artifactId ) )
1624         {
1625             properties.remove( Constants.ARTIFACT_ID );
1626         }
1627         else
1628         {
1629             properties.setProperty( Constants.ARTIFACT_ID, artifactId );
1630         }
1631     }
1632 
1633     private void restoreParentArtifactId( Properties properties, String parentArtifactId )
1634     {
1635         if ( StringUtils.isEmpty( parentArtifactId ) )
1636         {
1637             properties.remove( Constants.PARENT_ARTIFACT_ID );
1638         }
1639         else
1640         {
1641             properties.setProperty( Constants.PARENT_ARTIFACT_ID, parentArtifactId );
1642         }
1643     }
1644 
1645     private String getReversedContent( String content, Properties properties )
1646     {
1647         String result =
1648             StringUtils.replace( StringUtils.replace( content, "$", "${symbol_dollar}" ), "\\", "${symbol_escape}" );
1649         result = getReversedPlainContent( result, properties );
1650 
1651         // TODO: Replace velocity to a better engine...
1652         return "#set( $symbol_pound = '#' )\n" + "#set( $symbol_dollar = '$' )\n" + "#set( $symbol_escape = '\\' )\n"
1653             + StringUtils.replace( result, "#", "${symbol_pound}" );
1654     }
1655 
1656     private String getReversedPlainContent( String content, Properties properties )
1657     {
1658         String result = content;
1659 
1660         for ( Iterator<?> propertyIterator = properties.keySet().iterator(); propertyIterator.hasNext(); )
1661         {
1662             String propertyKey = (String) propertyIterator.next();
1663 
1664             result = StringUtils.replace( result, properties.getProperty( propertyKey ), "${" + propertyKey + "}" );
1665         }
1666         return result;
1667     }
1668 
1669     private String getReversedFilename( String filename, Properties properties )
1670     {
1671         String result = filename;
1672 
1673         for ( Iterator<?> propertyIterator = properties.keySet().iterator(); propertyIterator.hasNext(); )
1674         {
1675             String propertyKey = (String) propertyIterator.next();
1676 
1677             result = StringUtils.replace( result, properties.getProperty( propertyKey ), "__" + propertyKey + "__" );
1678         }
1679 
1680         return result;
1681     }
1682 
1683     private String getTemplateOutputDirectory()
1684     {
1685         return Constants.SRC + File.separator + Constants.MAIN + File.separator + Constants.RESOURCES;
1686     }
1687 
1688     private FileSet getUnpackagedFileSet( final boolean filtered, final String group, final List<String> groupFiles,
1689                                           String defaultEncoding )
1690     {
1691         Set<String> extensions = getExtensions( groupFiles );
1692 
1693         List<String> includes = new ArrayList<>();
1694         List<String> excludes = new ArrayList<>();
1695 
1696         for ( String extension : extensions )
1697         {
1698             includes.add( "**/*." + extension );
1699         }
1700 
1701         return createFileSet( excludes, false, filtered, group, includes, defaultEncoding );
1702     }
1703 
1704     private FileSet getUnpackagedFileSet( final boolean filtered, final Set<String> unpackagedExtensions,
1705                                           final List<String> unpackagedFiles, final String group,
1706                                           final Set<String> packagedExtensions, String defaultEncoding )
1707     {
1708         List<String> includes = new ArrayList<>();
1709         List<String> excludes = new ArrayList<>();
1710 
1711         for ( String extension : unpackagedExtensions )
1712         {
1713             if ( packagedExtensions.contains( extension ) )
1714             {
1715                 includes.addAll( archetypeFilesResolver.getFilesWithExtension( unpackagedFiles, extension ) );
1716             }
1717             else
1718             {
1719                 if ( StringUtils.isEmpty( extension ) )
1720                 {
1721                     includes.add( "**/*" );
1722                 }
1723                 else
1724                 {
1725                     includes.add( "**/*." + extension );
1726                 }
1727             }
1728         }
1729 
1730         return createFileSet( excludes, false, filtered, group, includes, defaultEncoding );
1731     }
1732 
1733     private static final String MAVEN_PROPERTIES =
1734         "META-INF/maven/org.apache.maven.archetype/archetype-common/pom.properties";
1735 
1736     public String getArchetypeVersion()
1737     {
1738         // This should actually come from the pom.properties at testing but it's not generated and put into the JAR, it
1739         // happens as part of the JAR plugin which is crap as it makes testing inconsistent.
1740         String version = "version";
1741 
1742         try ( InputStream is = getClass().getClassLoader().getResourceAsStream( MAVEN_PROPERTIES ) )
1743         {
1744             Properties properties = new Properties();
1745 
1746             if ( is != null )
1747             {
1748                 properties.load( is );
1749 
1750                 String property = properties.getProperty( "version" );
1751 
1752                 if ( property != null )
1753                 {
1754                     return property;
1755                 }
1756             }
1757 
1758             return version;
1759         }
1760         catch ( IOException e )
1761         {
1762             return version;
1763         }
1764     }
1765 }