/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.maven.archetype.creator;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.maven.archetype.ArchetypeCreationRequest;
import org.apache.maven.archetype.ArchetypeCreationResult;
import org.apache.maven.archetype.common.ArchetypeFilesResolver;
import org.apache.maven.archetype.common.Constants;
import org.apache.maven.archetype.common.PomManager;
import org.apache.maven.archetype.common.util.ListScanner;
import org.apache.maven.archetype.common.util.PathUtils;
import org.apache.maven.archetype.metadata.ArchetypeDescriptor;
import org.apache.maven.archetype.metadata.FileSet;
import org.apache.maven.archetype.metadata.ModuleDescriptor;
import org.apache.maven.archetype.metadata.RequiredProperty;
import org.apache.maven.archetype.metadata.io.xpp3.ArchetypeDescriptorXpp3Writer;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Extension;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Profile;
import org.apache.maven.model.Resource;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.Invoker;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.XmlStreamReader;
import org.codehaus.plexus.util.xml.XmlStreamWriter;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.commons.io.IOUtils.write;

/**
 * Create a 2.x Archetype project from a project. Since 2.0-alpha-5, an integration-test named "basic" is created along
 * the archetype itself to provide immediate test when building the archetype.
 */
@Named("fileset")
@Singleton
public class FilesetArchetypeCreator implements ArchetypeCreator {
    private static final Logger LOGGER = LoggerFactory.getLogger(FilesetArchetypeCreator.class);
    private static final String DEFAULT_OUTPUT_DIRECTORY =
            "target" + File.separator + "generated-sources" + File.separator + "archetype";

    private ArchetypeFilesResolver archetypeFilesResolver;

    private PomManager pomManager;

    private Invoker invoker;

    @Inject
    public FilesetArchetypeCreator(
            ArchetypeFilesResolver archetypeFilesResolver, PomManager pomManager, Invoker invoker) {
        this.archetypeFilesResolver = archetypeFilesResolver;
        this.pomManager = pomManager;
        this.invoker = invoker;
    }

