]> Dogcows Code - chaz/vimcoder/commitdiff
small cleanups; documented the code
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Fri, 12 Nov 2010 23:18:38 +0000 (16:18 -0700)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Fri, 12 Nov 2010 23:18:38 +0000 (16:18 -0700)
see #1

src/com/dogcows/Editor.java
src/com/dogcows/Util.java [new file with mode: 0644]
src/com/dogcows/Utility.java [deleted file]
src/com/dogcows/VimCoder.java

index ddb7ccfe69043010dac3787b77a3bb15297efc4d..3bc81d2b3e1b2e51b2c947d1dbb4809ce24eddaf 100644 (file)
@@ -17,16 +17,37 @@ import com.topcoder.shared.problem.TestCase;
 
 /**
  * @author Charles McGarvey
- *
+ * The TopCoder Arena editor plug-in providing support for Vim.
+ * 
+ * Distributable under the terms and conditions of the 2-clause BSD license;
+ * see the file COPYING for a complete text of the license.
  */
 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
     {
@@ -38,19 +59,28 @@ public class Editor
     }
 
     
+    /**
+     * 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 IOException
+                  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 = new File(System.getProperty("user.home"), ".vimcoder");
         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())
         {
@@ -60,20 +90,22 @@ public class Editor
         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("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", Utility.join(component.getParamNames(), ", "));
-        terms.put("METHODPARAMSTREAMIN", Utility.join(component.getParamNames(), " >> "));
-        terms.put("METHODPARAMSTREAMOUT", Utility.join(component.getParamNames(), " << "));
+        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())
         {
@@ -82,22 +114,37 @@ public class Editor
             {
                 writer.write(renderer.toHTML(language));
             }
-            catch (Exception exception)
+            finally
             {
+                writer.close();
             }
-            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 = Utility.expandTemplate(Utility.readResource(lang + "Template"),
-                                         terms);
+            String text = Util.expandTemplate(Util.readResource(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(Util.readResource(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())
         {
@@ -118,19 +165,10 @@ public class Editor
             writer.close();
         }
         
-        File driverFile = new File(directory, "driver." + ext);
-        if (!driverFile.canRead())
-        {
-            String text = Utility.expandTemplate(Utility.readResource(lang + "Driver"),
-                                                 terms);
-            FileWriter writer = new FileWriter(driverFile);
-            writer.write(text);
-            writer.close();
-        }
-        
+        // Finally, expand the Makefile template and write it.
         File makeFile = new File(directory, "Makefile");
         {
-            String text = Utility.expandTemplate(Utility.readResource(lang + "Makefile"),
+            String text = Util.expandTemplate(Util.readResource(lang + "Makefile"),
                                          terms);
             FileWriter writer = new FileWriter(makeFile);
             writer.write(text);
@@ -138,36 +176,75 @@ public class Editor
         }
     }
 
+    /**
+     * 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();
-        doVimCommand("--remote-tab-silent", sourceFile.getPath());
+        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 Utility.readFile(sourceFile) + "\n// Edited by " + VimCoder.version + "\n// " + VimCoder.website + "\n\n";
+        return Util.readFile(sourceFile) + "\n// Edited by " +
+        VimCoder.version + "\n// " + VimCoder.website + "\n\n";
     }
     
     
-    private void doVimCommand(String command, String argument) throws Exception
+    /**
+     * 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};
-        doVimCommand(command, arguments);
+        sendVimCommand(command, arguments);
     }
     
-    private void doVimCommand(String command, String[] arguments) throws Exception
+    /**
+     * 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[] exec = {"gvim", "--servername", "VimCoder" + id,
-                         command};
-        exec = Utility.concat(exec, arguments);
+        String[] exec = {"gvim", "--servername", "VimCoder" + id, command};
+        exec = Util.concat(exec, arguments);
         Process child = Runtime.getRuntime().exec(exec, null, directory);
         
-        long expire = System.currentTimeMillis() + 500;
+        /* 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.  This all
+         * works very well in practice, but perhaps there's a better way... */
+        long expire = System.currentTimeMillis() + 250;
         while (System.currentTimeMillis() < expire)
         {
+            Thread.yield();
             try
             {
                 int exitCode = child.exitValue();
@@ -176,38 +253,61 @@ public class Editor
             }
             catch (IllegalThreadStateException exception)
             {
+                // The child has not exited; intentionally ignoring exception.
             }
-            Thread.yield();
         }
     }
     
