/*
* Copyright 2000-2009 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.napile.idea.thermit.config.execution;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.napile.idea.thermit.ThermitBundle;
import org.napile.idea.thermit.config.AntBuildFileBase;
import org.napile.idea.thermit.config.AntBuildListener;
import org.napile.idea.thermit.config.actions.ChangeViewAction;
import org.napile.idea.thermit.config.actions.PauseOutputAction;
import org.napile.idea.thermit.config.actions.RunAction;
import org.napile.idea.thermit.config.actions.StopAction;
import org.napile.idea.thermit.config.actions.VerboseAction;
import org.napile.idea.thermit.config.impl.AntBuildFileImpl;
import org.napile.idea.thermit.config.impl.HelpID;
import com.intellij.execution.junit2.segments.OutputPacketProcessor;
import com.intellij.execution.testframework.Printable;
import com.intellij.execution.testframework.Printer;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.OccurenceNavigator;
import com.intellij.ide.TreeExpander;
import com.intellij.ide.actions.CloseTabToolbarAction;
import com.intellij.ide.actions.ContextHelpAction;
import com.intellij.ide.actions.NextOccurenceToolbarAction;
import com.intellij.ide.actions.PreviousOccurenceToolbarAction;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Clock;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.peer.PeerFactory;
import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.intellij.ui.content.ContentManagerAdapter;
import com.intellij.ui.content.ContentManagerEvent;
import com.intellij.ui.content.MessageView;
import com.intellij.util.Alarm;
import com.intellij.util.text.DateFormatUtil;
public final class AntBuildMessageView extends JPanel implements DataProvider, OccurenceNavigator
{
private static final Logger LOG = Logger.getInstance("#com.intellij.thermit.execution.AntBuildMessageView");
public enum MessageType
{
BUILD,
TARGET,
TASK,
MESSAGE,
ERROR,
}
private static final Key<AntBuildMessageView> KEY = Key.create("BuildMessageView.KEY");
private static final String BUILD_CONTENT_NAME = ThermitBundle.message("ant.build.tab.content.title");
public static final int PRIORITY_ERR = 0;
public static final int PRIORITY_WARN = 1;
public static final int PRIORITY_BRIEF = 2;
public static final int PRIORITY_VERBOSE = 3;
private OutputParser myParsingThread;
private final Project myProject;
private final JPanel myMessagePanel;
private AntBuildFileBase myBuildFile;
private final String[] myTargets;
private int myPriorityThreshold = PRIORITY_BRIEF;
private int myErrorCount;
private int myWarningCount;
private volatile boolean myIsOutputPaused = false;
private AntOutputView myCurrentView;
private final PlainTextView myPlainTextView;
private final TreeView myTreeView;
private final java.util.List<LogCommand> myLog = Collections.synchronizedList(new ArrayList<LogCommand>(1024));
private volatile int myCommandsProcessedCount = 0;
private JPanel myProgressPanel;
private final AntMessageCustomizer[] myMessageCustomizers = AntMessageCustomizer.EP_NAME.getExtensions();
private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
private final Runnable myFlushLogRunnable = new Runnable()
{
@Override
public void run()
{
if(myTreeView != null && myCommandsProcessedCount < myLog.size())
{
if(!myIsOutputPaused)
{
new OutputFlusher().doFlush();
myTreeView.scrollToLastMessage();
}
}
}
};
private boolean myIsAborted;
private ActionToolbar myLeftToolbar;
private ActionToolbar myRightToolbar;
private final TreeExpander myTreeExpander = new TreeExpander()
{
public boolean canCollapse()
{
return isTreeView();
}
public boolean canExpand()
{
return isTreeView();
}
public void collapseAll()
{
AntBuildMessageView.this.collapseAll();
}
public void expandAll()
{
AntBuildMessageView.this.expandAll();
}
};
@NonNls
public static final String FILE_PREFIX = "file:";
private AntBuildMessageView(Project project, AntBuildFileBase buildFile, String[] targets)
{
super(new BorderLayout(2, 0));
myProject = project;
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
myPlainTextView = new PlainTextView(project);
myTreeView = new TreeView(project, buildFile);
myMessagePanel = new JPanel(new BorderLayout());
myBuildFile = buildFile;
myTargets = targets;
showAntView(AntBuildFileImpl.TREE_VIEW.value(buildFile.getAllOptions()));
setVerboseMode(AntBuildFileImpl.VERBOSE.value(buildFile.getAllOptions()));
add(createToolbarPanel(), BorderLayout.WEST);
add(myMessagePanel, BorderLayout.CENTER);
}
public void changeView()
{
showAntView(!isTreeView());
if(myBuildFile != null)
{
myBuildFile.setTreeView(isTreeView());
}
}
private boolean isTreeView()
{
return myCurrentView == myTreeView;
}
public void setVerboseMode(boolean verbose)
{
changeDetalizationLevel(verbose ? PRIORITY_VERBOSE : PRIORITY_BRIEF);
if(myBuildFile != null)
{
myBuildFile.setVerboseMode(verbose);
}
}
public boolean isVerboseMode()
{
return myPriorityThreshold == PRIORITY_VERBOSE;
}
private synchronized void changeDetalizationLevel(int priorityThreshold)
{
myPriorityThreshold = priorityThreshold;
TreeView.TreeSelection selection = myTreeView.getSelection();
myTreeView.clearAllMessages();
myPlainTextView.clearAllMessages();
myTreeView.setActionsEnabled(false);
new OutputFlusher()
{
public void doFlush()
{
final int processedCount = myCommandsProcessedCount;
for(int i = 0; i < processedCount; i++)
{
LogCommand command = myLog.get(i);
proceedOneCommand(command);
}
flushDelayedMessages();
}
}.doFlush();
myTreeView.setActionsEnabled(true);
if(!myTreeView.restoreSelection(selection))
{
myTreeView.scrollToLastMessage();
}
}
private void showAntView(boolean treeView)
{
AntOutputView oldView = getOutputView(treeView);
AntOutputView newView = getOutputView(!treeView);
myCurrentView = newView;
myMessagePanel.remove(oldView.getComponent());
myMessagePanel.add(newView.getComponent(), BorderLayout.CENTER);
myMessagePanel.validate();
JComponent component = IdeFocusTraversalPolicy.getPreferredFocusedComponent(myMessagePanel);
component.requestFocus();
repaint();
}
private AntOutputView getOutputView(boolean isText)
{
return isText ? myPlainTextView : myTreeView;
}
public AntBuildFileBase getBuildFile()
{
return myBuildFile;
}
/**
* @return can be null if user cancelled operation
*/
@Nullable
public static AntBuildMessageView openBuildMessageView(Project project, AntBuildFileBase buildFile, String[] targets)
{
final VirtualFile antFile = buildFile.getVirtualFile();
if(!LOG.assertTrue(antFile != null))
{
return null;
}
// check if there are running instances of the same build file
MessageView ijMessageView = MessageView.SERVICE.getInstance(project);
Content[] contents = ijMessageView.getContentManager().getContents();
for(Content content : contents)
{
if(content.isPinned())
{
continue;
}
AntBuildMessageView buildMessageView = content.getUserData(KEY);
if(buildMessageView == null)
{
continue;
}
if(!antFile.equals(buildMessageView.getBuildFile().getVirtualFile()))
{
continue;
}
if(buildMessageView.isStopped())
{
ijMessageView.getContentManager().removeContent(content, true);
continue;
}
int result = Messages.showYesNoCancelDialog(ThermitBundle.message("ant.is.active.terminate.confirmation.text"), ThermitBundle.message("starting.ant.build.dialog.title"), Messages.getQuestionIcon());
switch(result)
{
case 0: // yes
buildMessageView.stopProcess();
ijMessageView.getContentManager().removeContent(content, true);
continue;
case 1: // no
continue;
default: // cancel
return null;
}
}
final AntBuildMessageView messageView = new AntBuildMessageView(project, buildFile, targets);
String contentName = buildFile.getPresentableName();
contentName = BUILD_CONTENT_NAME + " (" + contentName + ")";
final Content content = PeerFactory.getInstance().getContentFactory().createContent(messageView.getComponent(), contentName, true);
content.putUserData(KEY, messageView);
ijMessageView.getContentManager().addContent(content);
ijMessageView.getContentManager().setSelectedContent(content);
content.setDisposer(new Disposable()
{
@Override
public void dispose()
{
Disposer.dispose(messageView.myAlarm);
}
});
new CloseListener(content, ijMessageView.getContentManager(), project);
// Do not inline next two variabled. Seeking for NPE.
ToolWindow messageToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
messageToolWindow.activate(null);
return messageView;
}
public void removeProgressPanel()
{
if(myProgressPanel != null)
{
myMessagePanel.remove(myProgressPanel);
// fix of 9377
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
myMessagePanel.validate();
}
});
myProgressPanel = null;
}
}
public void setParsingThread(OutputParser parsingThread)
{
myParsingThread = parsingThread;
myIsAborted = false;
}
public void stopProcess()
{
if(myParsingThread != null)
{
myParsingThread.stopProcess();
}
myIsAborted = true;
myLeftToolbar.updateActionsImmediately();
myRightToolbar.updateActionsImmediately();
}
public boolean isStopped()
{
return myParsingThread == null || myParsingThread.isStopped();
}
public boolean isStoppedOrTerminateRequested()
{
return myParsingThread == null || myParsingThread.isTerminateInvoked() || isStopped();
}
private void close()
{
MessageView messageView = MessageView.SERVICE.getInstance(myProject);
Content[] contents = messageView.getContentManager().getContents();
for(Content content : contents)
{
if(content.getComponent() == this)
{
messageView.getContentManager().removeContent(content, true);
return;
}
}
}
private JPanel createToolbarPanel()
{
RunAction runAction = new RunAction(this);
runAction.registerCustomShortcutSet(CommonShortcuts.getRerun(), this);
DefaultActionGroup leftActionGroup = new DefaultActionGroup();
leftActionGroup.add(runAction);
leftActionGroup.add(new PauseOutputAction(this));
leftActionGroup.add(new StopAction(this));
leftActionGroup.add(new CloseAction());
leftActionGroup.add(new PreviousOccurenceToolbarAction(this));
leftActionGroup.add(new NextOccurenceToolbarAction(this));
leftActionGroup.add(new ContextHelpAction(HelpID.ANT));
DefaultActionGroup rightActionGroup = new DefaultActionGroup();
rightActionGroup.add(new ChangeViewAction(this));
rightActionGroup.add(new VerboseAction(this));
rightActionGroup.add(CommonActionsManager.getInstance().createExpandAllAction(myTreeExpander, this));
rightActionGroup.add(CommonActionsManager.getInstance().createCollapseAllAction(myTreeExpander, this));
rightActionGroup.add(myTreeView.createToggleAutoscrollAction());
myLeftToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.ANT_MESSAGES_TOOLBAR, leftActionGroup, false);
JPanel toolbarPanel = new JPanel(new GridLayout(1, 2, 2, 0));
toolbarPanel.add(myLeftToolbar.getComponent());
myRightToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.ANT_MESSAGES_TOOLBAR, rightActionGroup, false);
toolbarPanel.add(myRightToolbar.getComponent());
return toolbarPanel;
}
public final class CloseAction extends CloseTabToolbarAction
{
public void actionPerformed(AnActionEvent e)
{
close();
}
}
private synchronized void addCommand(LogCommand command)
{
myLog.add(command);
myAlarm.cancelAllRequests();
myAlarm.addRequest(myFlushLogRunnable, 100L);
}
public void startBuild(String buildName)
{
addCommand(new StartBuildCommand(buildName));
}
public void buildFailed(String buildName)
{
addCommand(new BuildFailedCommand(buildName));
}
public void startTarget(String targetName)
{
addCommand(new StartTargetCommand(targetName));
}
public void startTask(String taskName)
{
addCommand(new StartTaskCommand(taskName));
}
public void outputMessage(final String text, final int priority)
{
final AntMessage customizedMessage = getCustomizedMessage(text, priority);
final AntMessage message = customizedMessage != null ? customizedMessage : new AntMessage(MessageType.MESSAGE, priority, text, null, 0, 0);
updateErrorAndWarningCounters(message.getPriority());
addCommand(new AddMessageCommand(message));
}
@Nullable
private AntMessage getCustomizedMessage(final String text, final int priority)
{
AntMessage customizedMessage = null;
for(AntMessageCustomizer customizer : myMessageCustomizers)
{
customizedMessage = customizer.createCustomizedMessage(text, priority);
if(customizedMessage != null)
{
break;
}
}
return customizedMessage;
}
public void outputError(String error, int priority)
{
//updateErrorAndWarningCounters(priority);
AntMessage message = createErrorMessage(MessageType.ERROR, priority, error);
addMessage(MessageType.ERROR, priority, error, message.getFile(), message.getLine(), message.getColumn());
WolfTheProblemSolver wolf = WolfTheProblemSolver.getInstance(myProject);
wolf.queue(message.getFile());
}
public void outputException(String exception)
{
updateErrorAndWarningCounters(PRIORITY_ERR);
AntMessage message = createErrorMessage(MessageType.ERROR, 0, exception);
addCommand(new AddExceptionCommand(message));
WolfTheProblemSolver wolf = WolfTheProblemSolver.getInstance(myProject);
wolf.queue(message.getFile());
}
private void updateErrorAndWarningCounters(int priority)
{
if(priority == PRIORITY_ERR)
{
myErrorCount++;
}
else if(priority == PRIORITY_WARN)
{
myWarningCount++;
}
}
public void finishTarget()
{
addCommand(new FinishTargetCommand());
}
public void finishTask()
{
addCommand(new FinishTaskCommand());
}
public Object getData(String dataId)
{
Object data = myCurrentView.getData(dataId);
if(data != null)
return data;
if(PlatformDataKeys.HELP_ID.is(dataId))
{
return HelpID.ANT;
}
else if(PlatformDataKeys.TREE_EXPANDER.is(dataId))
{
return myTreeExpander;
}
return null;
}
private static AntMessage createErrorMessage(MessageType type, int priority, String text)
{
if(text.startsWith(FILE_PREFIX))
{
text = text.substring(FILE_PREFIX.length());
}
int afterLineNumberIndex = text.indexOf(": "); // end of file_name_and_line_number sequence
if(afterLineNumberIndex != -1)
{
String fileAndLineNumber = text.substring(0, afterLineNumberIndex);
int index = fileAndLineNumber.lastIndexOf(':');
if(index != -1)
{
String fileName = fileAndLineNumber.substring(0, index);
String lineNumberStr = fileAndLineNumber.substring(index + 1, fileAndLineNumber.length()).trim();
try
{
int line = Integer.parseInt(lineNumberStr);
final File file = new File(fileName);
final VirtualFile result = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>()
{
public VirtualFile compute()
{
String url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, file.getAbsolutePath().replace(File.separatorChar, '/'));
return VirtualFileManager.getInstance().findFileByUrl(url);
}
});
// convert separators
text = fileName.replace('/', File.separatorChar) + ':' + line + text.substring(afterLineNumberIndex);
return new AntMessage(type, priority, text, result, line, 1);
}
catch(NumberFormatException ignored)
{
}
}
}
return new AntMessage(type, priority, text, null, 0, 0);
}
private void addMessage(MessageType type, int priority, String text, VirtualFile file, int line, int column)
{
AntMessage message = new AntMessage(type, priority, text, file, line, column);
addCommand(new AddMessageCommand(message));
}
public void outputJavacMessage(MessageType type, String[] text, VirtualFile file, String url, int line, int column)
{
int priority = type == MessageType.ERROR ? PRIORITY_ERR : PRIORITY_VERBOSE;
updateErrorAndWarningCounters(priority);
AntMessage message = new AntMessage(type, priority, text, file, line, column);
addCommand(new AddJavacMessageCommand(message, url));
if(type == MessageType.ERROR)
{
WolfTheProblemSolver wolf = WolfTheProblemSolver.getInstance(myProject);
wolf.queue(file);
}
}
private JComponent getComponent()
{
return this;
}
public void emptyAll()
{
myLog.clear();
myCommandsProcessedCount = 0;
myErrorCount = 0;
myWarningCount = 0;
myPlainTextView.clearAllMessages();
myTreeView.clearAllMessages();
}
private void collapseAll()
{
myTreeView.collapseAll();
}
private void expandAll()
{
myTreeView.expandAll();
}
private static final class CloseListener extends ContentManagerAdapter implements ProjectManagerListener
{
private Content myContent;
private boolean myCloseAllowed = false;
private final ContentManager myContentManager;
private final Project myProject;
private CloseListener(Content content, ContentManager contentManager, Project project)
{
myContent = content;
myContentManager = contentManager;
myProject = project;
contentManager.addContentManagerListener(this);
ProjectManager.getInstance().addProjectManagerListener(myProject, this);
}
public void contentRemoved(ContentManagerEvent event)
{
if(event.getContent() == myContent)
{
myContentManager.removeContentManagerListener(this);
AntBuildMessageView buildMessageView = myContent.getUserData(KEY);
if(!myCloseAllowed)
{
buildMessageView.stopProcess();
}
ProjectManager.getInstance().removeProjectManagerListener(myProject, this);
myContent.release();
myContent = null;
buildMessageView.myBuildFile = null;
buildMessageView.myPlainTextView.dispose();
}
}
public void contentRemoveQuery(ContentManagerEvent event)
{
if(event.getContent() == myContent)
{
boolean canClose = closeQuery();
if(!canClose)
{
event.consume();
}
}
}
public void projectOpened(Project project)
{
}
public void projectClosed(Project project)
{
if(myContent != null)
{
myContentManager.removeContent(myContent, true);
}
}
public void projectClosing(Project project)
{
}
public boolean canCloseProject(Project project)
{
return closeQuery();
}
/**
* @return true if content can be closed
*/
private boolean closeQuery()
{
if(myContent == null)
{
return true;
}
AntBuildMessageView messageView = myContent.getUserData(KEY);
if(messageView.isStoppedOrTerminateRequested())
{
return true;
}
if(myCloseAllowed)
return true;
int result = Messages.showYesNoCancelDialog(ThermitBundle.message("ant.process.is.active.terminate.confirmation.text"), ThermitBundle.message("close.ant.build.messages.dialog.title"), Messages.getQuestionIcon());
if(result == 0)
{ // yes
messageView.stopProcess();
myCloseAllowed = true;
return true;
}
if(result == 1)
{ // no
// close content and leave the process running
myCloseAllowed = true;
return true;
}
return false;
}
}
private abstract static class LogCommand
{
private final int myPriority;
LogCommand(int priority)
{
myPriority = priority;
}
final int getPriority()
{
return myPriority;
}
abstract void execute(AntOutputView outputView);
}
private static final class StartBuildCommand extends LogCommand
{
private final AntMessage myMessage;
StartBuildCommand(String buildName)
{
super(0);
myMessage = new AntMessage(MessageType.BUILD, 0, buildName, null, 0, 0);
}
void execute(AntOutputView outputView)
{
outputView.startBuild(myMessage);
}
}
private static final class BuildFailedCommand extends LogCommand
{
private final AntMessage myMessage;
BuildFailedCommand(String buildName)
{
super(0);
myMessage = new AntMessage(MessageType.ERROR, 0, ThermitBundle.message("cannot.start.build.name.error.message", buildName), null, 0, 0);
}
void execute(AntOutputView outputView)
{
outputView.buildFailed(myMessage);
}
}
private static final class FinishBuildCommand extends LogCommand
{
private final String myFinishStatusText;
FinishBuildCommand(String finishStatusText)
{
super(0);
myFinishStatusText = finishStatusText;
}
void execute(AntOutputView outputView)
{
outputView.finishBuild(myFinishStatusText);
}
}
private static final class StartTargetCommand extends LogCommand
{
private final AntMessage myMessage;
StartTargetCommand(String targetName)
{
super(0);
myMessage = new AntMessage(MessageType.TARGET, 0, targetName, null, 0, 0);
}
void execute(AntOutputView outputView)
{
outputView.startTarget(myMessage);
}
}
private static final class FinishTargetCommand extends LogCommand
{
FinishTargetCommand()
{
super(0);
}
void execute(AntOutputView outputView)
{
outputView.finishTarget();
}
}
private static final class StartTaskCommand extends LogCommand
{
private final AntMessage myMessage;
StartTaskCommand(String taskName)
{
super(0);
myMessage = new AntMessage(MessageType.TASK, 0, taskName, null, 0, 0);
}
void execute(AntOutputView outputView)
{
outputView.startTask(myMessage);
}
}
private static final class FinishTaskCommand extends LogCommand
{
FinishTaskCommand()
{
super(0);
}
public void execute(AntOutputView outputView)
{
outputView.finishTask();
}
}
private static final class AddMessageCommand extends LogCommand
{
final AntMessage myAntMessage;
AddMessageCommand(AntMessage antMessage)
{
super(antMessage.getPriority());
myAntMessage = antMessage;
}
void execute(AntOutputView outputView)
{
outputView.addMessage(myAntMessage);
}
}
private final class AddExceptionCommand extends LogCommand
{
private final AntMessage myAntMessage;
AddExceptionCommand(AntMessage antMessage)
{
super(antMessage.getPriority());
myAntMessage = antMessage;
}
void execute(AntOutputView outputView)
{
outputView.addException(myAntMessage, isVerboseMode());
}
}
private static final class AddJavacMessageCommand extends LogCommand
{
private final String myUrl;
private final AntMessage myAntMessage;
AddJavacMessageCommand(AntMessage antMessage, String url)
{
super(antMessage.getPriority());
myAntMessage = antMessage;
myUrl = url;
}
void execute(AntOutputView outputView)
{
outputView.addJavacMessage(myAntMessage, myUrl);
}
}
public String[] getTargets()
{
return myTargets;
}
private int getErrorCount()
{
return myErrorCount;
}
private int getWarningCount()
{
return myWarningCount;
}
void buildFinished(boolean isProgressAborted, long buildTimeInMilliseconds, @NotNull final AntBuildListener antBuildListener, OutputPacketProcessor dispatcher)
{
final boolean aborted = isProgressAborted || myIsAborted;
final String message = getFinishStatusText(aborted, buildTimeInMilliseconds);
dispatcher.processOutput(new Printable()
{
@Override
public void printOn(Printer printer)
{
if(!myProject.isDisposed())
{ // if not disposed
addCommand(new FinishBuildCommand(message));
final StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
if(statusBar != null)
{
statusBar.setInfo(message);
}
}
}
});
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
if(!myIsOutputPaused)
{
new OutputFlusher().doFlush();
}
final AntBuildFileBase buildFile = myBuildFile;
if(buildFile != null)
{
if(getErrorCount() == 0 && buildFile.isViewClosedWhenNoErrors())
{
close();
}
else if(getErrorCount() > 0)
{
myTreeView.scrollToFirstError();
}
else
{
myTreeView.scrollToStatus();
}
}
else
{
myTreeView.scrollToLastMessage();
}
VirtualFileManager.getInstance().refresh(true, new Runnable()
{
public void run()
{
antBuildListener.buildFinished(aborted ? AntBuildListener.ABORTED : AntBuildListener.FINISHED_SUCCESSFULLY, getErrorCount());
}
});
}
});
}
public String getFinishStatusText(boolean isAborted, long buildTimeInMilliseconds)
{
int errors = getErrorCount();
int warnings = getWarningCount();
final String theDateAsString = DateFormatUtil.formatDateTime(Clock.getTime());
String formattedBuildTime = formatBuildTime(buildTimeInMilliseconds / 1000);
if(isAborted)
{
return ThermitBundle.message("build.finished.status.ant.build.aborted", formattedBuildTime, theDateAsString);
}
else if(errors == 0 && warnings == 0)
{
return ThermitBundle.message("build.finished.status.ant.build.completed.successfully", formattedBuildTime, theDateAsString);
}
else if(errors == 0)
{
return ThermitBundle.message("build.finished.status.ant.build.completed.with.warnings", warnings, formattedBuildTime, theDateAsString);
}
else
{
return ThermitBundle.message("build.finished.status.ant.build.completed.with.errors.warnings", errors, warnings, formattedBuildTime, theDateAsString);
}
}
private static String formatBuildTime(long seconds)
{
if(seconds == 0)
{
return "0s";
}
final StringBuilder sb = new StringBuilder();
if(seconds >= 3600)
{
sb.append(seconds / 3600).append("h ");
seconds %= 3600;
}
if(seconds >= 60 || sb.length() > 0)
{
sb.append(seconds / 60).append("m ");
seconds %= 60;
}
if(seconds > 0 || sb.length() > 0)
{
sb.append(seconds).append("s");
}
return sb.toString();
}
public boolean isOutputPaused()
{
return myIsOutputPaused;
}
public synchronized void setOutputPaused(boolean outputPaused)
{
if(outputPaused == myIsOutputPaused)
return;
if(myIsOutputPaused)
{
new OutputFlusher().doFlush();
}
myIsOutputPaused = outputPaused;
}
private class OutputFlusher
{
private final ArrayList<AntMessage> myDelayedMessages = new ArrayList<AntMessage>();
public void doFlush()
{
int currentProcessedCount = myCommandsProcessedCount;
while(currentProcessedCount < myLog.size())
{
final LogCommand command = myLog.get(currentProcessedCount++);
proceedOneCommand(command);
}
myCommandsProcessedCount = currentProcessedCount;
flushDelayedMessages();
}
protected final void proceedOneCommand(LogCommand command)
{
if(command.getPriority() > myPriorityThreshold)
return;
// proceed messages in a special way
if(command instanceof AddMessageCommand)
{
AddMessageCommand addMessageCommand = (AddMessageCommand) command;
myDelayedMessages.add(addMessageCommand.myAntMessage);
}
else
{
flushDelayedMessages(); // message type changed -> flush
command.execute(myTreeView);
command.execute(myPlainTextView);
}
}
protected final void flushDelayedMessages()
{
if(!myDelayedMessages.isEmpty())
{
AntMessage[] messages = myDelayedMessages.toArray(new AntMessage[myDelayedMessages.size()]);
myDelayedMessages.clear();
myTreeView.addMessages(messages);
myPlainTextView.addMessages(messages);
}
}
}
public String getNextOccurenceActionName()
{
return myTreeView.getNextOccurenceActionName();
}
public String getPreviousOccurenceActionName()
{
return myTreeView.getPreviousOccurenceActionName();
}
public OccurenceInfo goNextOccurence()
{
return isTreeView() ? myTreeView.goNextOccurence() : null;
}
public OccurenceInfo goPreviousOccurence()
{
return isTreeView() ? myTreeView.goPreviousOccurence() : null;
}
public boolean hasNextOccurence()
{
return isTreeView() && myTreeView.hasNextOccurence();
}
public boolean hasPreviousOccurence()
{
return isTreeView() && myTreeView.hasPreviousOccurence();
}
public void setBuildCommandLine(String commandLine)
{
myPlainTextView.setBuildCommandLine(commandLine);
}
}