    @Override
    @SuppressWarnings("checkstyle:MethodLength")
    public void createArchetype(ArchetypeCreationRequest request, ArchetypeCreationResult result) {
        MavenProject project = request.getProject();
        List<String> languages = request.getLanguages();
        List<String> filtereds = request.getFiltereds();
        String defaultEncoding = request.getDefaultEncoding();
        boolean preserveCData = request.isPreserveCData();
        boolean keepParent = request.isKeepParent();
        boolean partialArchetype = request.isPartialArchetype();
        File outputDirectory = request.getOutputDirectory();
        File basedir = project.getBasedir();

        Properties properties = new Properties();
        Properties configurationProperties = new Properties();
        if (request.getProperties() != null) {
            properties.putAll(request.getProperties());
            configurationProperties.putAll(request.getProperties());
        }

        extractPropertiesFromProject(project, properties, configurationProperties, request.getPackageName());

        if (outputDirectory == null) {
            LOGGER.debug("No output directory defined, using default: " + DEFAULT_OUTPUT_DIRECTORY);
            outputDirectory = FileUtils.resolveFile(basedir, DEFAULT_OUTPUT_DIRECTORY);
        }
        outputDirectory.mkdirs();

        LOGGER.debug("Creating archetype in " + outputDirectory);

        try {
            File archetypePomFile = createArchetypeProjectPom(project, configurationProperties, outputDirectory);

            File archetypeResourcesDirectory = new File(outputDirectory, getTemplateOutputDirectory());

            File archetypeFilesDirectory = new File(archetypeResourcesDirectory, Constants.ARCHETYPE_RESOURCES);
            LOGGER.debug("Archetype's files output directory " + archetypeFilesDirectory);

            File archetypeDescriptorFile = new File(archetypeResourcesDirectory, Constants.ARCHETYPE_DESCRIPTOR);
            archetypeDescriptorFile.getParentFile().mkdirs();

            File archetypePostGenerationScript =
                    new File(archetypeResourcesDirectory, Constants.ARCHETYPE_POST_GENERATION_SCRIPT);
            archetypePostGenerationScript.getParentFile().mkdirs();

            if (request.getProject().getBuild() != null
                    && CollectionUtils.isNotEmpty(
                            request.getProject().getBuild().getResources())) {
                for (Resource resource : request.getProject().getBuild().getResources()) {
                    File inputFile = new File(
                            resource.getDirectory() + File.separator + Constants.ARCHETYPE_POST_GENERATION_SCRIPT);
                    if (inputFile.exists()) {
                        FileUtils.copyFile(inputFile, archetypePostGenerationScript);
                    }
                }
            }

            LOGGER.debug("Starting archetype's descriptor " + project.getArtifactId());
            ArchetypeDescriptor archetypeDescriptor = new ArchetypeDescriptor();

            archetypeDescriptor.setName(project.getArtifactId());
            archetypeDescriptor.setPartial(partialArchetype);

            addRequiredProperties(archetypeDescriptor, properties);

            // TODO ensure reverseProperties contains NO dotted properties
            Properties reverseProperties = getReversedProperties(archetypeDescriptor, properties);
            // reverseProperties.remove( Constants.GROUP_ID );

            // TODO ensure pomReversedProperties contains NO dotted properties
            Properties pomReversedProperties = getReversedProperties(archetypeDescriptor, properties);
            // pomReversedProperties.remove( Constants.PACKAGE );

            String packageName = configurationProperties.getProperty(Constants.PACKAGE);

            Model pom = pomManager.readPom(project.getFile());

            List<String> excludePatterns = configurationProperties.getProperty(Constants.EXCLUDE_PATTERNS) != null
                    ? Arrays.asList(configurationProperties
                            .getProperty(Constants.EXCLUDE_PATTERNS)
                            .split(","))
                    : Collections.emptyList();

            List<String> fileNames = resolveFileNames(pom, basedir, excludePatterns);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Scanned for files " + fileNames.size());

                for (String name : fileNames) {
                    LOGGER.debug("- " + name);
                }
            }

            List<FileSet> filesets = resolveFileSets(packageName, fileNames, languages, filtereds, defaultEncoding);
            LOGGER.debug("Resolved filesets for " + archetypeDescriptor.getName());

            archetypeDescriptor.setFileSets(filesets);

            createArchetypeFiles(
                    reverseProperties,
                    filesets,
                    packageName,
                    basedir,
                    archetypeFilesDirectory,
                    defaultEncoding,
                    excludePatterns);
            LOGGER.debug("Created files for " + archetypeDescriptor.getName());

            setParentArtifactId(reverseProperties, configurationProperties.getProperty(Constants.ARTIFACT_ID));

            for (String moduleId : pom.getModules()) {
                String rootArtifactId = configurationProperties.getProperty(Constants.ARTIFACT_ID);
                String moduleIdDirectory = moduleId;

                if (moduleId.indexOf(rootArtifactId) >= 0) {
                    moduleIdDirectory = StringUtils.replace(moduleId, rootArtifactId, "__rootArtifactId__");
                }

                LOGGER.debug("Creating module " + moduleId);

                ModuleDescriptor moduleDescriptor = createModule(
                        reverseProperties,
                        rootArtifactId,
                        moduleId,
                        packageName,
                        FileUtils.resolveFile(basedir, moduleId),
                        new File(archetypeFilesDirectory, moduleIdDirectory),
                        languages,
                        filtereds,
                        defaultEncoding,
                        preserveCData,
                        keepParent);

                archetypeDescriptor.addModule(moduleDescriptor);

                LOGGER.debug("Added module " + moduleDescriptor.getName() + " in " + archetypeDescriptor.getName());
            }

            restoreParentArtifactId(reverseProperties, null);
            restoreArtifactId(reverseProperties, configurationProperties.getProperty(Constants.ARTIFACT_ID));

            createPoms(
                    pom,
                    configurationProperties.getProperty(Constants.ARTIFACT_ID),
                    configurationProperties.getProperty(Constants.ARTIFACT_ID),
                    archetypeFilesDirectory,
                    basedir,
                    project.getFile(),
                    pomReversedProperties,
                    preserveCData,
                    keepParent);
            LOGGER.debug("Created Archetype " + archetypeDescriptor.getName() + " template pom(s)");

            try (Writer out = new XmlStreamWriter(archetypeDescriptorFile)) {
                ArchetypeDescriptorXpp3Writer writer = new ArchetypeDescriptorXpp3Writer();

                writer.write(out, archetypeDescriptor);

                LOGGER.debug("Archetype " + archetypeDescriptor.getName() + " descriptor written");
            }

            createArchetypeBasicIt(archetypeDescriptor, outputDirectory);

            // Copy archetype integration tests.
            File archetypeIntegrationTestInputFolder =
                    new File(basedir, Constants.SRC + File.separator + "it" + File.separator + "projects");
            File archetypeIntegrationTestOutputFolder = new File(
                    outputDirectory,
                    Constants.SRC + File.separator + Constants.TEST
                            + File.separator + Constants.RESOURCES
                            + File.separator + "projects");

            if (archetypeIntegrationTestInputFolder.exists()) {
                LOGGER.info("Copying: " + archetypeIntegrationTestInputFolder.getAbsolutePath() + " into "
                        + archetypeIntegrationTestOutputFolder.getAbsolutePath());

                FileUtils.copyDirectoryStructure(
                        archetypeIntegrationTestInputFolder, archetypeIntegrationTestOutputFolder);
            }
            InvocationRequest internalRequest = new DefaultInvocationRequest();
            internalRequest.setPomFile(archetypePomFile);
            internalRequest.setUserSettingsFile(request.getSettingsFile());
            internalRequest.addArg(request.getPostPhase());
            internalRequest.setLocalRepositoryDirectory(request.getLocalRepositoryBasedir());

            String httpsProtocols = System.getProperty("https.protocols");
            if (httpsProtocols != null) {
                Properties userProperties = new Properties();
                userProperties.setProperty("https.protocols", httpsProtocols);
                internalRequest.setProperties(userProperties);
            }

            InvocationResult invokerResult = invoker.execute(internalRequest);
            if (invokerResult.getExitCode() != 0) {
                if (invokerResult.getExecutionException() != null) {
                    throw invokerResult.getExecutionException();
                } else {
                    throw new Exception("Invoker process ended with result different than 0!");
                }
            }

        } catch (Exception e) {
            result.setCause(e);
        }
    }

    /**
     * Create an archetype IT, ie goals.txt and archetype.properties in src/test/resources/projects/basic.
     *
     * @param archetypeDescriptor
     * @param generatedSourcesDirectory
     * @throws IOException
     * @since 2.0-alpha-5
     */
    private void createArchetypeBasicIt(ArchetypeDescriptor archetypeDescriptor, File generatedSourcesDirectory)
            throws IOException {
        String basic = Constants.SRC + File.separator + Constants.TEST + File.separator + Constants.RESOURCES
                + File.separator + "projects" + File.separator + "basic";
        File basicItDirectory = new File(generatedSourcesDirectory, basic);
        basicItDirectory.mkdirs();

        File archetypePropertiesFile = new File(basicItDirectory, "archetype.properties");
        if (!archetypePropertiesFile.exists() && !archetypePropertiesFile.createNewFile()) {
            LOGGER.warn("Could not create new file \"" + archetypePropertiesFile.getPath()
                    + "\" or the file already exists.");
        }

        try (InputStream in = FilesetArchetypeCreator.class.getResourceAsStream("archetype.properties");
                OutputStream out = Files.newOutputStream(archetypePropertiesFile.toPath())) {
            Properties archetypeProperties = new Properties();
            archetypeProperties.load(in);

            for (RequiredProperty req : archetypeDescriptor.getRequiredProperties()) {
                archetypeProperties.put(req.getKey(), req.getDefaultValue());
            }

            archetypeProperties.store(out, null);
        }

        copyResource("goal.txt", new File(basicItDirectory, "goal.txt"));

        LOGGER.debug("Added basic integration test");
    }

    private void extractPropertiesFromProject(
            MavenProject project, Properties properties, Properties configurationProperties, String packageName) {
        if (!properties.containsKey(Constants.GROUP_ID)) {
            properties.setProperty(Constants.GROUP_ID, project.getGroupId());
        }
        configurationProperties.setProperty(Constants.GROUP_ID, properties.getProperty(Constants.GROUP_ID));

        if (!properties.containsKey(Constants.ARTIFACT_ID)) {
            properties.setProperty(Constants.ARTIFACT_ID, project.getArtifactId());
        }
        configurationProperties.setProperty(Constants.ARTIFACT_ID, properties.getProperty(Constants.ARTIFACT_ID));

        if (!properties.containsKey(Constants.VERSION)) {
            properties.setProperty(Constants.VERSION, project.getVersion());
        }
        configurationProperties.setProperty(Constants.VERSION, properties.getProperty(Constants.VERSION));

        if (packageName != null) {
            properties.setProperty(Constants.PACKAGE, packageName);
        } else if (!properties.containsKey(Constants.PACKAGE)) {
            properties.setProperty(Constants.PACKAGE, project.getGroupId());
        }
        configurationProperties.setProperty(Constants.PACKAGE, properties.getProperty(Constants.PACKAGE));
    }

    /**
     * Create the archetype project pom.xml file, that will be used to build the archetype.
     */
    private File createArchetypeProjectPom(MavenProject project, Properties configurationProperties, File projectDir)
            throws IOException {
        Model model = new Model();
        model.setModelVersion("4.0.0");
        // these values should be retrieved from the request with sensible defaults
        model.setGroupId(configurationProperties.getProperty(Constants.ARCHETYPE_GROUP_ID, project.getGroupId()));
        model.setArtifactId(
                configurationProperties.getProperty(Constants.ARCHETYPE_ARTIFACT_ID, project.getArtifactId()));
        model.setVersion(configurationProperties.getProperty(Constants.ARCHETYPE_VERSION, project.getVersion()));
        model.setPackaging("maven-archetype");
        model.setName(configurationProperties.getProperty(Constants.ARCHETYPE_ARTIFACT_ID, project.getArtifactId()));
        model.setUrl(configurationProperties.getProperty(Constants.ARCHETYPE_URL, project.getUrl()));
        model.setDescription(
                configurationProperties.getProperty(Constants.ARCHETYPE_DESCRIPTION, project.getDescription()));
        model.setLicenses(project.getLicenses());
        model.setDevelopers(project.getDevelopers());
        model.setScm(project.getScm());
        Build build = new Build();
        model.setBuild(build);

        // In many cases where we are behind a firewall making Archetypes for work mates we want
        // to simply be able to deploy the archetypes once we have created them. In order to do
        // this we want to utilize information from the project we are creating the archetype from.
        // This will be a fully working project that has been testing and inherits from a POM
        // that contains deployment information, along with any extensions required for deployment.
        // We don't want to create archetypes that cannot be deployed after we create them. People
        // might want to edit the archetype POM but they should not have too.

        if (project.getParent() != null) {
            MavenProject p = project.getParent();

            if (p.getDistributionManagement() != null) {
                model.setDistributionManagement(p.getDistributionManagement());
            }

            if (p.getBuildExtensions() != null) {
                for (Extension be : p.getBuildExtensions()) {
                    model.getBuild().addExtension(be);
                }
            }
        }

        Extension extension = new Extension();
        extension.setGroupId("org.apache.maven.archetype");
        extension.setArtifactId("archetype-packaging");
        extension.setVersion(getArchetypeVersion());
        model.getBuild().addExtension(extension);

        LOGGER.debug("Creating archetype's pom");

        File archetypePomFile = new File(projectDir, Constants.ARCHETYPE_POM);

        archetypePomFile.getParentFile().mkdirs();

        copyResource("pom-prototype.xml", archetypePomFile);

        pomManager.writePom(model, archetypePomFile, archetypePomFile);

        return archetypePomFile;
    }

    private void copyResource(String name, File destination) throws IOException {
        if (!destination.exists() && !destination.createNewFile()) {
            LOGGER.warn("Could not create new file \"" + destination.getPath() + "\" or the file already exists.");
        }

        try (InputStream in = FilesetArchetypeCreator.class.getResourceAsStream(name);
                OutputStream out = Files.newOutputStream(destination.toPath())) {
            IOUtil.copy(in, out);
        }
    }

    private void addRequiredProperties(ArchetypeDescriptor archetypeDescriptor, Properties properties) {
        Properties requiredProperties = new Properties();
        requiredProperties.putAll(properties);
        requiredProperties.remove(Constants.ARCHETYPE_GROUP_ID);
        requiredProperties.remove(Constants.ARCHETYPE_ARTIFACT_ID);
        requiredProperties.remove(Constants.ARCHETYPE_VERSION);
        requiredProperties.remove(Constants.GROUP_ID);
        requiredProperties.remove(Constants.ARTIFACT_ID);
        requiredProperties.remove(Constants.VERSION);
        requiredProperties.remove(Constants.PACKAGE);
        requiredProperties.remove(Constants.EXCLUDE_PATTERNS);

        for (Iterator<?> propertiesIterator = requiredProperties.keySet().iterator(); propertiesIterator.hasNext(); ) {
            String propertyKey = (String) propertiesIterator.next();

            RequiredProperty requiredProperty = new RequiredProperty();
            requiredProperty.setKey(propertyKey);
            requiredProperty.setDefaultValue(requiredProperties.getProperty(propertyKey));

            archetypeDescriptor.addRequiredProperty(requiredProperty);

            LOGGER.debug("Adding requiredProperty " + propertyKey + "=" + requiredProperties.getProperty(propertyKey)
                    + " to archetype's descriptor");
        }
    }

    private void createModulePoms(
            Properties pomReversedProperties,
            String rootArtifactId,
            String packageName,
            File basedir,
            File archetypeFilesDirectory,
            boolean preserveCData,
            boolean keepParent)
            throws IOException, XmlPullParserException {
        Model pom = pomManager.readPom(FileUtils.resolveFile(basedir, Constants.ARCHETYPE_POM));

        String parentArtifactId = pomReversedProperties.getProperty(Constants.PARENT_ARTIFACT_ID);
        String artifactId = pom.getArtifactId();
        setParentArtifactId(pomReversedProperties, pomReversedProperties.getProperty(Constants.ARTIFACT_ID));
        setArtifactId(pomReversedProperties, pom.getArtifactId());

        for (String subModuleId : pom.getModules()) {
            String subModuleIdDirectory = subModuleId;

            if (subModuleId.indexOf(rootArtifactId) >= 0) {
                subModuleIdDirectory = StringUtils.replace(subModuleId, rootArtifactId, "__rootArtifactId__");
            }

            createModulePoms(
                    pomReversedProperties,
                    rootArtifactId,
                    packageName,
                    FileUtils.resolveFile(basedir, subModuleId),
                    FileUtils.resolveFile(archetypeFilesDirectory, subModuleIdDirectory),
                    preserveCData,
                    keepParent);
        }

        createModulePom(
                pom,
                rootArtifactId,
                archetypeFilesDirectory,
                pomReversedProperties,
                FileUtils.resolveFile(basedir, Constants.ARCHETYPE_POM),
                preserveCData,
                keepParent);

        restoreParentArtifactId(pomReversedProperties, parentArtifactId);
        restoreArtifactId(pomReversedProperties, artifactId);
    }

    @SuppressWarnings("checkstyle:ParameterNumber")
    private void createPoms(
            Model pom,
            String rootArtifactId,
            String artifactId,
            File archetypeFilesDirectory,
            File basedir,
            File rootPom,
            Properties pomReversedProperties,
            boolean preserveCData,
            boolean keepParent)
            throws IOException, XmlPullParserException {
        setArtifactId(pomReversedProperties, pom.getArtifactId());

        for (String moduleId : pom.getModules()) {
            String moduleIdDirectory = moduleId;

            if (moduleId.indexOf(rootArtifactId) >= 0) {
                moduleIdDirectory = StringUtils.replace(moduleId, rootArtifactId, "__rootArtifactId__");
            }

            createModulePoms(
                    pomReversedProperties,
                    rootArtifactId,
                    moduleId,
                    FileUtils.resolveFile(basedir, moduleId),
                    new File(archetypeFilesDirectory, moduleIdDirectory),
                    preserveCData,
                    keepParent);
        }

        restoreParentArtifactId(pomReversedProperties, null);
        restoreArtifactId(pomReversedProperties, artifactId);

        createArchetypePom(pom, archetypeFilesDirectory, pomReversedProperties, rootPom, preserveCData, keepParent);
    }

    private String getPackageInPathFormat(String aPackage) {
        return StringUtils.replace(aPackage, ".", "/");
    }

    private void rewriteReferences(Model pom, String rootArtifactId, String groupId) {
        // rewrite Dependencies
        if (pom.getDependencies() != null && !pom.getDependencies().isEmpty()) {
            for (Dependency dependency : pom.getDependencies()) {
                rewriteDependencyReferences(dependency, rootArtifactId, groupId);
            }
        }

        // rewrite DependencyManagement
        if (pom.getDependencyManagement() != null
                && pom.getDependencyManagement().getDependencies() != null
                && !pom.getDependencyManagement().getDependencies().isEmpty()) {
            for (Dependency dependency : pom.getDependencyManagement().getDependencies()) {
                rewriteDependencyReferences(dependency, rootArtifactId, groupId);
            }
        }

        // rewrite Plugins
        if (pom.getBuild() != null
                && pom.getBuild().getPlugins() != null
                && !pom.getBuild().getPlugins().isEmpty()) {
            for (Plugin plugin : pom.getBuild().getPlugins()) {
                rewritePluginReferences(plugin, rootArtifactId, groupId);
            }
        }

        // rewrite PluginManagement
        if (pom.getBuild() != null
                && pom.getBuild().getPluginManagement() != null
                && pom.getBuild().getPluginManagement().getPlugins() != null
                && !pom.getBuild().getPluginManagement().getPlugins().isEmpty()) {
            for (Plugin plugin : pom.getBuild().getPluginManagement().getPlugins()) {
                rewritePluginReferences(plugin, rootArtifactId, groupId);
            }
        }

        // rewrite Profiles
        if (pom.getProfiles() != null) {
            for (Profile profile : pom.getProfiles()) {
                // rewrite Dependencies
                if (profile.getDependencies() != null
                        && !profile.getDependencies().isEmpty()) {
                    for (Dependency dependency : profile.getDependencies()) {
                        rewriteDependencyReferences(dependency, rootArtifactId, groupId);
                    }
                }

                // rewrite DependencyManagement
                if (profile.getDependencyManagement() != null
                        && profile.getDependencyManagement().getDependencies() != null
                        && !profile.getDependencyManagement().getDependencies().isEmpty()) {
                    for (Dependency dependency :
                            profile.getDependencyManagement().getDependencies()) {
                        rewriteDependencyReferences(dependency, rootArtifactId, groupId);
                    }
                }

                // rewrite Plugins
                if (profile.getBuild() != null
                        && profile.getBuild().getPlugins() != null
                        && !profile.getBuild().getPlugins().isEmpty()) {
                    for (Plugin plugin : profile.getBuild().getPlugins()) {
                        rewritePluginReferences(plugin, rootArtifactId, groupId);
                    }
                }

                // rewrite PluginManagement
                if (profile.getBuild() != null
                        && profile.getBuild().getPluginManagement() != null
                        && profile.getBuild().getPluginManagement().getPlugins() != null
                        && !profile.getBuild()
                                .getPluginManagement()
                                .getPlugins()
                                .isEmpty()) {
                    for (Plugin plugin :
                            profile.getBuild().getPluginManagement().getPlugins()) {
                        rewritePluginReferences(plugin, rootArtifactId, groupId);
                    }
                }
            }
        }
    }

    private void rewriteDependencyReferences(Dependency dependency, String rootArtifactId, String groupId) {
        if (dependency.getArtifactId() != null && dependency.getArtifactId().indexOf(rootArtifactId) >= 0) {
            if (dependency.getGroupId() != null) {
                dependency.setGroupId(
                        StringUtils.replace(dependency.getGroupId(), groupId, "${" + Constants.GROUP_ID + "}"));
            }

            dependency.setArtifactId(
                    StringUtils.replace(dependency.getArtifactId(), rootArtifactId, "${rootArtifactId}"));

            if (dependency.getVersion() != null) {
                dependency.setVersion("${" + Constants.VERSION + "}");
            }
        }
    }

    private void rewritePluginReferences(Plugin plugin, String rootArtifactId, String groupId) {
        if (plugin.getArtifactId() != null && plugin.getArtifactId().indexOf(rootArtifactId) >= 0) {
            if (plugin.getGroupId() != null) {
                String g = StringUtils.replace(plugin.getGroupId(), groupId, "${" + Constants.GROUP_ID + "}");
                plugin.setGroupId(g);
            }

            plugin.setArtifactId(StringUtils.replace(plugin.getArtifactId(), rootArtifactId, "${rootArtifactId}"));

            if (plugin.getVersion() != null) {
                plugin.setVersion("${" + Constants.VERSION + "}");
            }
        }

        if (plugin.getArtifactId() != null && "maven-ear-plugin".equals(plugin.getArtifactId())) {
            rewriteEARPluginReferences(plugin, rootArtifactId, groupId);
        }
    }

    private void rewriteEARPluginReferences(Plugin plugin, String rootArtifactId, String groupId) {
        Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
        if (configuration != null) {
            Xpp3Dom modulesConfiguration = configuration.getChild("modules");
            Xpp3Dom[] modules = modulesConfiguration != null ? modulesConfiguration.getChildren() : new Xpp3Dom[0];
            for (int i = 0; i < modules.length; i++) {
                Xpp3Dom module = modules[i];
                Xpp3Dom moduleGroupId = module.getChild("groupId");
                Xpp3Dom moduleArtifactId = module.getChild("artifactId");
                Xpp3Dom moduleBundleFileName = module.getChild("bundleFileName");
                Xpp3Dom moduleModuleId = module.getChild("moduleId");
                Xpp3Dom moduleContextRoot = module.getChild("contextRoot");

                if (moduleGroupId != null) {
                    moduleGroupId.setValue(
                            StringUtils.replace(moduleGroupId.getValue(), groupId, "${" + Constants.GROUP_ID + "}"));
                }

                if (moduleArtifactId != null) {
                    moduleArtifactId.setValue(
                            StringUtils.replace(moduleArtifactId.getValue(), rootArtifactId, "${rootArtifactId}"));
                }

                if (moduleBundleFileName != null) {
                    moduleBundleFileName.setValue(
                            StringUtils.replace(moduleBundleFileName.getValue(), rootArtifactId, "${rootArtifactId}"));
                }

                if (moduleModuleId != null) {
                    moduleModuleId.setValue(
                            StringUtils.replace(moduleModuleId.getValue(), rootArtifactId, "${rootArtifactId}"));
                }

