org.jfrog.jade.plugins.idea.IdeaModuleMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.jfrog.jade.plugins.idea.IdeaModuleMojo.java

Source

package org.jfrog.jade.plugins.idea;

/*
 * Copyright 2005-2006 The Apache Software Foundation.
 *
 * Licensed 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.
 */

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.jfrog.maven.annomojo.annotations.MojoExecute;
import org.jfrog.maven.annomojo.annotations.MojoGoal;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Creates the module (*.iml) files for IntelliJ Idea
 *
 * @author Edwin Punzalan
 */
@MojoGoal("module")
@MojoExecute(phase = "generate-sources")
public class IdeaModuleMojo extends AbstractModuleIdeaMojo {

    /**
     * Create IDEA (.iml) project files.
     *
     * @throws org.apache.maven.plugin.MojoExecutionException
     *
     */
    public void execute() throws MojoExecutionException {
        try {
            doDependencyResolution();
        } catch (Exception e) {
            throw new MojoExecutionException("Unable to build project dependencies.", e);
        }

        rewriteModule();
    }

    public void rewriteModule() throws MojoExecutionException {
        MavenProject executedProject = getExecutedProject();
        File moduleFile = new File(executedProject.getBasedir(), getIdeProjectName() + ".iml");
        try {
            Document document = readXmlDocument(moduleFile, "module.xml");

            Element module = document.getRootElement();

            // TODO: how can we let the WAR/EJBs plugin hook in and provide this?
            // TODO: merge in ejb-module, etc.
            if ("war".equals(executedProject.getPackaging())) {
                addWebModule(module);
            } else if ("ejb".equals(executedProject.getPackaging())) {
                addEjbModule(module);
            } else if ("ear".equals(executedProject.getPackaging())) {
                addEarModule(module);
            } else if (isIdeaPlugin()) {
                addPluginModule(module);
            }

            Element component = findComponent(module, "NewModuleRootManager");
            Element output = findElement(component, "output");
            output.addAttribute("url", getModuleFileUrl(executedProject.getBuild().getOutputDirectory()));

            Element outputTest = findElement(component, "output-test");
            outputTest.addAttribute("url", getModuleFileUrl(executedProject.getBuild().getTestOutputDirectory()));

            Element content = findElement(component, "content");

            removeOldElements(content, "sourceFolder");

            for (Iterator i = executedProject.getCompileSourceRoots().iterator(); i.hasNext();) {
                String directory = (String) i.next();
                addSourceFolder(content, directory, false);
            }
            for (Iterator i = executedProject.getTestCompileSourceRoots().iterator(); i.hasNext();) {
                String directory = (String) i.next();
                addSourceFolder(content, directory, true);
            }

            for (Iterator i = executedProject.getBuild().getResources().iterator(); i.hasNext();) {
                Resource resource = (Resource) i.next();
                String directory = resource.getDirectory();
                if (resource.getTargetPath() == null && !resource.isFiltering()) {
                    addSourceFolder(content, directory, false);
                } else {
                    getLog().info(
                            "Not adding resource directory as it has an incompatible target path or filtering: "
                                    + directory);
                }
            }

            for (Iterator i = executedProject.getBuild().getTestResources().iterator(); i.hasNext();) {
                Resource resource = (Resource) i.next();
                String directory = resource.getDirectory();
                if (resource.getTargetPath() == null && !resource.isFiltering()) {
                    addSourceFolder(content, directory, true);
                } else {
                    getLog().info(
                            "Not adding test resource directory as it has an incompatible target path or filtering: "
                                    + directory);
                }
            }

            removeOldElements(content, "excludeFolder");

            //For excludeFolder
            File target = new File(executedProject.getBuild().getDirectory());
            File classes = new File(executedProject.getBuild().getOutputDirectory());
            File testClasses = new File(executedProject.getBuild().getTestOutputDirectory());

            List sourceFolders = content.elements("sourceFolder");

            List<String> filteredExcludes = new ArrayList<String>();
            filteredExcludes.addAll(getExcludedDirectories(target, filteredExcludes, sourceFolders));
            filteredExcludes.addAll(getExcludedDirectories(classes, filteredExcludes, sourceFolders));
            filteredExcludes.addAll(getExcludedDirectories(testClasses, filteredExcludes, sourceFolders));

            if (getExclude() != null) {
                String[] dirs = getExclude().split("[,\\s]+");
                for (int i = 0; i < dirs.length; i++) {
                    File excludedDir = new File(executedProject.getBasedir(), dirs[i]);
                    filteredExcludes.addAll(getExcludedDirectories(excludedDir, filteredExcludes, sourceFolders));
                }
            }

            // even though we just ran all the directories in the filteredExcludes List through the intelligent
            // getExcludedDirectories method, we never actually were guaranteed the order that they were added was
            // in the order required to make the most optimized exclude list. In addition, the smart logic from
            // that method is entirely skipped if the directory doesn't currently exist. A simple string matching
            // will do pretty much the same thing and make the list more concise.
            List<String> actuallyExcluded = new ArrayList<String>();
            Collections.sort(filteredExcludes);
            for (Iterator i = filteredExcludes.iterator(); i.hasNext();) {
                String dirToExclude = i.next().toString();
                String dirToExcludeTemp = dirToExclude.replace('\\', '/');
                boolean addExclude = true;
                for (Iterator iterator = actuallyExcluded.iterator(); iterator.hasNext();) {
                    String dir = iterator.next().toString();
                    String dirTemp = dir.replace('\\', '/');
                    if (dirToExcludeTemp.startsWith(dirTemp + "/")) {
                        addExclude = false;
                        break;
                    } else if (dir.startsWith(dirToExcludeTemp + "/")) {
                        actuallyExcluded.remove(dir);
                    }
                }

                if (addExclude) {
                    actuallyExcluded.add(dirToExclude);
                    addExcludeFolder(content, dirToExclude);
                }
            }

            rewriteDependencies(component);

            writeXmlDocument(moduleFile, document);
        } catch (DocumentException e) {
            throw new MojoExecutionException("Error parsing existing IML file " + moduleFile.getAbsolutePath(), e);
        } catch (IOException e) {
            throw new MojoExecutionException("Error parsing existing IML file " + moduleFile.getAbsolutePath(), e);
        }
    }

