package com.dogcows;
-import javax.swing.*;
import java.awt.*;
+import java.awt.event.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+
+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.*;
-import com.topcoder.shared.problem.*;
+import com.topcoder.shared.language.Language;
import com.topcoder.shared.problem.Renderer;
/**
*/
public class VimCoder
{
- /**
- *
- */
- private final static String version = "VimCoder 0.1";
- private final static String website = "http://www.dogcows.com/vimcoder";
-
- private JPanel panel;
- private JTextArea logArea;
-
- private Editor editor;
-
-
- 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");
- }
-
-
- private class Editor
- {
- private String id;
- private String name;
-
- private File sourceFile;
- private File directory;
-
-
- public Editor(ProblemComponentModel component, Language language, Renderer renderer) throws IOException
- {
- this.id = String.valueOf(component.getProblem().getProblemID());
- this.name = component.getClassName();
-
- File topDir = new File(System.getProperty("user.home"), ".vimcoder");
- if (!topDir.isDirectory())
- {
- if (!topDir.mkdirs()) throw new IOException(topDir.getPath());
- }
-
- this.directory = new File(topDir, String.valueOf(component.getProblem().getProblemID()));
- if (!directory.isDirectory())
- {
- if (!directory.mkdirs()) throw new IOException(directory.getPath());
- }
-
- String lang = language.getName();
- String ext = languageExtension.get(lang);
-
- HashMap<String,String> terms = new HashMap<String,String>();
- terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language));
- terms.put("CLASSNAME", component.getClassName());
- terms.put("METHODNAME", component.getMethodName());
- terms.put("METHODPARAMS", getMethodParams(component.getParamTypes(), component.getParamNames(), language));
- terms.put("METHODPARAMNAMES", join(component.getParamNames(), ", "));
-
- File problemFile = new File(directory, "Problem.html");
- if (!problemFile.canRead())
- {
- FileWriter writer = new FileWriter(problemFile);
- try
- {
- writer.write(renderer.toHTML(language));
- }
- catch (Exception exception)
- {
- }
- writer.close();
- }
-
- sourceFile = new File(directory, terms.get("CLASSNAME") + "." + ext);
- if (!sourceFile.canRead())
- {
- String text = expandTemplate(readResource(lang + "Template"), terms);
- FileWriter writer = new FileWriter(sourceFile);
- writer.write(text);
- writer.close();
- }
-
- File driverFile = new File(directory, "driver" + "." + ext);
- if (!driverFile.canRead())
- {
- StringBuilder testCases = new StringBuilder();
- if (component.hasTestCases())
- {
- HashMap<String,String> testTerms = new HashMap<String,String>();
- testTerms.putAll(terms);
- String template = readResource(lang + "Test");
- for (TestCase testCase : component.getTestCases())
- {
- testTerms.put("TESTOUTPUT", "\"" + quote(testCase.getOutput()) + "\"");
- testTerms.put("TESTINPUTS", join(testCase.getInput(), ", "));
- testCases.append(expandTemplate(template, testTerms));
- }
- }
- terms.put("TESTCASES", testCases.toString());
-
- String text = expandTemplate(readResource(lang + "Driver"), terms);
- FileWriter writer = new FileWriter(driverFile);
- writer.write(text);
- writer.close();
- }
-
- File makeFile = new File(directory, "Makefile");
- {
- String text = expandTemplate(readResource(lang + "Makefile"), terms);
- FileWriter writer = new FileWriter(makeFile);
- writer.write(text);
- writer.close();
- }
- }
-
- public void setSource(String source) throws IOException
- {
- String actualSource = readFile(sourceFile);
- if (!actualSource.equals(source))
- {
- File actualFile = new File(directory, name);
- FileWriter writer = new FileWriter(actualFile);
- writer.write(source);
- writer.close();
- }
- doVimCommand("--remote-tab-silent", sourceFile.getPath());
- doVimCommand("--remote-send", "<C-\\><C-N>:if search('\\$CARAT\\\\$') != 0<CR>normal df$<CR>endif<CR>:redraw<CR>");
- }
-
- public String getSource() throws IOException
- {
- return readFile(sourceFile) + "\n// Edited by " + version + "\n// " + website + "\n\n";
- }
-
- public void setTextEnabled(boolean enable)
- {
- doVimCommand("--remote-send", "<C-\\><C-N>:set readonly<CR>:echo \"The contest is over.\"<CR>");
- }
-
-
- private boolean doVimCommand(String command, String argument)
- {
- String[] arguments = {argument};
- return doVimCommand(command, arguments);
- }
-
- private boolean doVimCommand(String command, String[] arguments)
- {
- try
- {
- String[] exec = {"gvim", "--servername", "VimCoder" + id, command};
- exec = concat(exec, arguments);
-
- Process child = Runtime.getRuntime().exec(exec);
- if (child.waitFor() == 0)
- {
- return true;
- }
- else
- {
- logError("vim command failed");
- }
- }
- catch (IOException exception)
- {
- logError("failed to launch external vim process");
- return false;
- }
- catch (InterruptedException exception)
- {
- logWarning("interrupted while waiting on vim process");
- }
- return false;
- }
-
- private String getMethodParams(DataType[] types, String[] names, Language language)
- {
- StringBuilder text = new StringBuilder();
-
- text.append(types[0].getDescriptor(language) + " " + names[0]);
- for (int i = 1; i < names.length; ++i)
- {
- text.append(", " + types[i].getDescriptor(language) + " " + names[i]);
- }
-
- return text.toString();
- }
-
- private 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();
- }
-
- private String readResource(String path) throws IOException
- {
- StringBuilder text = new StringBuilder();
-
- InputStream stream = getClass().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();
- }
-
- private String expandTemplate(String template, Map<String,String> terms)
- {
- String text = template;
- for (String key : terms.keySet())
- {
- text = text.replaceAll("\\$" + key + "\\$", quote(terms.get(key)));
- }
- return text;
- }
- }
-
-
- 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 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);
- }
-
-
- public void startUsing()
- {
- logArea.setText("");
- }
-
- public void stopUsing()
- {
- editor = null;
- }
-
- public JPanel getEditorPanel()
- {
- return panel;
- }
-
- public String getSource()
- {
- try
- {
- String source = editor.getSource();
- logInfo("Source code uploaded to server.");
- return source;
- }
- catch (IOException exception)
- {
- logError("failed to open file source file for reading");
- return "";
- }
- }
-
- public void setSource(String source)
- {
- try
- {
- editor.setSource(source);
- logInfo("source set");
- }
- catch (IOException exception)
- {
- logError("failed setting the source");
- return;
- }
- }
-
- public void setProblemComponent(ProblemComponentModel component, Language language, Renderer renderer)
- {
- try
- {
- editor = new Editor(component, language, renderer);
- }
- catch (IOException exception)
- {
- logError("failed while loading the problem");
- }
- }
-
- public void setTextEnabled(Boolean enable)
- {
- editor.setTextEnabled(enable);
- }
-
-
- private void log(String what)
- {
- SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss");
- String time = format.format(new Date());
- logArea.append(time + ", " + what);
- }
-
- private void logInfo(String what)
- {
- log(" INFO: " + what + "\n");
- }
-
- private void logWarning(String what)
- {
- log(" WARN: " + what + "\n");
- }
-
- private void logError(String what)
- {
- log("ERROR: " + what + "\n");
- }
-
-
- 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();
- }
+ /**
+ * The name and version of this plugin.
+ */
+ public final static String version = "VimCoder 0.3.3";
+
+ /**
+ * The website of the plugin project.
+ */
+ public final static String website = "http://www.brokenzipper.com/trac/wiki/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()
+ {
+ final int border = 10;
+ final int inset = 2;
+
+ loadConfiguration();
+
+ configDialog = new JDialog();
+ Container container = configDialog.getContentPane();
+ container.setForeground(Common.FG_COLOR);
+ container.setBackground(Common.WPB_COLOR);
+
+ JPanel pane = new JPanel();
+ container.add(pane);
+
+ BoxLayout boxLayout = new BoxLayout(pane, BoxLayout.Y_AXIS);
+ pane.setLayout(boxLayout);
+ pane.setBorder(BorderFactory.createEmptyBorder(border, border, border, border));
+
+ JPanel fieldPanel = new JPanel(new GridBagLayout());
+ pane.add(fieldPanel);
+ pane.add(Box.createRigidArea(new Dimension(0, border)));
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.insets = new Insets(inset, inset, inset, inset);
+
+ JLabel rootDirLabel = new JLabel("Storage Directory:");
+ rootDirLabel.setForeground(Common.FG_COLOR);
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 1;
+ fieldPanel.add(rootDirLabel, c);
+
+ final JTextField rootDirField = new JTextField(rootDir.getPath());
+ rootDirField.setPreferredSize(new Dimension(0, 24));
+ c.gridx = 1;
+ c.gridy = 0;
+ c.weightx = 1.0;
+ fieldPanel.add(rootDirField, c);
+
+ JButton browseButton = new JButton("Browse");
+ c.gridx = 2;
+ c.gridy = 0;
+ c.weightx = 0.0;
+ c.anchor = GridBagConstraints.BASELINE_LEADING;
+ fieldPanel.add(browseButton, c);
+
+ JLabel vimCommandLabel = new JLabel("Vim Command:");
+ vimCommandLabel.setForeground(Common.FG_COLOR);
+ c.gridx = 0;
+ c.gridy = 1;
+ fieldPanel.add(vimCommandLabel, c);
+
+ final JTextField vimCommandField = new JTextField(vimCommand);
+ vimCommandField.setPreferredSize(new Dimension(0, 24));
+ c.gridx = 1;
+ c.gridy = 1;
+ c.weightx = 1.0;
+ c.gridwidth = 2;
+ fieldPanel.add(vimCommandField, c);
+
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, inset, inset));
+ buttonPanel.setPreferredSize(new Dimension(400, 24 + 2 * inset));
+ pane.add(buttonPanel);
+
+ JButton saveButton = new JButton("Save");
+ buttonPanel.add(saveButton);
+ buttonPanel.add(Box.createRigidArea(new Dimension(1, 0)));
+
+ JButton closeButton = new JButton("Close");
+ buttonPanel.add(closeButton);
+
+ 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());
+ JOptionPane.showMessageDialog(null, "Preferences were saved successfully.");
+ }
+ });
+
+ configDialog.setTitle("VimCoder Preferences");
+ configDialog.pack();
+ configDialog.setLocationRelativeTo(null); // Center dialog in screen.
+ 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"));
+ }
}