org.jetbrains.plugins.groovy.grape.GrabDependencies.java Source code

Java tutorial

Introduction

Here is the source code for org.jetbrains.plugins.groovy.grape.GrabDependencies.java

Source

/*
 * Copyright 2000-2012 JetBrains s.r.o.
 *
 * 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.
 */
package org.jetbrains.plugins.groovy.grape;

import gnu.trove.THashSet;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.consulo.java.platform.module.extension.JavaModuleExtensionImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.runner.DefaultGroovyScriptRunner;
import org.jetbrains.plugins.groovy.runner.GroovyScriptRunConfiguration;
import org.jetbrains.plugins.groovy.runner.GroovyScriptRunner;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.execution.CantRunException;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.process.DefaultJavaProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.JdkUtil;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.util.ArchiveVfsUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PathUtil;
import com.intellij.util.PathsList;
import com.intellij.util.containers.ContainerUtil;

/**
 * @author peter
 */
public class GrabDependencies implements IntentionAction {
    private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.grape.GrabDependencies");

    public static final String GRAB_ANNO = "groovy.lang.Grab";
    public static final String GRAPES_ANNO = "groovy.lang.Grapes";
    public static final String GRAB_EXCLUDE_ANNO = "groovy.lang.GrabExclude";
    public static final String GRAB_RESOLVER_ANNO = "groovy.lang.GrabResolver";
    private static final NotificationGroup NOTIFICATION_GROUP = new NotificationGroup("Grape",
            NotificationDisplayType.BALLOON, true);

    @NotNull
    public String getText() {
        return "Grab the artifacts";
    }

    @NotNull
    public String getFamilyName() {
        return "Grab";
    }

    public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
        final GrAnnotation anno = PsiTreeUtil.findElementOfClassAtOffset(file, editor.getCaretModel().getOffset(),
                GrAnnotation.class, false);
        if (anno == null) {
            return false;
        }

        final String qname = anno.getQualifiedName();
        if (qname == null || !(qname.startsWith(GRAB_ANNO) || GRAPES_ANNO.equals(qname))) {
            return false;
        }

        final Module module = ModuleUtilCore.findModuleForPsiElement(file);
        if (module == null) {
            return false;
        }

        final Sdk sdk = ModuleUtilCore.getSdk(module, JavaModuleExtensionImpl.class);
        if (sdk == null) {
            return false;
        }

