/*
* $Id$
*
* Copyright (c) 2004-2005 by the TeXlapse Team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package net.sourceforge.texlipse.builder;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import net.sourceforge.texlipse.PathUtils;
import net.sourceforge.texlipse.TexlipsePlugin;
import net.sourceforge.texlipse.properties.TexlipseProperties;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.texteditor.MarkerUtilities;
/**
* Helper methods for external program runners.
*
* @author Kimmo Karlsson
*/
public abstract class AbstractProgramRunner implements ProgramRunner {
// the currently running program
private ExternalProgram extrun;
/**
* Create a new program runner.
* @param project the project holding the properties
* @param propertyName name of the property containing the name of the program
*/
protected AbstractProgramRunner() {
extrun = new ExternalProgram();
}
/**
* @return the name of the program runner arguments -preference in the plugin preferences
*/
private String getArgumentsPreferenceName() {
return getClass() + "_args";
}
/**
* @return the name of the program runner path -preference in the plugin preferences
*/
private String getCommandPreferenceName() {
return getClass() + "_prog";
}
/**
* @return the program path and filename from the preferences
*/
public String getProgramPath() {
return TexlipsePlugin.getPreference(getCommandPreferenceName());
}
/**
* @param path the program path and filename for the preferences
*/
public void setProgramPath(String path) {
TexlipsePlugin.getDefault().getPreferenceStore().setValue(getCommandPreferenceName(), path);
}
/**
* Read the command line arguments for the program from the preferences.
* The input filename is marked with a "%input" and the output file name
* is marked with a "%output".
* @return the command line arguments for the program
*/
public String getProgramArguments() {
return TexlipsePlugin.getPreference(getArgumentsPreferenceName());
}
/**
* @param args the program arguments for the preferences
*/
public void setProgramArguments(String args) {
TexlipsePlugin.getDefault().getPreferenceStore().setValue(getArgumentsPreferenceName(), args);
}
/**
*
*/
public void initializeDefaults(IPreferenceStore pref, String path) {
pref.setDefault(getCommandPreferenceName(), path);
pref.setDefault(getArgumentsPreferenceName(), getDefaultArguments());
}
/**
* Returns the default value for the program arguments -preference.
* @return the program arguments
*/
protected String getDefaultArguments() {
return "%input";
}
/**
* @return the name of the executable program
*/
public String getProgramName() {
String os = System.getProperty("os.name").toLowerCase();
if (os.indexOf("windows") >= 0) {
return getWindowsProgramName();
} else {
return getUnixProgramName();
}
}
protected abstract String getWindowsProgramName();
protected abstract String getUnixProgramName();
/**
* @param resource the input file to be processed
* @return the arguments to give to the external program
*/
protected String getArguments(IResource resource) {
String args = getProgramArguments();
if (args == null) {
return null;
}
String ext = resource.getFileExtension();
String name = resource.getName();
String baseName = name.substring(0, name.length() - ext.length());
String inputName = baseName + getInputFormat();
String outputName = baseName + getOutputFormat();
if (baseName.indexOf(' ') >= 0) {
inputName = "\"" + inputName + "\"";
outputName = "\"" + outputName + "\"";
}
if (args.indexOf("%input") >= 0) {
args = args.replaceAll("%input", inputName);
}
if (args.indexOf("%output") >= 0) {
args = args.replaceAll("%output", outputName);
}
if (args.indexOf("%fullinput") >= 0) {
args = args.replaceAll("%fullinput",
resource.getParent().getLocation().toFile().getAbsolutePath()
+ File.separator + inputName);
}
if (args.indexOf("%fulloutput") >= 0) {
args = args.replaceAll("%fulloutput",
resource.getParent().getLocation().toFile().getAbsolutePath()
+ File.separator + outputName);
}
return args;
}
/**
* Parse errors from the output of an external program.
*
* @param resource the input file that was processed
* @param output the output of the external program
* @return true, if error messages were found in the output, false otherwise
*/
protected abstract boolean parseErrors(IResource resource, String output);
/**
* Check to see if this program is ready for operation.
* @return true if this program exists
*/
public boolean isValid() {
if (getProgramPath() == null) {
return false;
}
File f = new File(getProgramPath());
return f.exists() && f.isFile();
}
/**
* The main method.
*
* @param resource the input file to feed to the external program
* @throws CoreException if the external program is not found
* or if there was an error during the build
*/
public void run(IResource resource) throws CoreException {
File sourceDir = resource.getLocation().toFile().getParentFile();
// find executable file
String programPath = getProgramPath();
File exec = new File(programPath);
if (!exec.exists()) {
throw new CoreException(TexlipsePlugin.stat("External program (" + programPath + ") not found"));
}
// split command into array
ArrayList list = new ArrayList();
list.add(exec.getAbsolutePath());
PathUtils.tokenizeEscapedString(getArguments(resource), list);
String[] command = (String[]) list.toArray(new String[0]);
// check if we are using console
String console = null;
if (TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.BUILDER_CONSOLE_OUTPUT)) {
console = getProgramName();
}
extrun.setup(command, sourceDir, console);
String output = null;
try {
String[] query = getQueryString();
if (query != null) {
output = extrun.run(query);
} else {
output = extrun.run();
}
} catch (Exception e) {
throw new CoreException(new Status(IStatus.ERROR, TexlipsePlugin.getPluginId(),
IStatus.ERROR, "Building the project: ", e));
} finally {
extrun.stop();
}
if (parseErrors(resource, output)) {
throw new BuilderCoreException(TexlipsePlugin.stat("Errors during build. See the problems dialog."));
}
}
/**
* Kill the external program if it is running.
*/
public void stop() {
if (extrun != null) {
extrun.stop();
}
}
/**
* Returns a special query string that indicates that this program is waiting an input from the user.
* @return the query string to look for in the output of the program
*/
protected String[] getQueryString() {
return null;
}
/**
* Create a layout warning marker to the given resource.
*
* @param resource the file where the problem occurred
* @param message error message
* @param lineNumber line number
* @param markerType
* @param severity Severity of the error
*/
@SuppressWarnings("unchecked")
protected static void createMarker(IResource resource,
Integer lineNumber, String message, String markerType, int severity) {
int lineNr = -1;
if (lineNumber != null) {
lineNr = lineNumber;
}
IMarker marker = AbstractProgramRunner.findMarker(resource, lineNr, message, markerType);
if (marker == null) {
try {
HashMap map = new HashMap();
map.put(IMarker.MESSAGE, message);
map.put(IMarker.SEVERITY, new Integer (severity));
if (lineNumber != null)
map.put(IMarker.LINE_NUMBER, lineNumber);
MarkerUtilities.createMarker(resource, map, markerType);
} catch (CoreException e) {
throw new RuntimeException(e);
}
}
}
/**
* Create a layout warning marker to the given resource.
*
* @param resource the file where the problem occured
* @param message error message
* @param lineNumber line number
*/
public static void createLayoutMarker(IResource resource, Integer lineNumber, String message) {
String markerType = TexlipseBuilder.LAYOUT_WARNING_TYPE;
int severity = IMarker.SEVERITY_WARNING;
createMarker(resource, lineNumber, message, markerType, severity);
}
/**
* Create a marker to the given resource.
*
* @param resource the file where the problem occured
* @param message error message
* @param lineNumber line number
* @param severity severity of the marker
*/
public static void createMarker(IResource resource, Integer lineNumber, String message, int severity) {
String markerType = TexlipseBuilder.MARKER_TYPE;
createMarker(resource, lineNumber, message, markerType, severity);
}
/**
* Create a marker to the given resource. The marker's severity will be "ERROR".
*
* @param resource the file where the problem occured
* @param message error message
* @param lineNumber line number
*/
public static void createMarker(IResource resource, Integer lineNumber, String message) {
createMarker(resource, lineNumber, message, IMarker.SEVERITY_ERROR);
}
/**
* Checks pre-existance of marker.
*
* @param resource Resource in which marker will searched
* @param lineNr IMarker.LINE_NUMBER of the marker
* @param message Message for marker
* @param type The type of the marker to find
* @return pre-existance of marker or null if no marker was found
*/
public static IMarker findMarker(IResource resource, int lineNr, String message, String type) {
try {
IMarker[] tasks = resource.findMarkers(type, true, IResource.DEPTH_ZERO);
for (IMarker marker : tasks) {
Object lNrObj = marker.getAttribute(IMarker.LINE_NUMBER);
int lNr = -1;
if (lNrObj != null) {
lNr = ((Integer) lNrObj);
}
if (lNr == lineNr
&& marker.getAttribute(IMarker.MESSAGE).equals(message)) {
return marker;
}
}
} catch (CoreException e) {
throw new RuntimeException(e);
}
return null;
}
}