--- /dev/null
+
+project := VimCoder
+version := 0.3
+
+mainclass = bin/com/dogcows/VimCoder.class
+library = lib/ContestApplet.jar
+jarfile = $(project)-$(version).jar
+
+JAVAC := javac
+JAVACFLAGS := -d bin -sourcepath src -classpath bin:$(library)
+
+
+.PHONY: all clean distclean dist fetch jar
+
+all: $(library) $(mainclass)
+
+clean:
+ rm -rf bin build
+
+distclean: clean
+ rm -rf lib
+
+dist:
+ git archive HEAD --prefix=vimcoder-$(version)/ | bzip2 >vimcoder-$(version).tar.bz2
+
+fetch: $(library)
+
+jar: all $(jarfile)
+
+
+$(library):
+ sh make.sh fetch $@
+
+bin/com/dogcows/%.class: src/com/dogcows/%.java
+ mkdir -p bin/com/dogcows/resources &&\
+$(JAVAC) $(JAVACFLAGS) $< &&\
+cp -R src/com/dogcows/resources bin/com/dogcows/
+
+$(jarfile): $(mainclass)
+ sh make.sh jar $@
+
+
+$(mainclass): src/com/dogcows/Util.java src/com/dogcows/Editor.java
+
--- /dev/null
+#!/bin/sh
+
+action=$1
+shift
+
+case "$action"
+in
+
+ fetch)
+ echo "Fetching dependencies..."
+ mkdir -p lib
+ curl -o $1 http://www.topcoder.com/contest/classes/ContestApplet.jar
+ ;;
+
+ jar)
+ echo "Packaging jar file..."
+ rm -rf build
+ mkdir -p build/META-INF
+ printf "Manifest-Version: 1.0\n\n" >build/META-INF/MANIFEST.MF
+ cp -R bin/com COPYING README build/
+ cd build
+ files=$(find com -type f | sort)
+ zip $1 META-INF/MANIFEST.MF README COPYING $files
+ cd ..
+ mv build/$1 .
+ rm -rf build
+ echo "Done."
+ ;;
+esac
+
*/
public class Editor
{
- /**
- * The problem ID number.
- */
- private String id;
-
- /**
- * The name of the class.
- */
- private String name;
-
- /**
- * The path of the current source file.
- */
- private File sourceFile;
-
- /**
- * The path of the problem directory.
- */
- private File directory;
-
-
- /**
- * Map languages names to file extensions.
- */
- private static final Map<String,String> languageExtension = new HashMap<String,String>();
- static
- {
- languageExtension.put("Java", "java");
- languageExtension.put("C++", "cc");
- languageExtension.put("C#", "cs");
- languageExtension.put("VB", "vb");
- languageExtension.put("Python", "py");
- }
-
-
- /**
- * Construct an editor with the problem objects given us by the Arena.
- * @param component A container for the particulars of the problem.
- * @param language The currently selected language.
- * @param renderer A helper object to help format the problem statement.
- * @throws Exception If the editor could not set itself up.
- */
- public Editor(ProblemComponentModel component,
- Language language,
- Renderer renderer) throws Exception
- {
- this.id = String.valueOf(component.getProblem().getProblemID());
- this.name = component.getClassName();
-
- // Make sure the top-level vimcoder directory exists.
- File topDir = VimCoder.getStorageDirectory();
- if (!topDir.isDirectory())
- {
- if (!topDir.mkdirs()) throw new IOException(topDir.getPath());
- }
-
- // Make sure the problem directory exists.
- this.directory = new File(topDir, id);
- if (!directory.isDirectory())
- {
- if (!directory.mkdirs()) throw new IOException(directory.getPath());
- }
-
- String lang = language.getName();
- String ext = languageExtension.get(lang);
-
- // Set up the terms used for the template expansion.
- HashMap<String,String> terms = new HashMap<String,String>();
- terms.put("RETURNTYPE",component.getReturnType().getDescriptor(language).replaceAll("\\s+", ""));
- terms.put("CLASSNAME", name);
- terms.put("METHODNAME", component.getMethodName());
- terms.put("METHODPARAMS", getMethodParams(component.getParamTypes(),
- component.getParamNames(),
- language));
- terms.put("METHODPARAMNAMES", Util.join(component.getParamNames(), ", "));
- terms.put("METHODPARAMSTREAMIN", Util.join(component.getParamNames(), " >> "));
- terms.put("METHODPARAMSTREAMOUT", Util.join(component.getParamNames(), " << "));
- terms.put("METHODPARAMDECLARES", getMethodParamDeclarations(component.getParamTypes(),
- component.getParamNames(),
- language));
-
- // Write the problem statement as an HTML file in the problem directory.
- File problemFile = new File(directory, "Problem.html");
- if (!problemFile.canRead())
- {
- FileWriter writer = new FileWriter(problemFile);
- try
- {
- writer.write(renderer.toHTML(language));
- }
- finally
- {
- writer.close();
- }
- }
-
- // Expand the template for the main class and write it to the current
- // source file.
- sourceFile = new File(directory, name + "." + ext);
- if (!sourceFile.canRead())
- {
- String text = Util.expandTemplate(readTemplate(lang + "Template"),
- terms);
- FileWriter writer = new FileWriter(sourceFile);
- writer.write(text);
- writer.close();
- }
-
- // Expand the driver template and write it to a source file.
- File driverFile = new File(directory, "driver." + ext);
- if (!driverFile.canRead())
- {
- String text = Util.expandTemplate(readTemplate(lang + "Driver"),
- terms);
- FileWriter writer = new FileWriter(driverFile);
- writer.write(text);
- writer.close();
- }
-
- // Write the test cases to a text file. The driver code can read this
- // file and perform the tests based on what it reads.
- File testcaseFile = new File(directory, "testcases.txt");
- if (!testcaseFile.canRead())
- {
- StringBuilder text = new StringBuilder();
- if (component.hasTestCases())
- {
- for (TestCase testCase : component.getTestCases())
- {
- text.append(testCase.getOutput() + System.getProperty("line.separator"));
- for (String input : testCase.getInput())
- {
- text.append(input + System.getProperty("line.separator"));
- }
- }
- }
- FileWriter writer = new FileWriter(testcaseFile);
- writer.write(text.toString());
- writer.close();
- }
-
- // Finally, expand the Makefile template and write it.
- File makeFile = new File(directory, "Makefile");
- {
- String text = Util.expandTemplate(readTemplate(lang + "Makefile"),
- terms);
- FileWriter writer = new FileWriter(makeFile);
- writer.write(text);
- writer.close();
- }
- }
-
- /**
- * Save the source code provided by the server, and tell the Vim server to
- * edit the current source file.
- * @param source The source code.
- * @throws Exception If the source couldn't be written or the Vim server
- * had a problem.
- */
- public void setSource(String source) throws Exception
- {
- FileWriter writer = new FileWriter(new File(directory, name));
- writer.write(source);
- writer.close();
- sendVimCommand("--remote-tab-silent", sourceFile.getPath());
- }
-
- /**
- * Read the source code from the current source file.
- * @return The source code.
- * @throws IOException If the source file could not be read.
- */
- public String getSource() throws IOException
- {
- return Util.readFile(sourceFile) + "\n// Edited by " +
- VimCoder.version + "\n// " + VimCoder.website + "\n\n";
- }
-
-
- /**
- * Send a command to the Vim server. If the server isn't running, it will
- * be started with the name VIMCODER#### where #### is the problem ID.
- * @param command The command to send to the server.
- * @param argument A single argument for the remote command.
- * @throws Exception If the command could not be sent.
- */
- private void sendVimCommand(String command,
- String argument) throws Exception
- {
- String[] arguments = {argument};
- sendVimCommand(command, arguments);
- }
-
- /**
- * Send a command to the Vim server. If the server isn't running, it will
- * be started with the name VIMCODER#### where #### is the problem ID.
- * @param command The command to send to the server.
- * @param argument Arguments for the remote command.
- * @throws Exception If the command could not be sent.
- */
- private void sendVimCommand(String command,
- String[] arguments) throws Exception
- {
- String[] vimCommand = VimCoder.getVimCommand().split("\\s");
- String[] flags = {"--servername", "VimCoder" + id, command};
- vimCommand = Util.concat(vimCommand, flags);
- vimCommand = Util.concat(vimCommand, arguments);
- Process child = Runtime.getRuntime().exec(vimCommand, null, directory);
-
- /* FIXME: This is a hack with a magic number. The problem is the Vim
- * process doesn't fork to the background on some systems, so we can't
- * wait on the child. At the same time, calling this method before the
- * previous child could finish initializing the server may result in
- * multiple editor windows popping up. We'd also like to be able to
- * get the return code from the child if we can. The workaround here is
- * to stall the thread for a little while or until we know the child
- * does exit. If the child never exits before the timeout, we will
- * assume it is not backgrounding and that everything worked. */
- long expire = System.currentTimeMillis() + 250;
- while (System.currentTimeMillis() < expire)
- {
- Thread.yield();
- try
- {
- int exitCode = child.exitValue();
- if (exitCode != 0) throw new Exception("Vim process returned exit code " + exitCode + ".");
- break;
- }
- catch (IllegalThreadStateException exception)
- {
- // The child has not exited; intentionally ignoring exception.
- }
- }
- }
-
-
- /**
- * Read a template. We first look in the storage directory. If we can't
- * find one, we look among the resources.
- * @param tName The name of the template.
- * @return The contents of the template file, or an empty string.
- */
- private String readTemplate(String tName)
- {
- File templateFile = new File(VimCoder.getStorageDirectory(), tName);
- try
- {
- if (templateFile.canRead()) return Util.readFile(templateFile);
- return Util.readResource(tName);
- }
- catch (IOException exception)
- {
- return "";
- }
- }
-
-
- /**
- * Convert an array of data types to an array of strings according to a
- * given language.
- * @param types The data types.
- * @param language The language to use in the conversion.
- * @return The array of string representations of the data types.
- */
- private String[] getStringTypes(DataType[] types, Language language)
- {
- String[] strings = new String[types.length];
- for (int i = 0; i < types.length; ++i)
- {
- strings[i] = types[i].getDescriptor(language).replaceAll("\\s+", "");
- }
- return strings;
- }
-
- /**
- * Combine the data types and parameter names into a comma-separated list of
- * the method parameters. The result could be used inside the parentheses
- * of a method declaration.
- * @param types The data types of the parameters.
- * @param names The names of the parameters.
- * @param language The language used for representing the data types.
- * @return The list of parameters.
- */
- private String getMethodParams(DataType[] types,
- String[] names,
- Language language)
- {
- String[] typeStrings = getStringTypes(types, language);
- return Util.join(Util.combine(typeStrings, names, " "), ", ");
- }
-
- /**
- * Combine the data types and parameter names into a group of variable
- * declarations. Each declaration is separated by a new line and terminated
- * with a semicolon.
- * @param types The data types of the parameters.
- * @param names The names of the parameters.
- * @param language The language used for representing the data types.
- * @return The parameters as a block of declarations.
- */
- private String getMethodParamDeclarations(DataType[] types,
- String[] names,
- Language language)
- {
- final String end = ";" + System.getProperty("line.separator");
- String[] typeStrings = getStringTypes(types, language);
- return Util.join(Util.combine(typeStrings, names, "\t"), end) + end;
- }
+ /**
+ * The problem ID number.
+ */
+ private String id;
+
+ /**
+ * The name of the class.
+ */
+ private String name;
+
+ /**
+ * The path of the current source file.
+ */
+ private File sourceFile;
+
+ /**
+ * The path of the problem directory.
+ */
+ private File directory;
+
+
+ /**
+ * Map languages names to file extensions.
+ */
+ private static final Map<String,String> languageExtension = new HashMap<String,String>();
+ static
+ {
+ languageExtension.put("Java", "java");
+ languageExtension.put("C++", "cc");
+ languageExtension.put("C#", "cs");
+ languageExtension.put("VB", "vb");
+ languageExtension.put("Python", "py");
+ }
+
+
+ /**
+ * Construct an editor with the problem objects given us by the Arena.
+ * @param component A container for the particulars of the problem.
+ * @param language The currently selected language.
+ * @param renderer A helper object to help format the problem statement.
+ * @throws Exception If the editor could not set itself up.
+ */
+ public Editor(ProblemComponentModel component,
+ Language language,
+ Renderer renderer) throws Exception
+ {
+ this.id = String.valueOf(component.getProblem().getProblemID());
+ this.name = component.getClassName();
+
+ // Make sure the top-level vimcoder directory exists.
+ File topDir = VimCoder.getStorageDirectory();
+ if (!topDir.isDirectory())
+ {
+ if (!topDir.mkdirs()) throw new IOException(topDir.getPath());
+ }
+
+ // Make sure the problem directory exists.
+ this.directory = new File(topDir, id);
+ if (!directory.isDirectory())
+ {
+ if (!directory.mkdirs()) throw new IOException(directory.getPath());
+ }
+
+ String lang = language.getName();
+ String ext = languageExtension.get(lang);
+
+ // Set up the terms used for the template expansion.
+ HashMap<String,String> terms = new HashMap<String,String>();
+ terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language).replaceAll("\\s+", ""));
+ terms.put("CLASSNAME", name);
+ terms.put("METHODNAME", component.getMethodName());
+ terms.put("METHODPARAMS", getMethodParams(component.getParamTypes(),
+ component.getParamNames(),
+ language));
+ terms.put("METHODPARAMNAMES", Util.join(component.getParamNames(), ", "));
+ terms.put("METHODPARAMSTREAMIN", Util.join(component.getParamNames(), " >> "));
+ terms.put("METHODPARAMSTREAMOUT", Util.join(component.getParamNames(), " << "));
+ terms.put("METHODPARAMDECLARES", getMethodParamDeclarations(component.getParamTypes(),
+ component.getParamNames(),
+ language));
+
+ // Write the problem statement as an HTML file in the problem directory.
+ File problemFile = new File(directory, "Problem.html");
+ if (!problemFile.canRead())
+ {
+ FileWriter writer = new FileWriter(problemFile);
+ try
+ {
+ writer.write(renderer.toHTML(language));
+ }
+ finally
+ {
+ writer.close();
+ }
+ }
+
+ // Expand the template for the main class and write it to the current
+ // source file.
+ sourceFile = new File(directory, name + "." + ext);
+ if (!sourceFile.canRead())
+ {
+ String text = Util.expandTemplate(readTemplate(lang + "Template"),
+ terms);
+ FileWriter writer = new FileWriter(sourceFile);
+ writer.write(text);
+ writer.close();
+ }
+
+ // Expand the driver template and write it to a source file.
+ File driverFile = new File(directory, "driver." + ext);
+ if (!driverFile.canRead())
+ {
+ String text = Util.expandTemplate(readTemplate(lang + "Driver"),
+ terms);
+ FileWriter writer = new FileWriter(driverFile);
+ writer.write(text);
+ writer.close();
+ }
+
+ // Write the test cases to a text file. The driver code can read this
+ // file and perform the tests based on what it reads.
+ File testcaseFile = new File(directory, "testcases.txt");
+ if (!testcaseFile.canRead())
+ {
+ StringBuilder text = new StringBuilder();
+ if (component.hasTestCases())
+ {
+ for (TestCase testCase : component.getTestCases())
+ {
+ text.append(testCase.getOutput() + System.getProperty("line.separator"));
+ for (String input : testCase.getInput())
+ {
+ text.append(input + System.getProperty("line.separator"));
+ }
+ }
+ }
+ FileWriter writer = new FileWriter(testcaseFile);
+ writer.write(text.toString());
+ writer.close();
+ }
+
+ // Finally, expand the Makefile template and write it.
+ File makeFile = new File(directory, "Makefile");
+ {
+ String text = Util.expandTemplate(readTemplate(lang + "Makefile"),
+ terms);
+ FileWriter writer = new FileWriter(makeFile);
+ writer.write(text);
+ writer.close();
+ }
+ }
+
+ /**
+ * Save the source code provided by the server, and tell the Vim server to
+ * edit the current source file.
+ * @param source The source code.
+ * @throws Exception If the source couldn't be written or the Vim server
+ * had a problem.
+ */
+ public void setSource(String source) throws Exception
+ {
+ FileWriter writer = new FileWriter(new File(directory, name));
+ writer.write(source);
+ writer.close();
+ sendVimCommand("--remote-tab-silent", sourceFile.getPath());
+ }
+
+ /**
+ * Read the source code from the current source file.
+ * @return The source code.
+ * @throws IOException If the source file could not be read.
+ */
+ public String getSource() throws IOException
+ {
+ return Util.readFile(sourceFile) + "\n// Edited by " +
+ VimCoder.version + "\n// " + VimCoder.website + "\n\n";
+ }
+
+
+ /**
+ * Send a command to the Vim server.
+ * If the server isn't running, it will be started with the name
+ * VIMCODER#### where #### is the problem ID.
+ * @param command The command to send to the server.
+ * @param argument A single argument for the remote command.
+ * @throws Exception If the command could not be sent.
+ */
+ private void sendVimCommand(String command,
+ String argument) throws Exception
+ {
+ String[] arguments = {argument};
+ sendVimCommand(command, arguments);
+ }
+
+ /**
+ * Send a command to the Vim server.
+ * If the server isn't running, it will be started with the name
+ * VIMCODER#### where #### is the problem ID.
+ * @param command The command to send to the server.
+ * @param argument Arguments for the remote command.
+ * @throws Exception If the command could not be sent.
+ */
+ private void sendVimCommand(String command,
+ String[] arguments) throws Exception
+ {
+ String[] vimCommand = VimCoder.getVimCommand().split("\\s");
+ String[] flags = {"--servername", "VimCoder" + id, command};
+ vimCommand = Util.concat(vimCommand, flags);
+ vimCommand = Util.concat(vimCommand, arguments);
+ Process child = Runtime.getRuntime().exec(vimCommand, null, directory);
+
+ /* FIXME: This is a pretty bad hack. The problem is that the Vim
+ * process doesn't fork to the background on some systems, so we
+ * can't wait on the child. At the same time, calling this method
+ * before the previous child could finish initializing the server
+ * may result in multiple editor windows popping up. We'd also
+ * like to be able to get the return code from the child if we can.
+ * The workaround here is to stall the thread for a little while or
+ * until we see that the child exits. If the child never exits
+ * before the timeout, we will assume it is not backgrounding and
+ * that everything worked. This works as long as the Vim server is
+ * able to start within the stall period. */
+ long expire = System.currentTimeMillis() + 250;
+ while (System.currentTimeMillis() < expire)
+ {
+ Thread.yield();
+ try
+ {
+ int exitCode = child.exitValue();
+ if (exitCode != 0) throw new Exception("Vim process returned exit code " + exitCode + ".");
+ break;
+ }
+ catch (IllegalThreadStateException exception)
+ {
+ // The child has not exited; intentionally ignoring exception.
+ }
+ }
+ }
+
+
+ /**
+ * Read a template.
+ * We first look in the storage directory. If we can't find one, we
+ * look among the resources.
+ * @param tName The name of the template.
+ * @return The contents of the template file, or an empty string.
+ */
+ private String readTemplate(String tName)
+ {
+ File templateFile = new File(VimCoder.getStorageDirectory(), tName);
+ try
+ {
+ if (templateFile.canRead()) return Util.readFile(templateFile);
+ return Util.readResource(tName);
+ }
+ catch (IOException exception)
+ {
+ return "";
+ }
+ }
+
+
+ /**
+ * Convert an array of data types to an array of strings according to a
+ * given language.
+ * @param types The data types.
+ * @param language The language to use in the conversion.
+ * @return The array of string representations of the data types.
+ */
+ private String[] getStringTypes(DataType[] types, Language language)
+ {
+ String[] strings = new String[types.length];
+ for (int i = 0; i < types.length; ++i)
+ {
+ strings[i] = types[i].getDescriptor(language).replaceAll("\\s+", "");
+ }
+ return strings;
+ }
+
+ /**
+ * Combine the data types and parameter names into a comma-separated list of
+ * the method parameters.
+ * The result could be used inside the parentheses of a method
+ * declaration.
+ * @param types The data types of the parameters.
+ * @param names The names of the parameters.
+ * @param language The language used for representing the data types.
+ * @return The list of parameters.
+ */
+ private String getMethodParams(DataType[] types,
+ String[] names,
+ Language language)
+ {
+ String[] typeStrings = getStringTypes(types, language);
+ return Util.join(Util.combine(typeStrings, names, " "), ", ");
+ }
+
+ /**
+ * Combine the data types and parameter names into a group of variable
+ * declarations.
+ * Each declaration is separated by a new line and terminated with a
+ * semicolon.
+ * @param types The data types of the parameters.
+ * @param names The names of the parameters.
+ * @param language The language used for representing the data types.
+ * @return The parameters as a block of declarations.
+ */
+ private String getMethodParamDeclarations(DataType[] types,
+ String[] names,
+ Language language)
+ {
+ final String end = ";" + System.getProperty("line.separator");
+ String[] typeStrings = getStringTypes(types, language);
+ return Util.join(Util.combine(typeStrings, names, "\t"), end) + end;
+ }
}
*/
public abstract class Util
{
- /**
- * Concatenate two arrays into a single array.
- * @param a First array.
- * @param b Second array.
- * @return The combined array.
- */
- public static <T> T[] concat(T[] a, T[] b)
- {
- T[] result = Arrays.copyOf(a, a.length + b.length);
- System.arraycopy(b, 0, result, a.length, b.length);
- return result;
- }
-
- /**
- * Combined string elements from two arrays into a single array, gluing
- * together elements of the same index with a delimiter string.
- * @param a First string array.
- * @param b Second string array.
- * @param glue The delimiter string.
- * @return The combined array.
- */
- public static String[] combine(String[] a, String[] b, String glue)
- {
- String[] result = new String[Math.min(a.length, b.length)];
- for (int i = 0; i < result.length; ++i)
- {
- result[i] = a[i] + glue + b[i];
- }
- return result;
- }
+ /**
+ * Concatenate two arrays into a single array.
+ * @param a First array.
+ * @param b Second array.
+ * @return The combined array.
+ */
+ public static <T> T[] concat(T[] a, T[] b)
+ {
+ T[] result = Arrays.copyOf(a, a.length + b.length);
+ System.arraycopy(b, 0, result, a.length, b.length);
+ return result;
+ }
- /**
- * Join the elements of a string array with a delimiter.
- * @param a The array.
- * @param glue The delimiter string.
- * @return The joined string.
- */
- public static String join(String[] a, String glue)
- {
- if (a.length == 0) return "";
- StringBuilder result = new StringBuilder();
- result.append(a[0]);
- for (int i = 1; i < a.length; ++i) result.append(glue).append(a[i]);
- return result.toString();
- }
-
- /**
- * Quote a string by replacing prepending backslashes and double
- * quotation characters with an extra backslash.
- * @param The string to be quoted.
- * @return The quoted string.
- */
- public static String quote(String a)
- {
- a = a.replaceAll("\\\\", "\\\\\\\\");
- a = a.replaceAll("\"", "\\\\\\\"");
- return a;
- }
-
- /**
- * Simply read a file's contents into a string object.
- * @param file The file to read.
- * @return The contents of the file.
- * @throws IOException If the file is not readable.
- */
- public static String readFile(File file) throws IOException
- {
- StringBuilder text = new StringBuilder();
-
- BufferedReader reader = new BufferedReader(new FileReader(file.getPath()));
- try
- {
- String line = null;
-
- while ((line = reader.readLine()) != null)
- {
- text.append(line + System.getProperty("line.separator"));
- }
- }
- finally
- {
- reader.close();
- }
-
- return text.toString();
- }
-
- /**
- * Read a resource file into a string object. The resources should be
- * placed in the directory `resources' underneath the parent directory of
- * this class. Reading resources packaged in a jar is allowable.
- * @param path Relative path to the resource.
- * @return The contents of the resource.
- * @throws IOException If the resource is not readable.
- */
- public static String readResource(String path) throws IOException
- {
- StringBuilder text = new StringBuilder();
-
- InputStream stream = Util.class.getResourceAsStream("resources/" + path);
- if (stream != null)
- {
- try
- {
- byte[] buffer = new byte[4096];
- int numBytes = 0;
- while (0 < (numBytes = stream.read(buffer)))
- {
- text.append(new String(buffer, 0, numBytes));
- }
- }
- finally
- {
- stream.close();
- }
- }
-
- return text.toString();
- }
-
- /**
- * The poor man's template package. Provide a template and a map of terms
- * to build the result with the terms expanded into the template. Terms
- * in the template should appear surrounded with dollar signs. For example,
- * if $MYTERM$ appears in the template, it will be replaced by the value
- * into the terms map with the key MYTERM (if it exists in the map).
- * @param template The template string.
- * @param terms A map of key/value terms.
- * @return The string expanded from the template and terms.
- */
- public static String expandTemplate(String template, Map<String,String> terms)
- {
- String text = template;
- for (String key : terms.keySet())
- {
- text = text.replaceAll("\\$" + key + "\\$",
- Util.quote(terms.get(key)));
- }
- return text;
- }
+ /**
+ * Combined string elements from two arrays into a single array, gluing
+ * together elements of the same index with a delimiter string.
+ * @param a First string array.
+ * @param b Second string array.
+ * @param glue The delimiter string.
+ * @return The combined array.
+ */
+ public static String[] combine(String[] a, String[] b, String glue)
+ {
+ String[] result = new String[Math.min(a.length, b.length)];
+ for (int i = 0; i < result.length; ++i)
+ {
+ result[i] = a[i] + glue + b[i];
+ }
+ return result;
+ }
+
+ /**
+ * Join the elements of a string array with a delimiter.
+ * @param a The array.
+ * @param glue The delimiter string.
+ * @return The joined string.
+ */
+ public static String join(String[] a, String glue)
+ {
+ if (a.length == 0) return "";
+ StringBuilder result = new StringBuilder();
+ result.append(a[0]);
+ for (int i = 1; i < a.length; ++i) result.append(glue).append(a[i]);
+ return result.toString();
+ }
+
+ /**
+ * Quote a string by replacing prepending backslashes and double
+ * quotation characters with an extra backslash.
+ * @param The string to be quoted.
+ * @return The quoted string.
+ */
+ public static String quote(String a)
+ {
+ a = a.replaceAll("\\\\", "\\\\\\\\");
+ a = a.replaceAll("\"", "\\\\\\\"");
+ return a;
+ }
+
+ /**
+ * Simply read a file's contents into a string object.
+ * @param file The file to read.
+ * @return The contents of the file.
+ * @throws IOException If the file is not readable.
+ */
+ public static String readFile(File file) throws IOException
+ {
+ StringBuilder text = new StringBuilder();
+
+ BufferedReader reader = new BufferedReader(new FileReader(file.getPath()));
+ try
+ {
+ String line = null;
+
+ while ((line = reader.readLine()) != null)
+ {
+ text.append(line + System.getProperty("line.separator"));
+ }
+ }
+ finally
+ {
+ reader.close();
+ }
+
+ return text.toString();
+ }
+
+ /**
+ * Read a resource file into a string object.
+ * The resources should be placed in the directory `resources'
+ * underneath the parent directory of this class. Reading resources
+ * packaged in a jar is allowable.
+ * @param path Relative path to the resource.
+ * @return The contents of the resource.
+ * @throws IOException If the resource is not readable.
+ */
+ public static String readResource(String path) throws IOException
+ {
+ StringBuilder text = new StringBuilder();
+
+ InputStream stream = Util.class.getResourceAsStream("resources/" + path);
+ if (stream != null)
+ {
+ try
+ {
+ byte[] buffer = new byte[4096];
+ int numBytes = 0;
+ while (0 < (numBytes = stream.read(buffer)))
+ {
+ text.append(new String(buffer, 0, numBytes));
+ }
+ }
+ finally
+ {
+ stream.close();
+ }
+ }
+
+ return text.toString();
+ }
+
+ /**
+ * The poor man's template package.
+ * Provide a template and a map of terms to build the result with the
+ * terms expanded into the template. Terms in the template should
+ * appear surrounded with dollar signs. For example, if $MYTERM$
+ * appears in the template, it will be replaced by the value into the
+ * terms map with the key MYTERM (if it exists in the map).
+ * @param template The template string.
+ * @param terms A map of key/value terms.
+ * @return The string expanded from the template and terms.
+ */
+ public static String expandTemplate(String template, Map<String,String> terms)
+ {
+ String text = template;
+ for (String key : terms.keySet())
+ {
+ text = text.replaceAll("\\$" + key + "\\$",
+ Util.quote(terms.get(key)));
+ }
+ return text;
+ }
}
package com.dogcows;
-import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
-import com.topcoder.client.contestant.ProblemComponentModel;
-import com.topcoder.shared.language.*;
-import com.topcoder.shared.problem.*;
-import com.topcoder.shared.problem.Renderer;
+import javax.swing.*;
+
import com.topcoder.client.contestApplet.common.Common;
import com.topcoder.client.contestApplet.common.LocalPreferences;
+import com.topcoder.client.contestant.ProblemComponentModel;
+import com.topcoder.shared.language.Language;
+import com.topcoder.shared.problem.Renderer;
/**
* @author Charles McGarvey
*/
public class VimCoder
{
- /**
- * The name and version of this plugin.
- */
- public final static String version = "VimCoder 0.3";
-
- /**
- * The website of the plugin project.
- */
- public final static String website = "http://www.dogcows.com/vimcoder";
-
-
- /**
- * The first part of the command used to invoke the Vim server.
- */
- private static String vimCommand = "gvim";
-
- /**
- * The path to the main VimCoder directory.
- */
- private static File rootDir;
- static
- {
- if (System.getProperty("os.name").toLowerCase().equals("win"))
- {
- vimCommand = "C:\\WINDOWS\\gvim.bat";
- }
- rootDir = new File(System.getProperty("user.home") +
- System.getProperty("file.separator") + ".vimcoder");
- }
-
-
- /**
- * The panel given to the Arena applet when it is requested.
- */
- private JPanel panel;
-
- /**
- * The text widget where log messages are appended.
- */
- private JTextArea logArea;
-
- /**
- * The current editor object (or null if there is none).
- */
- private Editor editor;
-
- /**
- * The configuration panel.
- */
- private JDialog configDialog;
-
-
- /**
- * The key for the vim command preference.
- */
- private final static String VIMCOMMAND = "com.dogcows.VimCoder.config.vimcommand";
-
- /**
- * The key for the root directory preference.
- */
- private final static String ROOTDIR = "com.dogcows.VimCoder.config.rootdir";
-
- /**
- * The preferences object for storing plugin settings.
- */
- private static LocalPreferences prefs = LocalPreferences.getInstance();
-
-
- /**
- * Get the command for invoking vim.
- * @return The command.
- */
- public static String getVimCommand()
- {
- return vimCommand;
- }
-
- /**
- * Get the storage directory.
- * @return The directory.
- */
- public static File getStorageDirectory()
- {
- return rootDir;
- }
-
-
- /**
- * Instantiate the entry point of the editor plugin. Sets up the log widget
- * and panel.
- */
- public VimCoder()
- {
- logArea = new JTextArea();
- logArea.setForeground(Color.GREEN);
- logArea.setBackground(Color.BLACK);
- logArea.setEditable(false);
- Font font = new Font("Courier", Font.PLAIN, 12);
- if (font != null) logArea.setFont(font);
-
- panel = new JPanel(new BorderLayout());
- panel.add(new JScrollPane(logArea), BorderLayout.CENTER);
- }
-
-
- /**
- * Called by the Arena when the plugin is about to be used.
- */
- public void startUsing()
- {
- Runnable task = new Runnable()
- {
- public void run()
- {
- logArea.setText("");
- }
- };
- if (SwingUtilities.isEventDispatchThread())
- {
- task.run();
- }
- else
- {
- SwingUtilities.invokeLater(task);
- }
- loadConfiguration();
- }
-
- /**
- * Called by the Arena when the plugin is no longer needed.
- */
- public void stopUsing()
- {
- editor = null;
- }
-
- /**
- * Called by the Arena to obtain the editor panel which we will use to show
- * log messages.
- * @return The editor panel.
- */
- public JPanel getEditorPanel()
- {
- return panel;
- }
-
- /**
- * Called by the Arena to obtain the current source. This happens when the
- * user is saving, compiling, and/or submitting.
- * @return The current source code.
- * @throws Exception If the source file edited by Vim couldn't be read.
- */
- public String getSource() throws Exception
- {
- try
- {
- String source = editor.getSource();
- logInfo("Source code uploaded to server.");
- return source;
- }
- catch (Exception exception)
- {
- logError("Failed to get source code: " +
- exception.getLocalizedMessage());
- throw exception;
- }
- }
-
- /**
- * Called by the Arena to pass the source it has.
- * @param source The source code.
- */
- public void setSource(String source)
- {
- try
- {
- editor.setSource(source);
- logInfo("Source code downloaded from server.");
- }
- catch (Exception exception)
- {
- logError("Failed to save the source given by the server: " +
- exception.getLocalizedMessage());
- return;
- }
- }
-
- /**
- * Called by the Arena to pass along information about the current problem.
- * @param component A container for the particulars of the problem.
- * @param language The currently selected language.
- * @param renderer A helper object to help format the problem statement.
- */
- public void setProblemComponent(ProblemComponentModel component,
- Language language,
- Renderer renderer)
- {
- try
- {
- editor = new Editor(component, language, renderer);
- }
- catch (Exception exception)
- {
- logError("An error occured while loading the problem: " +
- exception.getLocalizedMessage());
- }
- }
-
- /**
- * Called by the Arena when it's time to show our configuration panel.
- */
- public void configure()
- {
- loadConfiguration();
-
- configDialog = new JDialog();
- Container pane = configDialog.getContentPane();
-
- pane.setPreferredSize(new Dimension(550, 135));
- pane.setLayout(new GridBagLayout());
- pane.setForeground(Common.FG_COLOR);
- pane.setBackground(Common.WPB_COLOR);
- GridBagConstraints c = new GridBagConstraints();
-
- JLabel rootDirLabel = new JLabel("Storage Directory:", SwingConstants.RIGHT);
- rootDirLabel.setForeground(Common.FG_COLOR);
- c.fill = GridBagConstraints.HORIZONTAL;
- c.insets = new Insets(5, 5, 5, 5);
- c.gridx = 0;
- c.gridy = 0;
- c.gridwidth = 1;
- pane.add(rootDirLabel, c);
-
- final JTextField rootDirField = new JTextField(rootDir.getPath(), 25);
- c.gridx = 1;
- c.gridy = 0;
- pane.add(rootDirField, c);
-
- JButton browseButton = new JButton("Browse");
- c.gridx = 2;
- c.gridy = 0;
- c.anchor = GridBagConstraints.BASELINE_LEADING;
- pane.add(browseButton, c);
-
- JLabel vimCommandLabel = new JLabel("Vim Command:", SwingConstants.RIGHT);
- vimCommandLabel.setForeground(Common.FG_COLOR);
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 0;
- c.gridy = 1;
- pane.add(vimCommandLabel, c);
-
- final JTextField vimCommandField = new JTextField(vimCommand, 25);
- c.gridx = 1;
- c.gridy = 1;
- c.gridwidth = 2;
- pane.add(vimCommandField, c);
-
- JButton closeButton = new JButton("Cancel");
- c.fill = GridBagConstraints.NONE;
- c.gridx = 1;
- c.gridy = 2;
- c.gridwidth = 1;
- c.anchor = GridBagConstraints.EAST;
- pane.add(closeButton, c);
-
- JButton saveButton = new JButton("Save");
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 2;
- c.gridy = 2;
- c.anchor = GridBagConstraints.EAST;
- pane.add(saveButton, c);
- configDialog.getRootPane().setDefaultButton(saveButton);
-
- browseButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent actionEvent)
- {
- JFileChooser chooser = new JFileChooser();
- chooser.setCurrentDirectory(new File("."));
- chooser.setDialogTitle("Choose Storage Directory");
- chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
- chooser.setAcceptAllFileFilterUsed(false);
-
- if (chooser.showOpenDialog(configDialog) == JFileChooser.APPROVE_OPTION)
- {
- rootDirField.setText(chooser.getSelectedFile().getPath());
- }
- }
- });
-
- closeButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent actionEvent)
- {
- configDialog.dispose();
- }
- });
-
- saveButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent actionEvent)
- {
- prefs.setProperty(VIMCOMMAND, vimCommandField.getText());
- prefs.setProperty(ROOTDIR, rootDirField.getText());
- configDialog.dispose();
- }
- });
-
- configDialog.setTitle("VimCoder Preferences");
- configDialog.pack();
- configDialog.setLocationByPlatform(true);
- configDialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
- configDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- configDialog.setVisible(true);
- }
-
-
- /**
- * Load the local preferences related to this plugin.
- */
- private void loadConfiguration()
- {
- String vc = prefs.getProperty(VIMCOMMAND);
- if (vc != null) vimCommand = vc;
-
- String dir = prefs.getProperty(ROOTDIR);
- if (dir != null) rootDir = new File(dir);
- }
-
-
- /**
- * A generic logging function, appends text to the text area. A timestamp
- * is also prepended to the next text.
- * @param what The text to append.
- */
- private void log(final String what)
- {
- Runnable task = new Runnable()
- {
- public void run()
- {
- SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss");
- logArea.append(format.format(new Date()) + ", " + what);
- }
- };
- if (SwingUtilities.isEventDispatchThread())
- {
- task.run();
- }
- else
- {
- SwingUtilities.invokeLater(task);
- }
- }
-
- /**
- * Output non-critical messages to the log.
- * @param what The text of the message.
- */
- private void logInfo(String what)
- {
- log(" INFO: " + what + System.getProperty("line.separator"));
- }
-
- /**
- * Output critical messages and errors to the log.
- * @param what The text of the message.
- */
- private void logError(String what)
- {
- log("ERROR: " + what + System.getProperty("line.separator"));
- }
+ /**
+ * The name and version of this plugin.
+ */
+ public final static String version = "VimCoder 0.3";
+
+ /**
+ * The website of the plugin project.
+ */
+ public final static String website = "http://www.dogcows.com/vimcoder";
+
+
+ /**
+ * The first part of the command used to invoke the Vim server.
+ */
+ private static String vimCommand = "gvim";
+
+ /**
+ * The path to the main VimCoder directory.
+ */
+ private static File rootDir;
+ static
+ {
+ if (System.getProperty("os.name").toLowerCase().equals("win"))
+ {
+ vimCommand = "C:\\WINDOWS\\gvim.bat";
+ }
+ rootDir = new File(System.getProperty("user.home") +
+ System.getProperty("file.separator") + ".vimcoder");
+ }
+
+
+ /**
+ * The panel given to the Arena applet when it is requested.
+ */
+ private JPanel panel;
+
+ /**
+ * The text widget where log messages are appended.
+ */
+ private JTextArea logArea;
+
+ /**
+ * The current editor object (or null if there is none).
+ */
+ private Editor editor;
+
+ /**
+ * The configuration panel.
+ */
+ private JDialog configDialog;
+
+
+ /**
+ * The key for the vim command preference.
+ */
+ private final static String VIMCOMMAND = "com.dogcows.VimCoder.config.vimcommand";
+
+ /**
+ * The key for the root directory preference.
+ */
+ private final static String ROOTDIR = "com.dogcows.VimCoder.config.rootdir";
+
+ /**
+ * The preferences object for storing plugin settings.
+ */
+ private static LocalPreferences prefs = LocalPreferences.getInstance();
+
+
+ /**
+ * Get the command for invoking vim.
+ * @return The command.
+ */
+ public static String getVimCommand()
+ {
+ return vimCommand;
+ }
+
+ /**
+ * Get the storage directory.
+ * @return The directory.
+ */
+ public static File getStorageDirectory()
+ {
+ return rootDir;
+ }
+
+
+ /**
+ * Instantiate the entry point of the editor plugin.
+ * Sets up the log widget and panel.
+ */
+ public VimCoder()
+ {
+ logArea = new JTextArea();
+ logArea.setForeground(Color.GREEN);
+ logArea.setBackground(Color.BLACK);
+ logArea.setEditable(false);
+ Font font = new Font("Courier", Font.PLAIN, 12);
+ if (font != null) logArea.setFont(font);
+
+ panel = new JPanel(new BorderLayout());
+ panel.add(new JScrollPane(logArea), BorderLayout.CENTER);
+ }
+
+
+ /**
+ * Called by the Arena when the plugin is about to be used.
+ */
+ public void startUsing()
+ {
+ Runnable task = new Runnable()
+ {
+ public void run()
+ {
+ logArea.setText("");
+ }
+ };
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ task.run();
+ }
+ else
+ {
+ SwingUtilities.invokeLater(task);
+ }
+ loadConfiguration();
+ }
+
+ /**
+ * Called by the Arena when the plugin is no longer needed.
+ */
+ public void stopUsing()
+ {
+ editor = null;
+ }
+
+ /**
+ * Called by the Arena to obtain the editor panel which we will use to
+ * show log messages.
+ * @return The editor panel.
+ */
+ public JPanel getEditorPanel()
+ {
+ return panel;
+ }
+
+ /**
+ * Called by the Arena to obtain the current source.
+ * This happens when the user is saving, compiling, and/or submitting.
+ * @return The current source code.
+ * @throws Exception If the source file edited by Vim couldn't be read.
+ */
+ public String getSource() throws Exception
+ {
+ try
+ {
+ String source = editor.getSource();
+ logInfo("Source code uploaded to server.");
+ return source;
+ }
+ catch (Exception exception)
+ {
+ logError("Failed to get source code: " +
+ exception.getLocalizedMessage());
+ throw exception;
+ }
+ }
+
+ /**
+ * Called by the Arena to pass the source it has.
+ * @param source The source code.
+ */
+ public void setSource(String source)
+ {
+ try
+ {
+ editor.setSource(source);
+ logInfo("Source code downloaded from server.");
+ }
+ catch (Exception exception)
+ {
+ logError("Failed to save the source given by the server: " +
+ exception.getLocalizedMessage());
+ return;
+ }
+ }
+
+ /**
+ * Called by the Arena to pass along information about the current
+ * problem.
+ * @param component A container for the particulars of the problem.
+ * @param language The currently selected language.
+ * @param renderer A helper object to help format the problem
+ * statement.
+ */
+ public void setProblemComponent(ProblemComponentModel component,
+ Language language,
+ Renderer renderer)
+ {
+ try
+ {
+ editor = new Editor(component, language, renderer);
+ }
+ catch (Exception exception)
+ {
+ logError("An error occured while loading the problem: " +
+ exception.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Called by the Arena when it's time to show our configuration panel.
+ */
+ public void configure()
+ {
+ loadConfiguration();
+
+ configDialog = new JDialog();
+ Container pane = configDialog.getContentPane();
+
+ pane.setPreferredSize(new Dimension(550, 135));
+ pane.setLayout(new GridBagLayout());
+ pane.setForeground(Common.FG_COLOR);
+ pane.setBackground(Common.WPB_COLOR);
+ GridBagConstraints c = new GridBagConstraints();
+
+ JLabel rootDirLabel = new JLabel("Storage Directory:", SwingConstants.RIGHT);
+ rootDirLabel.setForeground(Common.FG_COLOR);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.insets = new Insets(5, 5, 5, 5);
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 1;
+ pane.add(rootDirLabel, c);
+
+ final JTextField rootDirField = new JTextField(rootDir.getPath(), 25);
+ c.gridx = 1;
+ c.gridy = 0;
+ pane.add(rootDirField, c);
+
+ JButton browseButton = new JButton("Browse");
+ c.gridx = 2;
+ c.gridy = 0;
+ c.anchor = GridBagConstraints.BASELINE_LEADING;
+ pane.add(browseButton, c);
+
+ JLabel vimCommandLabel = new JLabel("Vim Command:", SwingConstants.RIGHT);
+ vimCommandLabel.setForeground(Common.FG_COLOR);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 0;
+ c.gridy = 1;
+ pane.add(vimCommandLabel, c);
+
+ final JTextField vimCommandField = new JTextField(vimCommand, 25);
+ c.gridx = 1;
+ c.gridy = 1;
+ c.gridwidth = 2;
+ pane.add(vimCommandField, c);
+
+ JButton closeButton = new JButton("Cancel");
+ c.fill = GridBagConstraints.NONE;
+ c.gridx = 1;
+ c.gridy = 2;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.EAST;
+ pane.add(closeButton, c);
+
+ JButton saveButton = new JButton("Save");
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 2;
+ c.gridy = 2;
+ c.anchor = GridBagConstraints.EAST;
+ pane.add(saveButton, c);
+ configDialog.getRootPane().setDefaultButton(saveButton);
+
+ browseButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent actionEvent)
+ {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setCurrentDirectory(new File("."));
+ chooser.setDialogTitle("Choose Storage Directory");
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ chooser.setAcceptAllFileFilterUsed(false);
+
+ if (chooser.showOpenDialog(configDialog) == JFileChooser.APPROVE_OPTION)
+ {
+ rootDirField.setText(chooser.getSelectedFile().getPath());
+ }
+ }
+ });
+
+ closeButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent actionEvent)
+ {
+ configDialog.dispose();
+ }
+ });
+
+ saveButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent actionEvent)
+ {
+ prefs.setProperty(VIMCOMMAND, vimCommandField.getText());
+ prefs.setProperty(ROOTDIR, rootDirField.getText());
+ configDialog.dispose();
+ }
+ });
+
+ configDialog.setTitle("VimCoder Preferences");
+ configDialog.pack();
+ configDialog.setLocationByPlatform(true);
+ configDialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
+ configDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ configDialog.setVisible(true);
+ }
+
+
+ /**
+ * Load the local preferences related to this plugin.
+ */
+ private void loadConfiguration()
+ {
+ String vc = prefs.getProperty(VIMCOMMAND);
+ if (vc != null) vimCommand = vc;
+
+ String dir = prefs.getProperty(ROOTDIR);
+ if (dir != null) rootDir = new File(dir);
+ }
+
+
+ /**
+ * A generic logging function, appends text to the text area. A timestamp
+ * is also prepended to the next text.
+ * @param what The text to append.
+ */
+ private void log(final String what)
+ {
+ Runnable task = new Runnable()
+ {
+ public void run()
+ {
+ SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss");
+ logArea.append(format.format(new Date()) + ", " + what);
+ }
+ };
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ task.run();
+ }
+ else
+ {
+ SwingUtilities.invokeLater(task);
+ }
+ }
+
+ /**
+ * Output non-critical messages to the log.
+ * @param what The text of the message.
+ */
+ private void logInfo(String what)
+ {
+ log(" INFO: " + what + System.getProperty("line.separator"));
+ }
+
+ /**
+ * Output critical messages and errors to the log.
+ * @param what The text of the message.
+ */
+ private void logError(String what)
+ {
+ log("ERROR: " + what + System.getProperty("line.separator"));
+ }
}