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