        return file.getOriginalFile().getVirtualFile() != null && sdk.getSdkType() instanceof JavaSdkType;
    }

    public void invoke(@NotNull final Project project, Editor editor, PsiFile file)
            throws IncorrectOperationException {
        final Module module = ModuleUtil.findModuleForPsiElement(file);
        assert module != null;

        final VirtualFile vfile = file.getOriginalFile().getVirtualFile();
        assert vfile != null;

        if (JavaPsiFacade.getInstance(project).findClass("org.apache.ivy.core.report.ResolveReport",
                file.getResolveScope()) == null) {
            Messages.showErrorDialog(
                    "Sorry, but IDEA cannot @Grab the dependencies without Ivy. Please add Ivy to your module dependencies and re-run the action.",
                    "Ivy Missing");
            return;
        }

        Map<String, String> queries = prepareQueries(file);

        final Sdk sdk = ModuleUtilCore.getSdk(module, JavaModuleExtensionImpl.class);
        assert sdk != null;
        SdkTypeId sdkType = sdk.getSdkType();
        assert sdkType instanceof JavaSdkType;

        final Map<String, GeneralCommandLine> lines = new HashMap<String, GeneralCommandLine>();
        for (String grabText : queries.keySet()) {
            final JavaParameters javaParameters = GroovyScriptRunConfiguration.createJavaParametersWithSdk(module);
            //debug
            //javaParameters.getVMParametersList().add("-Xdebug"); javaParameters.getVMParametersList().add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");

            DefaultGroovyScriptRunner.configureGenericGroovyRunner(javaParameters, module,
                    "org.jetbrains.plugins.groovy.grape.GrapeRunner", false);
            PathsList list;
            try {
                list = GroovyScriptRunner.getClassPathFromRootModel(module,
                        ProjectRootManager.getInstance(project).getFileIndex().isInTestSourceContent(vfile),
                        javaParameters, true);
            } catch (CantRunException e) {
                NOTIFICATION_GROUP.createNotification("Can't run @Grab: " + ExceptionUtil.getMessage(e),
                        ExceptionUtil.getThrowableText(e), NotificationType.ERROR, null).notify(project);
                return;
            }
            if (list == null) {
                list = new PathsList();
            }
            list.add(PathUtil.getJarPathForClass(GrapeRunner.class));

            javaParameters.getProgramParametersList().add("--classpath");
            javaParameters.getProgramParametersList().add(list.getPathsString());
            javaParameters.getProgramParametersList().add(queries.get(grabText));

            lines.put(grabText, JdkUtil.setupJVMCommandLine(sdk, javaParameters, true));
        }

        ProgressManager.getInstance().run(new Task.Backgroundable(project, "Processing @Grab annotations") {

            public void run(@NotNull ProgressIndicator indicator) {
                int jarCount = 0;
                String messages = "";

                for (Map.Entry<String, GeneralCommandLine> entry : lines.entrySet()) {
                    String grabText = entry.getKey();
                    indicator.setText2(grabText);
                    try {
                        final GrapeProcessHandler handler = new GrapeProcessHandler(entry.getValue(), module);
                        handler.startNotify();
                        handler.waitFor();
                        jarCount += handler.jarCount;
                        messages += "<b>" + grabText + "</b>: " + handler.messages + "<p>";
                    } catch (ExecutionException e) {
                        LOG.error(e);
                    }
                }

                final String finalMessages = messages;
                final String title = jarCount + " Grape dependency jar" + (jarCount == 1 ? "" : "s") + " added";
                NOTIFICATION_GROUP.createNotification(title, finalMessages, NotificationType.INFORMATION, null)
                        .notify(project);
            }
        });

    }

    static Map<String, String> prepareQueries(PsiFile file) {
        final Set<GrAnnotation> grabs = new LinkedHashSet<GrAnnotation>();
        final Set<GrAnnotation> excludes = new THashSet<GrAnnotation>();
        final Set<GrAnnotation> resolvers = new THashSet<GrAnnotation>();
        file.acceptChildren(new PsiRecursiveElementWalkingVisitor() {
            @Override
            public void visitElement(PsiElement element) {
                if (element instanceof GrAnnotation) {
                    GrAnnotation anno = (GrAnnotation) element;
                    String qname = anno.getQualifiedName();
                    if (GRAB_ANNO.equals(qname))
                        grabs.add(anno);
                    else if (GRAB_EXCLUDE_ANNO.equals(qname))
                        excludes.add(anno);
                    else if (GRAB_RESOLVER_ANNO.equals(qname))
                        resolvers.add(anno);
                }
                super.visitElement(element);
            }
        });

        Function<GrAnnotation, String> mapper = new Function<GrAnnotation, String>() {
            @Override
            public String fun(GrAnnotation grAnnotation) {
                return grAnnotation.getText();
            }
        };
        String common = StringUtil.join(excludes, mapper, " ") + " " + StringUtil.join(resolvers, mapper, " ");
        LinkedHashMap<String, String> result = new LinkedHashMap<String, String>();
        for (GrAnnotation grab : grabs) {
            String grabText = grab.getText();
            result.put(grabText, (grabText + " " + common).trim());
        }
        return result;
    }

    public boolean startInWriteAction() {
        return false;
    }

    private static class GrapeProcessHandler extends DefaultJavaProcessHandler {
        private final StringBuilder myStdOut = new StringBuilder();
        private final StringBuilder myStdErr = new StringBuilder();
        private final Module myModule;

        public GrapeProcessHandler(GeneralCommandLine commandLine, Module module) throws ExecutionException {
            super(commandLine);
            myModule = module;
        }

        @Override
        public void notifyTextAvailable(String text, Key outputType) {
            text = StringUtil.convertLineSeparators(text);
            if (LOG.isDebugEnabled()) {
                LOG.debug(outputType + text);
            }
            if (outputType == ProcessOutputTypes.STDOUT) {
                myStdOut.append(text);
            } else if (outputType == ProcessOutputTypes.STDERR) {
                myStdErr.append(text);
            }
        }

        private void addGrapeDependencies(List<VirtualFile> jars) {
            final ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel();
            final LibraryTable.ModifiableModel tableModel = model.getModuleLibraryTable().getModifiableModel();
            for (VirtualFile jar : jars) {
                final VirtualFile jarRoot = ArchiveVfsUtil.getJarRootForLocalFile(jar);
                if (jarRoot != null) {
                    OrderRootType rootType = OrderRootType.CLASSES;
                    String libName = "Grab:" + jar.getName();
                    for (String classifier : ContainerUtil.ar("sources", "source", "src")) {
                        if (libName.endsWith("-" + classifier + ".jar")) {
                            rootType = OrderRootType.SOURCES;
                            libName = StringUtil.trimEnd(libName, "-" + classifier + ".jar") + ".jar";
                        }
                    }

                    Library library = tableModel.getLibraryByName(libName);
                    if (library == null) {
                        library = tableModel.createLibrary(libName);
                    }

                    final Library.ModifiableModel libModel = library.getModifiableModel();
                    for (String url : libModel.getUrls(rootType)) {
                        libModel.removeRoot(url, rootType);
                    }
                    libModel.addRoot(jarRoot, rootType);
                    libModel.commit();
                }
            }
            tableModel.commit();
            model.commit();
        }

        int jarCount;
        String messages = "";

        @Override
        protected void notifyProcessTerminated(int exitCode) {
            try {
                final List<VirtualFile> jars = new ArrayList<VirtualFile>();
                for (String line : myStdOut.toString().split("\n")) {
                    if (line.startsWith(GrapeRunner.URL_PREFIX)) {
                        try {
                            final URL url = new URL(line.substring(GrapeRunner.URL_PREFIX.length()));
                            final File libFile = new File(url.toURI());
                            if (libFile.exists() && libFile.getName().endsWith(".jar")) {
                                ContainerUtil.addIfNotNull(jars,
                                        LocalFileSystem.getInstance().refreshAndFindFileByIoFile(libFile));
                            }
                        } catch (MalformedURLException e) {
                            LOG.error(e);
                        } catch (URISyntaxException e) {
                            LOG.error(e);
                        }
                    }
                }
                new WriteAction() {
                    protected void run(Result result) throws Throwable {
                        jarCount = jars.size();
                        messages = jarCount + " jar";
                        if (jarCount != 1) {
                            messages += "s";
                        }
                        if (jarCount == 0) {
                            messages += "<br>" + myStdOut.toString().replaceAll("\n", "<br>") + "<p>"
                                    + myStdErr.toString().replaceAll("\n", "<br>");
                        }
                        if (!jars.isEmpty()) {
                            addGrapeDependencies(jars);
                        }
                    }
                }.execute();
            } finally {
                super.notifyProcessTerminated(exitCode);
            }
        }
    }
}