    private void rewriteDependencies(Element component) {
        Map<String, Element> modulesByName = new HashMap<String, Element>();
        Map<String, Element> modulesByUrl = new HashMap<String, Element>();
        Set<Element> unusedModules = new HashSet<Element>();
        for (Iterator children = component.elementIterator("orderEntry"); children.hasNext();) {
            Element orderEntry = (Element) children.next();

            String type = orderEntry.attributeValue("type");
            if ("module".equals(type)) {
                modulesByName.put(orderEntry.attributeValue("module-name"), orderEntry);
            } else if ("module-library".equals(type)) {
                // keep track for later so we know what is left
                unusedModules.add(orderEntry);

                Element lib = orderEntry.element("library");
                String name = lib.attributeValue("name");
                if (name != null) {
                    modulesByName.put(name, orderEntry);
                } else {
                    Element classesChild = lib.element("CLASSES");
                    if (classesChild != null) {
                        Element rootChild = classesChild.element("root");
                        if (rootChild != null) {
                            String url = rootChild.attributeValue("url");
                            if (url != null) {
                                // Need to ignore case because of Windows drive letters
                                modulesByUrl.put(url.toLowerCase(), orderEntry);
                            }
                        }
                    }
                }
            }
        }

        List testClasspathElements = getExecutedProject().getTestArtifacts();
        for (Iterator i = testClasspathElements.iterator(); i.hasNext();) {
            Artifact a = (Artifact) i.next();

            Library library = findLibrary(a);
            if (library != null && library.isExclude()) {
                continue;
            }

            String moduleName;
            if (isUseFullNames()) {
                moduleName = a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getType() + ':' + a.getVersion();
            } else {
                moduleName = getNameProvider().getProjectName(a);
            }

            Element dep = (Element) modulesByName.get(moduleName);

            if (dep == null) {
                // Need to ignore case because of Windows drive letters
                dep = (Element) modulesByUrl.get(getLibraryUrl(a).toLowerCase());
            }

            if (dep != null) {
                unusedModules.remove(dep);
            } else {
                dep = createElement(component, "orderEntry");
            }

            boolean isIdeaModule = false;
            if (isLinkModules()) {
                isIdeaModule = isReactorProject(a.getGroupId(), a.getArtifactId());

                if (isIdeaModule) {
                    dep.addAttribute("type", "module");
                    dep.addAttribute("module-name", moduleName);
                }
            }

            if (a.getFile() != null && !isIdeaModule) {
                dep.addAttribute("type", "module-library");

                Element lib = dep.element("library");

                if (lib == null) {
                    lib = createElement(dep, "library");
                }

                if (isDependenciesAsLibraries()) {
                    lib.addAttribute("name", moduleName);
                }

                // replace classes
                removeOldElements(lib, "CLASSES");
                Element classes = createElement(lib, "CLASSES");
                if (library != null && library.getSplitClasses().length > 0) {
                    lib.addAttribute("name", moduleName);
                    String[] libraryClasses = library.getSplitClasses();
                    for (int k = 0; k < libraryClasses.length; k++) {
                        String classpath = libraryClasses[k];
                        extractMacro(classpath);
                        Element classEl = createElement(classes, "root");
                        classEl.addAttribute("url", classpath);
                    }
                } else {
                    createElement(classes, "root").addAttribute("url", getLibraryUrl(a));
                }

                if (library != null && library.getSplitSources().length > 0) {
                    removeOldElements(lib, "SOURCES");
                    Element sourcesElement = createElement(lib, "SOURCES");
                    String[] sources = library.getSplitSources();
                    for (int k = 0; k < sources.length; k++) {
                        String source = sources[k];
                        extractMacro(source);
                        Element sourceEl = createElement(sourcesElement, "root");
                        sourceEl.addAttribute("url", source);
                    }
                } else if (isDownloadSources()) {
                    resolveClassifier(createOrGetElement(lib, "SOURCES"), a, getSourceClassifier());
                }

                if (isDownloadJavadocs()) {
                    resolveClassifier(createOrGetElement(lib, "JAVADOC"), a, getJavadocClassifier());
                }
            }
        }

        for (Iterator i = unusedModules.iterator(); i.hasNext();) {
            Element orderEntry = (Element) i.next();

            component.remove(orderEntry);
        }
    }

