/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.tvl.goworks.project;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.antlr.netbeans.util.NotificationIcons;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.extexecution.ExecutionDescriptor;
import org.netbeans.api.extexecution.print.ConvertedLine;
import org.netbeans.api.extexecution.print.LineConvertor;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment;
import org.netbeans.modules.nativeexecution.api.ExecutionListener;
import org.netbeans.modules.nativeexecution.api.NativeProcess;
import org.netbeans.modules.nativeexecution.api.NativeProcessBuilder;
import org.netbeans.modules.nativeexecution.api.NativeProcessChangeEvent;
import org.netbeans.modules.nativeexecution.api.execution.NativeExecutionDescriptor;
import org.netbeans.modules.nativeexecution.api.execution.NativeExecutionService;
import org.netbeans.modules.nativeexecution.api.execution.PostMessageDisplayer;
import org.netbeans.modules.nativeexecution.spi.ExecutionEnvironmentFactoryService;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
import org.openide.awt.NotificationDisplayer;
import org.openide.awt.StatusDisplayer;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.text.Line;
import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.NotImplementedException;
import org.openide.util.Parameters;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;
import org.openide.windows.IOProvider;
import org.openide.windows.InputOutput;
import org.openide.windows.OutputEvent;
import org.openide.windows.OutputListener;
import org.tvl.goworks.project.testing.GoTestOutputWriter;
/**
*
* @author Sam Harwell
*/
public final class GoActionProvider implements ActionProvider {
// -J-Dorg.tvl.goworks.project.GoActionProvider.level=FINE
private static final Logger LOGGER = Logger.getLogger(GoActionProvider.class.getName());
@StaticResource
private static final String STOP_IMAGE = "org/tvl/goworks/project/ui/resources/stop.png";
private static final RequestProcessor RP = new RequestProcessor(GoActionProvider.class);
private static final String[] supported = new String[] {
ActionProvider.COMMAND_BUILD,
ActionProvider.COMMAND_COMPILE_SINGLE,
ActionProvider.COMMAND_REBUILD,
ActionProvider.COMMAND_CLEAN,
ActionProvider.COMMAND_RUN,
ActionProvider.COMMAND_DEBUG,
ActionProvider.COMMAND_PROFILE,
ActionProvider.COMMAND_TEST,
ActionProvider.COMMAND_TEST_SINGLE,
ActionProvider.COMMAND_DELETE,
ActionProvider.COMMAND_COPY
};
private final GoProject _project;
private final List<ExecutionListener> listeners = new CopyOnWriteArrayList<>();
private WeakReference<Future<Integer>> _running;
public GoActionProvider(final GoProject project) {
this._project = project;
}
@Override
public String[] getSupportedActions() {
return supported;
}
@Override
public void invokeAction(String string, Lookup lookup) throws IllegalArgumentException {
if (string.equals(COMMAND_BUILD)) {
handleBuildAction(lookup);
}
else if (string.equals(COMMAND_COMPILE_SINGLE)) {
handleBuildPackageAction(lookup);
}
else if (string.equals(COMMAND_REBUILD)) {
handleRebuildAction(lookup);
}
else if (string.equals(COMMAND_CLEAN)) {
handleCleanAction(lookup);
}
else if (string.equals(COMMAND_RUN)) {
handleRunAction(lookup);
}
else if (string.equals(COMMAND_DEBUG)) {
handleDebugAction(lookup);
}
else if (string.equals(COMMAND_PROFILE)) {
handleProfileAction(lookup);
}
else if (string.equals(COMMAND_TEST)) {
handleTestAction(lookup);
}
else if (string.equals(COMMAND_TEST_SINGLE)) {
handleTestPackageAction(lookup);
}
else if (string.equalsIgnoreCase(ActionProvider.COMMAND_DELETE)) {
DefaultProjectOperations.performDefaultDeleteOperation(_project);
}
else if (string.equalsIgnoreCase(ActionProvider.COMMAND_COPY)) {
DefaultProjectOperations.performDefaultCopyOperation(_project);
}
}
private void handleBuildAction(Lookup lookup) throws IllegalArgumentException {
String buildCommand = "go";
String args = "build";
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (build)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
};
tab = IOProvider.getDefault().getIO(projectName + " (build)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (build)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
execute("build", ioTab, progressHandle);
}
private void handleBuildPackageAction(Lookup lookup) throws IllegalArgumentException {
String buildCommand = "go";
String args = "build";
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (compile-single)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
};
tab = IOProvider.getDefault().getIO(projectName + " (compile-single)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (compile-single)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
DataObject dataFolder = lookup.lookup(DataObject.class);
if (dataFolder == null) {
return;
}
FileObject fileObject = dataFolder.getPrimaryFile();
if (fileObject == null || !fileObject.isFolder()) {
return;
}
FileObject sourceRoot = _project.getSourceRoot();
String relativePath = FileUtil.getRelativePath(sourceRoot, fileObject);
if (relativePath == null) {
return;
}
execute("build", ioTab, relativePath.replace(File.separatorChar, '/'), progressHandle);
}
public void execute(String commandName, InputOutput io, ProgressHandle progressHandle) {
execute(commandName, io, "./...", progressHandle);
}
public void execute(final String commandName, final InputOutput io, final String packageName, final ProgressHandle progressHandle) {
final ExecutionEventListener standardListener = new ExecutionEventListener(progressHandle, io);
final ExecutionListener listener = new ExecutionListener() {
@Override
public void executionStarted(int pid) {
standardListener.executionStarted(pid);
for (ExecutionListener listener : listeners) {
listener.executionStarted(pid);
}
}
@Override
public void executionFinished(int rc) {
standardListener.executionFinished(rc);
for (ExecutionListener listener : listeners) {
listener.executionFinished(rc);
}
}
};
final ExecutionListener firstListener = new ExecutionListener() {
@Override
public void executionStarted(int pid) {
for (ExecutionListener listener : listeners) {
listener.executionStarted(pid);
}
}
@Override
public void executionFinished(int rc) {
for (ExecutionListener listener : listeners) {
listener.executionFinished(rc);
}
}
};
Runnable executor = new Runnable() {
@Override
public void run() {
try {
switch (commandName) {
case COMMAND_REBUILD:
{
Future<Integer> result;
result = executeImpl(COMMAND_CLEAN, packageName, io, firstListener);
if (result.get() != 0) {
return;
} executeImpl(COMMAND_BUILD, packageName, io, listener);
break;
}
case COMMAND_RUN:
{
Future<Integer> result;
result = executeImpl(COMMAND_BUILD, packageName, io, firstListener);
if (result.get() != 0) {
return;
} executeImpl(COMMAND_RUN, packageName, io, listener);
break;
}
default:
executeImpl(commandName, packageName, io, listener);
break;
}
} catch (InterruptedException | ExecutionException throwable) {
try {
io.getErr().println("Internal error occurred. Please report a bug.", null, true);
} catch (IOException ex) {
}
io.getOut().close();
listener.executionFinished(-1);
throw new RuntimeException(throwable);
}
}
};
RP.post(executor);
}
private static final String PACKAGE_NAME_PATTERN_STRING = "\\w+(?:/\\w+)*";
private static final Pattern PACKAGE_NAME_PATTERN = Pattern.compile(PACKAGE_NAME_PATTERN_STRING);
private static final Pattern BUILD_ERROR_PATTERN = Pattern.compile("(\\w+(?:[\\\\/]\\w+)*[\\\\/]\\w+\\.go):(\\d+):\\s*(.*)");
private Future<Integer> executeImpl(final String commandName, final String packageName, final InputOutput io, final ExecutionListener listener) {
if (COMMAND_REBUILD.equals(commandName)) {
throw new IllegalArgumentException("Expected 'clean' and 'build' as separate commands.");
}
ExecutionEnvironmentFactoryService executionEnvironmentFactoryService =
Lookup.getDefault().lookup(ExecutionEnvironmentFactoryService.class);
final ExecutionEnvironment executionEnvironment = executionEnvironmentFactoryService.getLocal();
final String workingDirectory = _project.getProjectDirectory().getPath().replace('/', File.separatorChar);
boolean showInput = COMMAND_RUN.equals(commandName);
final boolean unbuffer = true;
final boolean statusEx = false;
LineConvertor convertor = new LineConvertor() {
@Override
public List<ConvertedLine> convert(String line) {
if (COMMAND_BUILD.equals(commandName) || COMMAND_COMPILE_SINGLE.equals(commandName)) {
String action = "Building";
if (PACKAGE_NAME_PATTERN.matcher(line).matches()) {
return Collections.singletonList(ConvertedLine.forText(action + " package " + line, null));
} else {
Matcher matcher = BUILD_ERROR_PATTERN.matcher(line);
if (matcher.matches()) {
return Collections.singletonList(ConvertedLine.forText(line, new BuildErrorOutputListener()));
}
}
}
return Collections.singletonList(ConvertedLine.forText(line, null));
}
};
List<Writer> outputHandlers = new ArrayList<>();
if ("test".equals(commandName)) {
outputHandlers.add(new GoTestOutputWriter(_project));
}
Writer outputListener = null;
if (!outputHandlers.isEmpty()) {
outputListener = new WriterRedirector(outputHandlers);
}
final ProcessChangeListener processChangeListener = new ProcessChangeListener(listener, outputListener, convertor, io);
FileObject executable;
List<String> args = new ArrayList<>();
if (COMMAND_RUN.equals(commandName)) {
FileObject projectDirectory = _project.getProjectDirectory();
if (projectDirectory == null || !projectDirectory.isFolder()) {
throw new UnsupportedOperationException("Couldn't determine the project directory.");
}
FileObject binFolder = projectDirectory.getFileObject("bin");
if (binFolder == null || !binFolder.isFolder()) {
displayError(commandName, String.format("The build directory could not be found. Expected: %s%sbin", projectDirectory.getPath(), File.separatorChar));
throw new UnsupportedOperationException("Couldn't determine build directory.");
}
String binaryName = null;
String[] projectBinaries = getProjectBinaries();
if (projectBinaries.length == 0) {
displayError(commandName, "Couldn't identify a binary produced by this project.");
throw new UnsupportedOperationException("Couldn't identify a binary produced by this project.");
}
binaryName = projectBinaries[0];
if (projectBinaries.length > 1) {
displayWarning(commandName, String.format("Found multiple binaries produced by this project; using '%s'. All binaries: %s", binaryName, Arrays.toString(projectBinaries)));
}
executable = binFolder.getFileObject(binaryName, "");
if (executable == null && Utilities.isWindows()) {
executable = binFolder.getFileObject(binaryName, "exe");
}
if (executable == null || !executable.isData()) {
String extension = Utilities.isWindows() ? ".exe" : "";
String expected = projectDirectory.getPath() + "/bin/" + binaryName + extension;
if (File.separatorChar != '/') {
expected = expected.replace('/', File.separatorChar);
}
displayError(commandName, String.format("Couldn't find the target executable. Expected: %s", expected));
throw new UnsupportedOperationException("Couldn't find the target executable.");
}
} else {
File goroot = new File(System.getenv("GOROOT"));
if (!goroot.isDirectory()) {
displayError(commandName, "The GOROOT environment variable does not point to an accessible Go installation.");
throw new UnsupportedOperationException("Couldn't determine GOROOT.");
}
FileObject gorootObject = FileUtil.toFileObject(goroot);
if (gorootObject == null || !gorootObject.isFolder()) {
throw new UnsupportedOperationException("Couldn't determine GOROOT.");
}
FileObject binFolder = gorootObject.getFileObject("bin");
if (binFolder == null || !binFolder.isFolder()) {
displayError(commandName, String.format("The Go installation directory environment variable does not contain a 'bin' directory. Expected: %s%sbin", gorootObject.getPath(), File.separatorChar));
throw new UnsupportedOperationException("Couldn't determine Go bin directory.");
}
executable = binFolder.getFileObject("go", "");
if (executable == null && Utilities.isWindows()) {
executable = binFolder.getFileObject("go", "exe");
}
if (executable == null || !executable.isData()) {
String extension = Utilities.isWindows() ? ".exe" : "";
String expected = gorootObject.getPath() + File.separator + "bin" + File.separator + "go" + extension;
if (File.separatorChar != '/') {
expected = expected.replace('/', File.separatorChar);
}
displayError(commandName, String.format("Couldn't find the Go tool. Expected: %s", expected));
throw new UnsupportedOperationException("Couldn't find the Go tool.");
}
switch (commandName) {
case COMMAND_BUILD:
case COMMAND_COMPILE_SINGLE:
args.add("install");
args.add("-v");
args.add(packageName);
break;
case COMMAND_CLEAN:
args.add("clean");
args.add("-i");
args.add("-x");
args.add(packageName);
break;
case COMMAND_TEST:
case COMMAND_TEST_SINGLE:
args.add("test");
args.add("-v");
args.add(packageName);
break;
}
}
NativeProcessBuilder nativeProcessBuilder = NativeProcessBuilder.newProcessBuilder(executionEnvironment)
.setWorkingDirectory(workingDirectory)
.unbufferOutput(unbuffer)
.setStatusEx(statusEx)
.addNativeProcessListener(processChangeListener);
nativeProcessBuilder.setExecutable(executable.getPath()).setArguments(args.toArray(new String[args.size()]));
nativeProcessBuilder.getEnvironment().put("GOPATH", workingDirectory);
io.getOut().print(executable.getPath());
for (String arg : args) {
io.getOut().print(" " + arg);
}
io.getOut().print("\n");
nativeProcessBuilder.redirectError();
NativeExecutionDescriptor descriptor =
new NativeExecutionDescriptor()
.controllable(false) // don't enable the rerun or stop buttons in the IO tab
.frontWindow(true) // select the IO tab before execution
.inputVisible(showInput) // true to allow input from user
.inputOutput(io)
.outLineBased(!unbuffer)
.showProgress(false)
.postMessageDisplayer(new PostMessageDisplayer.Default(commandName))
.postExecution(processChangeListener)
.errConvertorFactory(processChangeListener)
.outConvertorFactory(processChangeListener)
.keepInputOutputOnFinish();
descriptor.noReset(true);
NativeExecutionService es = NativeExecutionService.newService(nativeProcessBuilder, descriptor, commandName);
Future<Integer> future = es.run();
_running = new WeakReference<>(future);
return future;
}
private String[] getProjectBinaries() {
Lookup lookup = MimeLookup.getLookup("text/x-go");
ProjectBinaryResolver resolver = lookup.lookup(ProjectBinaryResolver.class);
String[] result = resolver.findProjectBinaries(_project);
if (result == null) {
return new String[0];
}
return result;
}
private void displayWarning(String command, String message) {
String title;
if (COMMAND_RUN.equals(command)) {
title = "Running the project";
} else {
title = String.format("Executing \"go %s\"", command);
}
NotificationDisplayer.getDefault().notify(title, NotificationIcons.WARNING, message, null);
}
private void displayError(String command, String message) {
String title;
if (COMMAND_RUN.equals(command)) {
title = "Error running the project";
} else {
title = String.format("Error executing \"go %s\"", command);
}
NotificationDisplayer.getDefault().notify(title, NotificationIcons.ERROR, message, null);
}
private static class ExecutionEventListener implements ExecutionListener {
private final ProgressHandle progressHandle;
private final InputOutput io;
public ExecutionEventListener(ProgressHandle progressHandle, InputOutput io) {
this.progressHandle = progressHandle;
this.io = io;
}
@Override
public void executionStarted(int pid) {
}
@Override
public void executionFinished(int rc) {
if (progressHandle != null) {
progressHandle.finish();
}
if (io != null) {
io.getOut().close();
}
}
}
private static class ProcessChangeListener implements ChangeListener, Runnable, ExecutionDescriptor.LineConvertorFactory {
private final AtomicReference<NativeProcess> processRef = new AtomicReference<>();
private final ExecutionListener listener;
private Writer outputListener;
private final LineConvertor lineConvertor;
public ProcessChangeListener(ExecutionListener listener, Writer outputListener, LineConvertor lineConvertor, InputOutput tab) {
this.listener = listener;
this.outputListener = outputListener;
this.lineConvertor = lineConvertor;
}
@Override
public void stateChanged(ChangeEvent e) {
if (!(e instanceof NativeProcessChangeEvent)) {
return;
}
final NativeProcessChangeEvent event = (NativeProcessChangeEvent)e;
processRef.compareAndSet(null, (NativeProcess)event.getSource());
if (event.state == NativeProcess.State.RUNNING) {
if (listener != null) {
listener.executionStarted(event.pid);
}
}
}
@Override
public void run() {
if (outputListener != null) {
try {
outputListener.flush();
outputListener.close();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
outputListener = null;
}
NativeProcess process = processRef.get();
if (process != null && listener != null) {
listener.executionFinished(process.exitValue());
}
}
@Override
public LineConvertor newLineConvertor() {
return new LineConvertor() {
@Override
public List<ConvertedLine> convert(String line) {
return ProcessChangeListener.this.convert(line);
}
};
}
private synchronized List<ConvertedLine> convert(String line) {
if (outputListener != null) {
try {
outputListener.write(line);
outputListener.write("\n");
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
if (lineConvertor != null) {
return lineConvertor.convert(line);
}
return null;
}
}
private void handleRebuildAction(Lookup lookup) throws IllegalArgumentException {
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (rebuild)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
};
tab = IOProvider.getDefault().getIO(projectName + " (rebuild)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (rebuild)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
execute(COMMAND_REBUILD, ioTab, progressHandle);
}
private void handleCleanAction(Lookup lookup) throws IllegalArgumentException {
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (clean)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
};
tab = IOProvider.getDefault().getIO(projectName + " (clean)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (clean)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
execute(COMMAND_CLEAN, ioTab, progressHandle);
}
private void handleRunAction(Lookup lookup) throws IllegalArgumentException {
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (run)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
new AbstractAction("Stop", new ImageIcon(ImageUtilities.loadImage(STOP_IMAGE, false))) {
@Override
public void actionPerformed(ActionEvent e) {
WeakReference<Future<Integer>> weakFuture = _running;
Future<Integer> future = weakFuture != null ? weakFuture.get() : null;
if (future != null && !future.isCancelled() && !future.isDone()) {
future.cancel(true);
}
}
}
};
tab = IOProvider.getDefault().getIO(projectName + " (run)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (run)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
execute(COMMAND_RUN, ioTab, progressHandle);
}
private void handleDebugAction(Lookup lookup) throws IllegalArgumentException {
throw new NotImplementedException();
}
private void handleProfileAction(Lookup lookup) throws IllegalArgumentException {
throw new NotImplementedException();
}
private void handleTestAction(Lookup lookup) throws IllegalArgumentException {
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (test)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
};
tab = IOProvider.getDefault().getIO(projectName + " (test)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (test)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
execute(COMMAND_TEST, ioTab, progressHandle);
}
private void handleTestPackageAction(Lookup lookup) throws IllegalArgumentException {
String projectName = ProjectUtils.getInformation(_project).getDisplayName();
InputOutput tab;
tab = IOProvider.getDefault().getIO(projectName + " (test-single)", false);
tab.closeInputOutput();
Action[] actions = new Action[] {
};
tab = IOProvider.getDefault().getIO(projectName + " (test-single)", actions);
try {
tab.getOut().reset();
} catch (IOException ex) {
}
final InputOutput ioTab = tab;
ProgressHandle progressHandle = ProgressHandleFactory.createHandle(projectName + " (test-single)", new Cancellable() {
@Override
public boolean cancel() {
return false;
}
}, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
ioTab.select();
}
});
progressHandle.setInitialDelay(0);
progressHandle.start();
DataObject dataFolder = lookup.lookup(DataObject.class);
if (dataFolder == null) {
return;
}
FileObject fileObject = dataFolder.getPrimaryFile();
if (fileObject == null || !fileObject.isFolder()) {
return;
}
FileObject sourceRoot = _project.getSourceRoot();
String relativePath = FileUtil.getRelativePath(sourceRoot, fileObject);
if (relativePath == null) {
return;
}
execute(COMMAND_TEST, ioTab, relativePath.replace(File.separatorChar, '/'), progressHandle);
}
@Override
public boolean isActionEnabled(String command, Lookup lookup) throws IllegalArgumentException {
if (_project == null) {
return false;
}
switch (command) {
case ActionProvider.COMMAND_BUILD:
case ActionProvider.COMMAND_TEST:
return !_project.isStandardLibrary();
case ActionProvider.COMMAND_COMPILE_SINGLE:
case ActionProvider.COMMAND_TEST_SINGLE:
if (_project.isStandardLibrary()) {
return false;
}
DataObject dataFolder = lookup.lookup(DataObject.class);
if (dataFolder == null) {
return false;
}
FileObject fileObject = dataFolder.getPrimaryFile();
if (fileObject == null || !fileObject.isFolder()) {
return false;
}
FileObject sourceRoot = _project.getSourceRoot();
String relativePath = FileUtil.getRelativePath(sourceRoot, fileObject);
if (relativePath == null) {
return false;
}
return true;
case ActionProvider.COMMAND_REBUILD:
return !_project.isStandardLibrary();
case ActionProvider.COMMAND_CLEAN:
return !_project.isStandardLibrary();
case ActionProvider.COMMAND_RUN:
return !_project.isStandardLibrary();
case ActionProvider.COMMAND_DEBUG:
return false;
case ActionProvider.COMMAND_PROFILE:
return false;
case ActionProvider.COMMAND_DELETE:
return !_project.isStandardLibrary();
case ActionProvider.COMMAND_COPY:
return !_project.isStandardLibrary();
default:
throw new IllegalArgumentException(command);
}
}
private class BuildErrorOutputListener implements OutputListener {
@Override
public void outputLineSelected(OutputEvent ev) {
}
@Override
public void outputLineAction(OutputEvent ev) {
Matcher matcher = BUILD_ERROR_PATTERN.matcher(ev.getLine());
if (!matcher.matches()) {
return;
}
final String workingDirectory = _project.getProjectDirectory().getPath().replace('/', File.separatorChar);
String filePath = workingDirectory + File.separatorChar + matcher.group(1).replace('/', File.separatorChar);
String message = matcher.group(3);
int line = Integer.parseInt(matcher.group(2));
final int column = -1;
FileObject file = FileUtil.toFileObject(new File(filePath));
if (file == null) { // #13115
Toolkit.getDefaultToolkit().beep();
return;
}
try {
DataObject dob = DataObject.find(file);
EditorCookie ed = dob.getLookup().lookup(EditorCookie.class);
if (ed != null && /* not true e.g. for *_ja.properties */file == dob.getPrimaryFile()) {
if (line <= 0) {
// OK, just open it.
ed.open();
} else {
ed.openDocument(); // XXX getLineSet does not do it for you!
LOGGER.log(Level.FINE, "opened document for {0}", file);
try {
Line.Set lines = ed.getLineSet();
final Line editorLine = lines.getOriginal(line - 1);
if (!editorLine.isDeleted()) {
EventQueue.invokeLater(new Runnable() {
public @Override void run() {
editorLine.show(Line.ShowOpenType.REUSE, Line.ShowVisibilityType.FOCUS, column);
}
});
}
} catch (IndexOutOfBoundsException ioobe) {
// Probably harmless. Bogus line number.
ed.open();
}
}
} else {
Toolkit.getDefaultToolkit().beep();
}
} catch (DataObjectNotFoundException donfe) {
LOGGER.log(Level.WARNING, donfe.getLocalizedMessage(), donfe);
}
catch (IOException ioe) {
// XXX see above, should not be necessary to call openDocument at all
LOGGER.log(Level.WARNING, ioe.getLocalizedMessage(), ioe);
}
if (message != null) {
// Try to do after opening the file, since opening a new file
// clears the current status message.
StatusDisplayer.getDefault().setStatusText(message);
}
}
@Override
public void outputLineCleared(OutputEvent ev) {
}
}
protected static class WriterRedirector extends Writer {
private final List<Writer> _writers;
public WriterRedirector(@NonNull Collection<? extends Writer> writers) {
Parameters.notNull("writers", writers);
_writers = new ArrayList<>(writers);
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
for (Writer writer : _writers) {
writer.write(cbuf, off, len);
}
}
@Override
public void flush() throws IOException {
for (Writer writer : _writers) {
writer.flush();
}
}
@Override
public void close() throws IOException {
for (Writer writer : _writers) {
writer.close();
}
}
}
}