+    /**
+     * 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)
     {
-        StringBuilder text = new StringBuilder();
-        
-        text.append(types[0].getDescriptor(language).replaceAll("\\s+", "") + " " + names[0]);
-        for (int i = 1; i < names.length; ++i)
-        {
-            text.append(", " + types[i].getDescriptor(language).replaceAll("\\s+", "") + " " + names[i]);
-        }
-        
-        return text.toString();
+        String[] typeStrings = getStringTypes(types, language);
+        return Util.join(Util.combine(typeStrings, names, " "), ", ");
     }
     
-    private String getMethodParamDeclarations (DataType[] types,
+    /**
+     * 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)
     {
-        StringBuilder text = new StringBuilder();
-        
-        for (int i = 0; i < names.length; ++i)
-        {
-            text.append(types[i].getDescriptor(language).replaceAll("\\s+", "") + "\t" + names[i] + ";" + System.getProperty("line.separator"));
-        }
-        
-        return text.toString();
+        final String end = ";" + System.getProperty("line.separator");
+        String[] typeStrings = getStringTypes(types, language);
+        return Util.join(Util.combine(typeStrings, names, "\t"), end) + end;
     }
 }
 
diff --git a/src/com/dogcows/Util.java b/src/com/dogcows/Util.java
new file mode 100644 (file)
index 0000000..bc5bdb8
--- /dev/null
@@ -0,0 +1,158 @@
+
+package com.dogcows;
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * @author Charles McGarvey
+ * The TopCoder Arena editor plug-in providing support for Vim.
+ * 
+ * Distributable under the terms and conditions of the 2-clause BSD license;
+ * see the file COPYING for a complete text of the license.
+ */
+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;
+    }
+
+    /**
+     * 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;
+    }
+}
+
diff --git a/src/com/dogcows/Utility.java b/src/com/dogcows/Utility.java
deleted file mode 100644 (file)
index 9b4c7cc..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-
-package com.dogcows;
-
-import java.io.*;
-import java.util.Arrays;
-import java.util.Map;
-
-/**
- * @author Charles McGarvey
- *
- */
-public abstract class Utility
-{
-
-    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;
-    }
-    
-    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();
-    }
-    
-    public static String quote(String a)
-    {
-        a = a.replaceAll("\\\\", "\\\\\\\\");
-        a = a.replaceAll("\"",   "\\\\\\\"");
-        return a;
-    }
-    
-    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();
-    }
-    
-    public static String readResource(String path) throws IOException
-    {
-        StringBuilder text = new StringBuilder();
-        
-        InputStream stream = Utility.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();
-    }
-    
-    public static String expandTemplate(String template, Map<String,String> terms)
-    {
-        String text = template;
-        for (String key : terms.keySet())
-        {
-            text = text.replaceAll("\\$" + key + "\\$",
-                                   Utility.quote(terms.get(key)));
-        }
-        return text;
-    }
-}
-
index 1fb9bab8e774debc5ee7f753213b59a5217dc05a..01394a501aa27e60337990d5a6e5acf6789bf543 100644 (file)
@@ -21,17 +21,36 @@ import com.topcoder.shared.problem.Renderer;
 public class VimCoder
 {
     /**
-     * 
+     * The name and version of this plugin.
      */
     public final static String     version = "VimCoder 0.1";
+    
+    /**
+     * The website of the plugin project.
+     */
     public final static String     website = "http://www.dogcows.com/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;
 
    
+    /**
+     * Instantiate the entry point of the editor plugin.  Sets up the log widget
+     * and panel.
+     */
     public VimCoder()
     {
         logArea = new JTextArea();
@@ -46,6 +65,9 @@ public class VimCoder
     }
 
     
+    /**
+     * Called by the Arena when the plugin is about to be used.
+     */
     public void startUsing()
     {
         System.out.println("startUsing");
@@ -66,18 +88,32 @@ public class VimCoder
         }
     }
     
+    /**
+     * Called by the Arena when the plugin is no longer needed.
+     */
     public void stopUsing()
     {
         System.out.println("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()
     {
         System.out.println("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
     {
         System.out.println("getSource");
@@ -89,11 +125,16 @@ public class VimCoder
         }
         catch (Exception exception)
         {
-            logError("Failed to get source code: " + exception.getLocalizedMessage());
+            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)
     {
         System.out.println("setSource: " + source);
@@ -104,11 +145,18 @@ public class VimCoder
         }
         catch (Exception exception)
         {
-            logError("Failed to save the source given by the server: " + exception.getLocalizedMessage());
+            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)
@@ -120,11 +168,17 @@ public class VimCoder
         }
         catch (Exception exception)
         {
-            logError("An error occured while loading the problem: " + exception.getLocalizedMessage());
+            logError("An error occured while loading the problem: " +
+                     exception.getLocalizedMessage());
         }
     }
 
     
+    /**
+     * 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()
@@ -145,33 +199,31 @@ public class VimCoder
         }
     }
     
+    /**
+     * 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 potentially important messages to the log.
+     * @param what The text of the message.
+     */
     private void logWarning(String what)
     {
         log(" WARN: " + 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"));
     }
-    
-    
-    public static void main(String args[])
-    {
-        VimCoder plugin = new VimCoder();
-        
-        JFrame frame = new JFrame("VimCoder");
-        frame.add(plugin.getEditorPanel());
-        frame.setSize(640, 480);
-        frame.setVisible(true);
-        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-        
-        plugin.startUsing();
-    }
 }
 
This page took 0.043949 seconds and 4 git commands to generate.