    private Element createOrGetElement(Element lib, String name) {
        Element el = lib.element("name");

        if (el == null) {
            el = createElement(lib, name);
        }
        return el;
    }

    private void addEarModule(Element module) {
        module.addAttribute("type", "J2EE_APPLICATION_MODULE");
        Element component = findComponent(module, "ApplicationModuleProperties");
        addDeploymentDescriptor(component, "application.xml", "1.3",
                getExecutedProject().getBuild().getDirectory() + "/application.xml");
    }

    private void addEjbModule(Element module) {
        module.addAttribute("type", "J2EE_EJB_MODULE");

        MavenProject executedProject = getExecutedProject();

        String explodedDir = executedProject.getBuild().getDirectory() + "/" + getIdeProjectName();

        Element component = findComponent(module, "EjbModuleBuildComponent");

        Element setting = findSetting(component, "EXPLODED_URL");
        setting.addAttribute("value", getModuleFileUrl(explodedDir));

        component = findComponent(module, "EjbModuleProperties");
        addDeploymentDescriptor(component, "ejb-jar.xml", "2.x", "src/main/resources/META-INF/ejb-jar.xml");

        removeOldElements(component, "containerElement");
        List artifacts = executedProject.getTestArtifacts();
        for (Iterator i = artifacts.iterator(); i.hasNext();) {
            Artifact artifact = (Artifact) i.next();

            Element containerElement = createElement(component, "containerElement");

            if (isLinkModules() && isReactorProject(artifact.getGroupId(), artifact.getArtifactId())) {
                containerElement.addAttribute("type", "module");
                containerElement.addAttribute("name", getNameProvider().getProjectName(artifact));
                Element methodAttribute = createElement(containerElement, "attribute");
                methodAttribute.addAttribute("name", "method");
                methodAttribute.addAttribute("value", "6");
                Element uriAttribute = createElement(containerElement, "attribute");
                uriAttribute.addAttribute("name", "URI");
                uriAttribute.addAttribute("value", "/WEB-INF/classes");
            } else if (artifact.getFile() != null) {
                containerElement.addAttribute("type", "library");
                containerElement.addAttribute("level", "module");
                containerElement.addAttribute("name", getNameProvider().getProjectName(artifact));
                Element methodAttribute = createElement(containerElement, "attribute");
                methodAttribute.addAttribute("name", "method");
                methodAttribute.addAttribute("value", "2");
                Element uriAttribute = createElement(containerElement, "attribute");
                uriAttribute.addAttribute("name", "URI");
                uriAttribute.addAttribute("value", "/WEB-INF/lib/" + artifact.getFile().getName());
            }
        }
    }

    private void extractMacro(String path) {
        if (getMacros() != null) {
            Pattern p = Pattern.compile(".*\\$([^\\$]+)\\$.*");
            Matcher matcher = p.matcher(path);
            while (matcher.find()) {
                String macro = matcher.group(1);
                getMacros().add(macro);
            }
        }
    }

