package org.rubypeople.rdt.internal.ui.infoviews;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.swt.SWT;
import org.rubypeople.rdt.core.IRubyInformation;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.internal.launching.LaunchingPlugin;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
import org.rubypeople.rdt.launching.IRubyLaunchConfigurationConstants;
import org.rubypeople.rdt.launching.RubyRuntime;
import org.rubypeople.rdt.ui.text.ansi.ANSIParser;
import org.rubypeople.rdt.ui.text.ansi.ANSIToken;
import com.aptana.rdt.AptanaRDTPlugin;
public class RiUtility implements IRubyInformation {
private final static String HEADER = "<html><head></head><body>";
private final static String TAIL = "</body></html>";
private static final String FASTRI_INDEX = ".fastri-index";
public String getDocs(String token) {
List<String> args = new ArrayList<String>();
args.add(token);
return getRIContents(args);
}
public static String getRIContents(List<String> args) {
File file = getFRIIndexFile();
if (!file.exists()) {
buildIndex();
}
args.add(0, "-L");
return execAndReadOutput(getFastRiPath(), args);
}
public static String getRIHTMLContents(List<String> args) {
String result = getRIContents(args);
if (result == null) return result;
StringBuilder buffer = new StringBuilder();
buffer.append(checkForANSIColors(escapeHTML(result)));
buffer.insert(0, HEADER); // Put the header before all the contents
buffer.append(TAIL); // Put the body and html close tags at end
return buffer.toString();
}
private static String checkForANSIColors(String string) {
StringBuilder buffer = new StringBuilder();
List<ANSIToken> tokens = getParser(string).parse(string);
for (ANSIToken token : tokens) {
String endTag = "";
if (token.hasFontStyle()) {
if (token.getFontStyle() == SWT.BOLD) {
buffer.append("<b>");
endTag = "</b>";
} else if (token.getFontStyle() == SWT.ITALIC) {
buffer.append("<i>");
endTag = "</i>";
}
}
if (token.hasForegroundColor()) {
buffer.append("<span style=\"color: #");
buffer.append(pad(Integer.toHexString(token.getForegroundRGB().red)));
buffer.append(pad(Integer.toHexString(token.getForegroundRGB().green)));
buffer.append(pad(Integer.toHexString(token.getForegroundRGB().blue)));
buffer.append("\">");
endTag = "</span>" + endTag;
}
buffer.append(token.toString());
buffer.append(endTag);
}
return buffer.toString();
}
private static ANSIParser getParser(String content) {
if (content.indexOf(ANSIParser.ESC) != -1) return new ANSIParser();
if (Platform.getOS().equals(Platform.OS_WIN32) && !RubyRuntime.currentVMIsCygwin() && !RubyRuntime.currentVMIsJRuby()) return new FastRIParser();
return new ANSIParser();
}
private static String pad(String hexString) {
if (hexString.length() == 1) return "0" + hexString;
return hexString;
}
private static String escapeHTML(final String content) {
String escaped = content.replace("&", "&");
escaped = escaped.replace("<", "<");
escaped = escaped.replace(">", ">");
escaped = escaped.replace("\r\n", "\n");
escaped = escaped.replace("\r", "\n");
escaped = escaped.replace("\n", "<br/>");
return escaped;
}
static void rebuildIndex() {
File file = getFRIIndexFile();
file.delete();
buildIndex();
}
static void buildIndex() {
List<String> commands = new ArrayList<String>();
commands.add("-b");
String output = execAndReadOutput(getFastRiServerPath(), commands);
}
private static File getFRIIndexFile() {
if (RubyRuntime.currentVMIsCygwin()) {
return new File(RubyRuntime.getDefaultVMInstall().getInstallLocation(), FASTRI_INDEX);
}
String homePath = System.getProperty("user.home");
return new File(homePath + File.separator + FASTRI_INDEX);
}
private static String getFastRiPath() {
copyFastRIFiles();
File file = LaunchingPlugin.getFileInPlugin(new Path("ruby/fri"));
if (file == null || !file.exists() || !file.isFile()) return null;
return file.getAbsolutePath();
}
private static void copyFastRIFiles() {
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("full_text_index.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("full_text_indexer.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("name_descriptor.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("ri_index.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("ri_service.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("util.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri").append("version.rb"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fri"));
RubyCore.copyToStateLocation(LaunchingPlugin.getDefault(), new Path("ruby").append("fastri-server"));
}
private static String getFastRiServerPath() {
copyFastRIFiles();
File file = LaunchingPlugin.getFileInPlugin(new Path("ruby/fastri-server"));
if (file == null || !file.exists() || !file.isFile()) return null;
return file.getAbsolutePath();
}
private static ILaunchConfigurationType getRubyApplicationConfigType() {
return getLaunchManager().getLaunchConfigurationType(
IRubyLaunchConfigurationConstants.ID_RUBY_APPLICATION);
}
private static ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
private synchronized static String execAndReadOutput(String file, List<String> commands) {
if (file == null || file.trim().length() == 0)
{
RubyPlugin.log(IStatus.ERROR, "Tried to execute commands against empty executable file for RIUtility: " + commands.toString());
return "";
}
ILaunchConfiguration config = createConfiguration(file, listToCommandLine(commands));
File output = RubyPlugin.getDefault().getStateLocation().append("fastri_output.txt").toFile();
return launchInBackgroundAndRead(config, output);
}
private static String listToCommandLine(List<String> commands) {
String arguments = "";
for (String command : commands) {
arguments += command;
arguments += " ";
}
if (arguments.length() > 0)
arguments = arguments.substring(0, arguments.length() - 1);
return arguments;
}
private static ILaunchConfiguration createConfiguration(String file, String arguments) {
ILaunchConfiguration config = null;
try {
ILaunchConfigurationType configType = getRubyApplicationConfigType();
ILaunchConfigurationWorkingCopy wc = configType
.newInstance(null, RubyRuntime.generateUniqueLaunchConfigurationNameFrom(file));
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_FILE_NAME,
file);
wc.setAttribute(
IRubyLaunchConfigurationConstants.ATTR_VM_INSTALL_NAME,
RubyRuntime.getDefaultVMInstall().getName());
wc.setAttribute(
IRubyLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE,
RubyRuntime.getDefaultVMInstall().getVMInstallType()
.getId());
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, arguments);
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_VM_ARGUMENTS,"");
Map<String, String> map = new HashMap<String, String>();
map.put(IRubyLaunchConfigurationConstants.ATTR_RUBY_COMMAND, "ruby");
wc
.setAttribute(
IRubyLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE_SPECIFIC_ATTRS_MAP,
map);
wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, LaunchingPlugin.getFileInPlugin(new Path("ruby")).getAbsolutePath());
wc.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, false);
config = wc.doSave();
} catch (CoreException ce) {
// ignore for now
}
return config;
}
private static String launchInBackgroundAndRead(ILaunchConfiguration config, File file) {
final StringBuffer buf = new StringBuffer();
try {
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
wc.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, true);
wc.setAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE, false);
wc.setAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, file.getAbsolutePath());
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_FORCE_NO_CONSOLE, true);
config = wc.doSave();
ILaunch launch = config.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor());
IProcess iproc = launch.getProcesses()[0];
IStreamMonitor stdOut = iproc.getStreamsProxy().getOutputStreamMonitor();
stdOut.addListener(new IStreamListener() {
public void streamAppended(final String text,
IStreamMonitor monitor) {
buf.append(text);
}
});
while (!launch.isTerminated()) {
Thread.yield();
}
if (buf.toString().trim().length() == 0) { // if we didn't get anything out of the listener, try reading the output file
buf.append(readFile(file));
}
return buf.toString();
} catch (Exception e) {
AptanaRDTPlugin.log(e);
}
return null;
}
private static String readFile(File file) {
StringBuffer buf = new StringBuffer();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = reader.readLine()) != null) {
buf.append(line);
buf.append("\n");
}
} catch (FileNotFoundException e) {
AptanaRDTPlugin.log(e);
} catch (IOException e) {
AptanaRDTPlugin.log(e);
} finally {
try {
if (reader != null) reader.close();
} catch (IOException e) {
// ignore
}
}
return buf.toString();
}
}