X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fvimcoder;a=blobdiff_plain;f=src%2Fcom%2Fdogcows%2FEditor.java;fp=src%2Fcom%2Fdogcows%2FEditor.java;h=19cee34defaae895f389d69cadb070bd5e016bb5;hp=87403240fa43c7de09fcd36b3035729dabadb312;hb=4afcc19722d6dde23c36f9491ebab9f7b6e38e8b;hpb=97ec47d986d74bf871e5ae0bef04fec277fadc96 diff --git a/src/com/dogcows/Editor.java b/src/com/dogcows/Editor.java index 8740324..19cee34 100644 --- a/src/com/dogcows/Editor.java +++ b/src/com/dogcows/Editor.java @@ -21,337 +21,335 @@ import com.topcoder.shared.problem.TestCase; */ public class Editor { - /** - * The problem ID number. - */ - private String id; - - /** - * The name of the class. - */ - private String name; - - /** - * The name of the contest. - */ - private String contestName; - - /** - * The point value. - */ - private String points; - - /** - * 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 languageExtension = new HashMap(); - 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(); - this.contestName = component.getProblem().getRound().getContestName().replaceAll(" ", "-"); - this.points = String.valueOf(component.getPoints().intValue()); - - // 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. - File newStyleDirectory = new File(new File(topDir, contestName), points); - File oldStyleDirectory = new File(topDir, id); - if (newStyleDirectory.isDirectory()) - { - this.directory = newStyleDirectory; - } - else if (oldStyleDirectory.isDirectory()) - { - this.directory = oldStyleDirectory; - } - else if (VimCoder.isContestDirNames()) - { - this.directory = newStyleDirectory; - if (!directory.mkdirs()) throw new IOException(directory.getPath()); - } - else - { - this.directory = oldStyleDirectory; - 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 terms = new HashMap(); - terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language)); - 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)); - terms.put("VIMCODER", VimCoder.version); - - // 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. - this.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"); - if (!makeFile.canRead()) - { - 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); - } - - - /** - * 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() + 2500; - 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); - } - 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 name of the contest. + */ + private String contestName; + + /** + * The point value. + */ + private String points; + + /** + * 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 languageExtension = new HashMap(); + 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(); + this.contestName = component.getProblem().getRound().getContestName().replaceAll(" ", "-"); + this.points = String.valueOf(component.getPoints().intValue()); + + // 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. + File newStyleDirectory = new File(new File(topDir, contestName), points); + File oldStyleDirectory = new File(topDir, id); + if (newStyleDirectory.isDirectory()) + { + this.directory = newStyleDirectory; + } + else if (oldStyleDirectory.isDirectory()) + { + this.directory = oldStyleDirectory; + } + else if (VimCoder.isContestDirNames()) + { + this.directory = newStyleDirectory; + if (!directory.mkdirs()) throw new IOException(directory.getPath()); + } + else + { + this.directory = oldStyleDirectory; + 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 terms = new HashMap(); + terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language)); + 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)); + terms.put("VIMCODER", VimCoder.version); + + // 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. + this.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"); + if (!makeFile.canRead()) + { + 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); + } + + + /** + * 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() + 2500; + 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); + } + 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; + } } -// vim:noet:ts=8 +// vim:et:ts=8:sts=4:sw=4