package ctagsinterface.main;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.gjt.sp.jedit.jEdit;
import org.gjt.sp.util.Log;
import ctagsinterface.options.GeneralOptionPane;
public class Runner
{
private static final int UNKNOWN_NUMBER_OF_FILES = -1;
static public final String MESSAGE = CtagsInterfacePlugin.MESSAGE;
static public final String TAGGING = MESSAGE + "tagging";
private static final String SPACES = "\\s+";
private static Set<String> tempFiles;
private Logger logger;
static {
tempFiles = new HashSet<String>();
}
public Runner(Logger logger)
{
this.logger = logger;
}
// Runs Ctags on a single file. Returns the tag file.
public String runOnFile(String file) {
Vector<String> what = new Vector<String>();
what.add(file);
return run(what, 1);
}
// Runs Ctags on an archive. Returns the tag file.
public String runOnArchive(String archive,
HashMap<String, String> localToVFS)
{
Vector<String> files = VFSHelper.listArchive(archive, true);
String tagFile = getTempFile("tags");
// The same tag file is used for the entire archive. Results
// for each of the source files in the archive are appended
// to the tag file. Hence, if the tag file exists when the
// archive tagging begins, it should be deleted.
new File(tagFile).delete();
int cnt = 0;
for (String file: files) {
String local = runOnVFS(file, tagFile);
if (local != null)
localToVFS.put(local, file);
cnt++;
Log.log(Log.DEBUG, getClass(), "Progress: " + cnt +
" out of " + files.size());
}
return tagFile;
}
// Runs Ctags on a source tree. Returns the tag file.
public String runOnTree(String tree) {
Vector<String> what = new Vector<String>();
what.add("-R");
what.add(tree);
return run(what, UNKNOWN_NUMBER_OF_FILES);
}
// Runs Ctags on a list of files. Returns the tag file.
public String runOnFiles(Vector<String> files) {
String fileList = getTempFile("files");
try {
PrintWriter w = new PrintWriter(new FileWriter(fileList));
for (int i = 0; i < files.size(); i++)
w.println(files.get(i));
w.close();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not create the file list file for Ctags");
return null;
}
Vector<String> what = new Vector<String>();
what.add("-L");
what.add(fileList);
String tagFile = run(what, files.size());
releaseFile(fileList);
return tagFile;
}
// Tag file no longer needed
public void releaseFile(String file) {
synchronized (tempFiles) {
tempFiles.remove(file);
}
}
private File getLocalCopyOfVFS(String vfsPath) {
String sourceFileName = VFSHelper.getFileName(vfsPath);
String prefix = sourceFileName, suffix = "";
int sepPos = sourceFileName.lastIndexOf(".");
if (sepPos >= 0) {
prefix = sourceFileName.substring(0, sepPos);
if (prefix.length() < 3) // Limitation of createTempFile()
prefix = prefix + "000";
suffix = sourceFileName.substring(sepPos);
}
try {
File localCopy = File.createTempFile(prefix, suffix);
localCopy.createNewFile();
VFSHelper.copy(vfsPath, localCopy.getPath());
return localCopy;
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// Runs Ctags on the given VFS file path, by making a local copy
// and running Ctags on it. Returns the name of the local copy.
private String runOnVFS(String path, String tagFile) {
Log.log(Log.DEBUG, getClass(), "Tagging VFS path: '" + path);
long start = System.currentTimeMillis();
String ctags = GeneralOptionPane.getCtags();
String cmd = GeneralOptionPane.getCmd();
File localCopy = getLocalCopyOfVFS(path);
if (localCopy == null) {
Log.log(Log.DEBUG, getClass(), "Tagging aborted - no local copy.");
return null;
}
Log.log(Log.DEBUG, getClass(), "Local copy: '" + localCopy.getPath() + "'");
Vector<String> cmdLine = new Vector<String>();
cmdLine.add(ctags);
cmdLine.add("-f");
cmdLine.add(tagFile);
cmdLine.add("--append=yes");
String [] customOptions = cmd.split(SPACES);
for (int i = 0; i < customOptions.length; i++)
cmdLine.add(customOptions[i]);
cmdLine.add(localCopy.getPath());
String [] args = new String[cmdLine.size()];
cmdLine.toArray(args);
try {
Process p = Runtime.getRuntime().exec(args);
p.waitFor();
long end = System.currentTimeMillis();
Log.log(Log.DEBUG, getClass(), "Tagging took "
+ (end - start) * .001 + " seconds.");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
localCopy.delete();
return localCopy.getPath();
}
private String run(Vector<String> what, int numFiles) {
String ctags = GeneralOptionPane.getCtags();
String cmd = GeneralOptionPane.getCmd();
String tagFile = getTempFile("tags");
Vector<String> cmdLine = new Vector<String>();
cmdLine.add(ctags);
cmdLine.add("--verbose"); // Allow progress tracking
cmdLine.add("--sort=no"); // Avoid sorting to improve performance
cmdLine.add("-f");
cmdLine.add(tagFile);
String [] customOptions = cmd.split(SPACES);
for (int i = 0; i < customOptions.length; i++)
cmdLine.add(customOptions[i]);
cmdLine.addAll(what);
String [] args = new String[cmdLine.size()];
cmdLine.toArray(args);
if (logger != null)
{
logger.beginTask(jEdit.getProperty(TAGGING));
if (numFiles != UNKNOWN_NUMBER_OF_FILES)
logger.setProgressParams(0, numFiles);
}
Process p = null;
StreamConsumer osc = null, esc = null;
try {
p = Runtime.getRuntime().exec(args);
osc = new StreamConsumer(p.getInputStream());
osc.start();
esc = new StreamConsumer(p.getErrorStream());
esc.start();
p.waitFor();
} catch (IOException e) {
e.printStackTrace();
tagFile = null;
} catch (InterruptedException e) {
if (p != null)
p.destroy();
// The output and error streams should close as a result of
// killing the process, and the reader threads should get an
// exception and end.
e.printStackTrace();
tagFile = null;
} finally {
if (logger != null)
logger.endTask();
}
return tagFile;
}
private void addProgressMessage(String s)
{
if (logger != null)
logger.log(s);
}
private void addProgress(String s, int value)
{
if (logger != null)
{
logger.log(s);
logger.setProgress(value);
}
}
private String getTempDirectory() {
return jEdit.getSettingsDirectory() + "/CtagsInterface";
}
private String getTempFile(String prefix) {
synchronized (tempFiles) {
String tempDir = getTempDirectory();
File d = new File(tempDir);
if (! d.exists())
d.mkdirs();
for (int i = 0; i < 100; i++) {
String path = tempDir + "/" + prefix + i;
if (tempFiles.add(path))
return path;
}
}
return null;
}
private class StreamConsumer extends Thread
{
private InputStream is;
private final Pattern progressLine = Pattern.compile(
"^(opening|ignoring).*", Pattern.CASE_INSENSITIVE);
public StreamConsumer(InputStream is)
{
this.is = is;
}
public void run()
{
try
{
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
int processed = 0;
do
{
line = br.readLine();
if (line != null)
{
Matcher m = progressLine.matcher(line);
if (m.matches())
addProgress(line, ++processed);
else
addProgressMessage(line);
}
}
while (line != null);
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
}