    private Library findLibrary(Artifact a) {
        Library[] libraries = getLibraries();
        if (libraries != null) {
            for (int j = 0; j < libraries.length; j++) {
                Library library = libraries[j];
                if (a.getArtifactId().equals(library.getName())) {
                    return library;
                }
            }
        }

        return null;
    }

    private List<String> getExcludedDirectories(File target, List excludeList, List sourceFolders) {
        List<String> foundFolders = new ArrayList<String>();

        int totalDirs = 0, excludedDirs = 0;

        if (target.exists() && !excludeList.contains(target.getAbsolutePath())) {
            File[] files = target.listFiles();

            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (file.isDirectory() && !excludeList.contains(file.getAbsolutePath())) {
                    totalDirs++;

                    // the '/' character is added to the directory path since the
                    // source urls end with this character
                    String absolutePath = file.getAbsolutePath() + "/";
                    String url = getModuleFileUrl(absolutePath);

                    boolean addToExclude = true;
                    for (Iterator sources = sourceFolders.iterator(); sources.hasNext();) {
                        String source = ((Element) sources.next()).attributeValue("url");
                        if (source.equals(url)) {
                            addToExclude = false;
                            break;
                        } else if (source.indexOf(url) == 0) {
                            foundFolders.addAll(
                                    getExcludedDirectories(new File(absolutePath), excludeList, sourceFolders));
                            addToExclude = false;
                            break;
                        }
                    }
                    if (addToExclude) {
                        excludedDirs++;
                        foundFolders.add(absolutePath);
                    }
                }
            }

            //if all directories are excluded, then just exclude the parent directory
            if (totalDirs > 0 && totalDirs == excludedDirs) {
                foundFolders.clear();

                foundFolders.add(target.getAbsolutePath());
            }
        } else if (!target.exists()) {
            //might as well exclude a non-existent dir so that it won't show when it suddenly appears
            foundFolders.add(target.getAbsolutePath());
        }

