| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * 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 com.android.tools.idea.run; |
| |
| import com.intellij.execution.ExecutionException; |
| import com.intellij.execution.ExecutionManager; |
| import com.intellij.execution.Executor; |
| import com.intellij.execution.configurations.GeneralCommandLine; |
| import com.intellij.execution.executors.DefaultRunExecutor; |
| import com.intellij.execution.filters.TextConsoleBuilder; |
| import com.intellij.execution.filters.TextConsoleBuilderFactory; |
| import com.intellij.execution.process.*; |
| import com.intellij.execution.ui.ConsoleView; |
| import com.intellij.execution.ui.ConsoleViewContentType; |
| import com.intellij.execution.ui.RunContentDescriptor; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.util.Locale; |
| |
| /** |
| * {@link com.android.tools.idea.run.ExternalToolRunner} allows running the given |
| * {@link com.intellij.execution.configurations.GeneralCommandLine} in an external process while attaching a console to it. The console |
| * shows up inside the Run tool window with the given title. By default, a single stop action is provided in the toolbar attached to |
| * the console that allows for killing the running command. |
| * |
| * This class is patterned after {@link com.intellij.execution.runners.AbstractConsoleRunnerWithHistory}. |
| */ |
| public class ExternalToolRunner { |
| private final Project myProject; |
| private final String myTitle; |
| private final GeneralCommandLine myCommandLine; |
| |
| private ProcessHandler myProcessHandler; |
| |
| public ExternalToolRunner(@NotNull Project project, @NotNull String consoleTitle, @NotNull GeneralCommandLine commandLine) { |
| myProject = project; |
| myTitle = consoleTitle; |
| myCommandLine = commandLine; |
| } |
| |
| public ProcessHandler start() throws ExecutionException { |
| final Process process = createProcess(); |
| myProcessHandler = createProcessHandler(process); |
| |
| UIUtil.invokeLaterIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| initConsoleUi(); |
| } |
| }); |
| |
| return myProcessHandler; |
| } |
| |
| @NotNull |
| protected Process createProcess() throws ExecutionException { |
| return myCommandLine.createProcess(); |
| } |
| |
| @NotNull |
| protected ProcessHandler createProcessHandler(Process process) { |
| return new OSProcessHandler(process); |
| } |
| |
| protected ConsoleView initConsoleUi() { |
| ConsoleView consoleView = createConsoleView(); |
| consoleView.print(myCommandLine.getCommandLineString() + '\n', ConsoleViewContentType.USER_INPUT); |
| consoleView.attachToProcess(myProcessHandler); |
| |
| JPanel panel = new JPanel(new BorderLayout()); |
| |
| DefaultActionGroup toolbarActions = new DefaultActionGroup(); |
| ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false); |
| panel.add(actionToolbar.getComponent(), BorderLayout.WEST); |
| panel.add(consoleView.getComponent(), BorderLayout.CENTER); |
| actionToolbar.setTargetComponent(panel); |
| |
| toolbarActions.addAll(consoleView.createConsoleActions()); |
| fillToolBarActions(toolbarActions); |
| panel.updateUI(); |
| |
| Executor defaultExecutor = DefaultRunExecutor.getRunExecutorInstance(); |
| final RunContentDescriptor contentDescriptor = new RunContentDescriptor(consoleView, myProcessHandler, panel, myTitle); |
| showConsole(defaultExecutor, contentDescriptor); |
| |
| myProcessHandler.addProcessListener(new ConsoleListener(myProject, defaultExecutor, myProcessHandler)); |
| myProcessHandler.startNotify(); |
| return consoleView; |
| } |
| |
| protected ConsoleView createConsoleView() { |
| TextConsoleBuilder builder = TextConsoleBuilderFactory.getInstance().createBuilder(myProject); |
| builder.setViewer(true); |
| return builder.getConsole(); |
| } |
| |
| protected void fillToolBarActions(DefaultActionGroup toolbarActions) { |
| toolbarActions.addAction(ActionManager.getInstance().getAction(IdeActions.ACTION_STOP_PROGRAM)); |
| } |
| |
| protected void showConsole(Executor defaultExecutor, RunContentDescriptor myDescriptor) { |
| // Show in run tool window |
| ExecutionManager.getInstance(myProject).getContentManager().showRunContent(defaultExecutor, myDescriptor); |
| } |
| |
| // Listens to console output and fronts the console in case of errors |
| private static class ConsoleListener extends ProcessAdapter { |
| private final Project myProject; |
| private final Executor myExecutor; |
| private final ProcessHandler myProcessHandler; |
| |
| public ConsoleListener(@NotNull Project project, |
| @NotNull Executor executor, |
| @NotNull ProcessHandler processHandler) { |
| myProject = project; |
| myExecutor = executor; |
| myProcessHandler = processHandler; |
| } |
| |
| @Override |
| public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) { |
| myProcessHandler.removeProcessListener(this); |
| } |
| |
| @Override |
| public void onTextAvailable(ProcessEvent event, Key outputType) { |
| String text = event.getText(); |
| boolean hasError = text != null && text.toLowerCase(Locale.US).contains("error"); |
| if (hasError || ProcessOutputTypes.STDERR.equals(outputType)) { |
| ExecutionManager.getInstance(myProject).getContentManager().toFrontRunContent(myExecutor, myProcessHandler); |
| } |
| } |
| } |
| |
| public static class ProcessOutputCollector extends ProcessAdapter { |
| private final StringBuilder sb = new StringBuilder(); |
| |
| @Override |
| public void onTextAvailable(ProcessEvent event, Key outputType) { |
| sb.append(event.getText()); |
| } |
| |
| public String getText() { |
| return sb.toString(); |
| } |
| } |
| } |