        return foundFolders;
    }

    /**
     * Adds the Web module to the (.iml) project file.
     *
     * @param module Xpp3Dom element
     */
    private void addWebModule(Element module) {
        // TODO: this is bad - reproducing war plugin defaults, etc!
        //   --> this is where the OGNL out of a plugin would be helpful as we could run package first and
        //       grab stuff from the mojo

        MavenProject executedProject = getExecutedProject();
        String warWebapp = executedProject.getBuild().getDirectory() + "/" + executedProject.getArtifactId();
        String warSrc = getPluginSetting("maven-war-plugin", "warSourceDirectory", "src/main/webapp");
        String webXml = warSrc + "/WEB-INF/web.xml";

        module.addAttribute("type", "J2EE_WEB_MODULE");

        Element component = findComponent(module, "WebModuleBuildComponent");
        Element setting = findSetting(component, "EXPLODED_URL");
        setting.addAttribute("value", getModuleFileUrl(warWebapp));

        component = findComponent(module, "WebModuleProperties");

        removeOldElements(component, "containerElement");
        List artifacts = executedProject.getTestArtifacts();
        for (Iterator i = artifacts.iterator(); i.hasNext();) {
            Artifact artifact = (Artifact) i.next();

            Element containerElement = createElement(component, "containerElement");

            if (isLinkModules() && isReactorProject(artifact.getGroupId(), artifact.getArtifactId())) {
                containerElement.addAttribute("type", "module");
                containerElement.addAttribute("name", getNameProvider().getProjectName(artifact));
                Element methodAttribute = createElement(containerElement, "attribute");
                methodAttribute.addAttribute("name", "method");
                methodAttribute.addAttribute("value", "5");
                Element uriAttribute = createElement(containerElement, "attribute");
                uriAttribute.addAttribute("name", "URI");
                // TODO: Find a way to get this info from the war plugin
                uriAttribute.addAttribute("value",
                        "/WEB-INF/lib/" + artifact.getArtifactId() + "-" + artifact.getVersion() + ".jar");
            } else if (artifact.getFile() != null) {
                containerElement.addAttribute("type", "library");
                containerElement.addAttribute("level", "module");
                Element methodAttribute = createElement(containerElement, "attribute");
                methodAttribute.addAttribute("name", "method");
                if (Artifact.SCOPE_PROVIDED.equalsIgnoreCase(artifact.getScope())) {
                    methodAttribute.addAttribute("value", "0");// If scope is provided, do not package.
                } else {
                    methodAttribute.addAttribute("value", "1");// IntelliJ 5.0.2 is bugged and doesn't read it
                }
                Element uriAttribute = createElement(containerElement, "attribute");
                uriAttribute.addAttribute("name", "URI");
                uriAttribute.addAttribute("value", "/WEB-INF/lib/" + artifact.getFile().getName());
                Element url = createElement(containerElement, "url");
                url.setText(getLibraryUrl(artifact));
            }
        }

        addDeploymentDescriptor(component, "web.xml", "2.3", webXml);

        Element element = findElement(component, "webroots");
        removeOldElements(element, "root");

        element = createElement(element, "root");
        element.addAttribute("relative", "/");
        element.addAttribute("url", getModuleFileUrl(warSrc));
    }

    private void addPluginModule(Element module) {
        module.addAttribute("type", "PLUGIN_MODULE");

        // this is where the META-INF/plugin.xml file is located
        Element pluginDevElement = createElement(module, "component");
        pluginDevElement.addAttribute("name", "DevKit.ModuleBuildProperties");
        pluginDevElement.addAttribute("url", getModuleFileUrl("src/main/resources/META-INF/plugin.xml"));
    }

    /**
     * Translate the relative path of the file into module path
     *
     * @param basedir File to use as basedir
     * @param path    Absolute path string to translate to ModuleFileUrl
     * @return moduleFileUrl Translated Module File URL
     */
    private String getModuleFileUrl(File basedir, String path) {
        return "file://$MODULE_DIR$/" + toRelative(basedir, path);
    }

    private String getModuleFileUrl(String file) {
        return getModuleFileUrl(getExecutedProject().getBasedir(), file);
    }

    /**
     * Adds a sourceFolder element to IDEA (.iml) project file
     *
     * @param content   Xpp3Dom element
     * @param directory Directory to set as url.
     * @param isTest    True if directory isTestSource.
     */
    private void addSourceFolder(Element content, String directory, boolean isTest) {
        if (!StringUtils.isEmpty(directory) && new File(directory).isDirectory()) {
            Element sourceFolder = createElement(content, "sourceFolder");
            sourceFolder.addAttribute("url", getModuleFileUrl(directory));
            sourceFolder.addAttribute("isTestSource", Boolean.toString(isTest));
        }
    }

    private void addExcludeFolder(Element content, String directory) {
        Element excludeFolder = createElement(content, "excludeFolder");
        excludeFolder.addAttribute("url", getModuleFileUrl(directory));
    }

    private boolean isReactorProject(String groupId, String artifactId) {
        List reactorProjects = getReactorProjects();
        if (reactorProjects != null) {
            for (Iterator j = reactorProjects.iterator(); j.hasNext();) {
                MavenProject p = (MavenProject) j.next();
                if (p.getGroupId().equals(groupId) && p.getArtifactId().equals(artifactId)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void resolveClassifier(Element element, Artifact a, String classifier) {
        String id = a.getId() + '-' + classifier;

        String path = getAttemptDownloadMgr().getDownloadedPath(this, id, a, classifier);

        if (path != null) {
            String jarPath = "jar://" + path + "!/";
            getLog().debug("Setting " + classifier + " for " + id + " to " + jarPath);
            removeOldElements(element, "root");
            createElement(element, "root").addAttribute("url", jarPath);
        }
    }

    /**
     * Returns a an Xpp3Dom element (setting).
     *
     * @param component Xpp3Dom element
     * @param name      Setting attribute to find
     * @return setting Xpp3Dom element
     */
    private Element findSetting(Element component, String name) {
        return findElement(component, "setting", name);
    }

    private String getLibraryUrl(Artifact artifact) {
        return "jar://" + artifact.getFile().getAbsolutePath().replace('\\', '/') + "!/";
    }

    private Element addDeploymentDescriptor(Element component, String name, String version, String file) {
        Element deploymentDescriptor = findElement(component, "deploymentDescriptor");

        if (deploymentDescriptor.attributeValue("version") == null) {
            deploymentDescriptor.addAttribute("version", version);
        }

        if (deploymentDescriptor.attributeValue("name") == null) {
            deploymentDescriptor.addAttribute("name", name);
        }

        deploymentDescriptor.addAttribute("optional", "false");

        if (deploymentDescriptorFile == null) {
            deploymentDescriptorFile = file;
        }

        deploymentDescriptor.addAttribute("url", getModuleFileUrl(deploymentDescriptorFile));

        return deploymentDescriptor;
    }
}