]> Dogcows Code - chaz/vimcoder/blob - src/com/dogcows/Editor.java
new build system; fixed white space problems
[chaz/vimcoder] / src / com / dogcows / Editor.java
1
2 package com.dogcows;
3
4 import java.io.BufferedReader;
5 import java.io.File;
6 import java.io.FileReader;
7 import java.io.FileWriter;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.util.*;
11
12 import com.topcoder.client.contestant.ProblemComponentModel;
13 import com.topcoder.shared.language.Language;
14 import com.topcoder.shared.problem.DataType;
15 import com.topcoder.shared.problem.Renderer;
16 import com.topcoder.shared.problem.TestCase;
17
18 /**
19 * @author Charles McGarvey
20 * The TopCoder Arena editor plug-in providing support for Vim.
21 *
22 * Distributable under the terms and conditions of the 2-clause BSD license;
23 * see the file COPYING for a complete text of the license.
24 */
25 public class Editor
26 {
27 /**
28 * The problem ID number.
29 */
30 private String id;
31
32 /**
33 * The name of the class.
34 */
35 private String name;
36
37 /**
38 * The path of the current source file.
39 */
40 private File sourceFile;
41
42 /**
43 * The path of the problem directory.
44 */
45 private File directory;
46
47
48 /**
49 * Map languages names to file extensions.
50 */
51 private static final Map<String,String> languageExtension = new HashMap<String,String>();
52 static
53 {
54 languageExtension.put("Java", "java");
55 languageExtension.put("C++", "cc");
56 languageExtension.put("C#", "cs");
57 languageExtension.put("VB", "vb");
58 languageExtension.put("Python", "py");
59 }
60
61
62 /**
63 * Construct an editor with the problem objects given us by the Arena.
64 * @param component A container for the particulars of the problem.
65 * @param language The currently selected language.
66 * @param renderer A helper object to help format the problem statement.
67 * @throws Exception If the editor could not set itself up.
68 */
69 public Editor(ProblemComponentModel component,
70 Language language,
71 Renderer renderer) throws Exception
72 {
73 this.id = String.valueOf(component.getProblem().getProblemID());
74 this.name = component.getClassName();
75
76 // Make sure the top-level vimcoder directory exists.
77 File topDir = VimCoder.getStorageDirectory();
78 if (!topDir.isDirectory())
79 {
80 if (!topDir.mkdirs()) throw new IOException(topDir.getPath());
81 }
82
83 // Make sure the problem directory exists.
84 this.directory = new File(topDir, id);
85 if (!directory.isDirectory())
86 {
87 if (!directory.mkdirs()) throw new IOException(directory.getPath());
88 }
89
90 String lang = language.getName();
91 String ext = languageExtension.get(lang);
92
93 // Set up the terms used for the template expansion.
94 HashMap<String,String> terms = new HashMap<String,String>();
95 terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language).replaceAll("\\s+", ""));
96 terms.put("CLASSNAME", name);
97 terms.put("METHODNAME", component.getMethodName());
98 terms.put("METHODPARAMS", getMethodParams(component.getParamTypes(),
99 component.getParamNames(),
100 language));
101 terms.put("METHODPARAMNAMES", Util.join(component.getParamNames(), ", "));
102 terms.put("METHODPARAMSTREAMIN", Util.join(component.getParamNames(), " >> "));
103 terms.put("METHODPARAMSTREAMOUT", Util.join(component.getParamNames(), " << "));
104 terms.put("METHODPARAMDECLARES", getMethodParamDeclarations(component.getParamTypes(),
105 component.getParamNames(),
106 language));
107
108 // Write the problem statement as an HTML file in the problem directory.
109 File problemFile = new File(directory, "Problem.html");
110 if (!problemFile.canRead())
111 {
112 FileWriter writer = new FileWriter(problemFile);
113 try
114 {
115 writer.write(renderer.toHTML(language));
116 }
117 finally
118 {
119 writer.close();
120 }
121 }
122
123 // Expand the template for the main class and write it to the current
124 // source file.
125 sourceFile = new File(directory, name + "." + ext);
126 if (!sourceFile.canRead())
127 {
128 String text = Util.expandTemplate(readTemplate(lang + "Template"),
129 terms);
130 FileWriter writer = new FileWriter(sourceFile);
131 writer.write(text);
132 writer.close();
133 }
134
135 // Expand the driver template and write it to a source file.
136 File driverFile = new File(directory, "driver." + ext);
137 if (!driverFile.canRead())
138 {
139 String text = Util.expandTemplate(readTemplate(lang + "Driver"),
140 terms);
141 FileWriter writer = new FileWriter(driverFile);
142 writer.write(text);
143 writer.close();
144 }
145
146 // Write the test cases to a text file. The driver code can read this
147 // file and perform the tests based on what it reads.
148 File testcaseFile = new File(directory, "testcases.txt");
149 if (!testcaseFile.canRead())
150 {
151 StringBuilder text = new StringBuilder();
152 if (component.hasTestCases())
153 {
154 for (TestCase testCase : component.getTestCases())
155 {
156 text.append(testCase.getOutput() + System.getProperty("line.separator"));
157 for (String input : testCase.getInput())
158 {
159 text.append(input + System.getProperty("line.separator"));
160 }
161 }
162 }
163 FileWriter writer = new FileWriter(testcaseFile);
164 writer.write(text.toString());
165 writer.close();
166 }
167
168 // Finally, expand the Makefile template and write it.
169 File makeFile = new File(directory, "Makefile");
170 {
171 String text = Util.expandTemplate(readTemplate(lang + "Makefile"),
172 terms);
173 FileWriter writer = new FileWriter(makeFile);
174 writer.write(text);
175 writer.close();
176 }
177 }
178
179 /**
180 * Save the source code provided by the server, and tell the Vim server to
181 * edit the current source file.
182 * @param source The source code.
183 * @throws Exception If the source couldn't be written or the Vim server
184 * had a problem.
185 */
186 public void setSource(String source) throws Exception
187 {
188 FileWriter writer = new FileWriter(new File(directory, name));
189 writer.write(source);
190 writer.close();
191 sendVimCommand("--remote-tab-silent", sourceFile.getPath());
192 }
193
194 /**
195 * Read the source code from the current source file.
196 * @return The source code.
197 * @throws IOException If the source file could not be read.
198 */
199 public String getSource() throws IOException
200 {
201 return Util.readFile(sourceFile) + "\n// Edited by " +
202 VimCoder.version + "\n// " + VimCoder.website + "\n\n";
203 }
204
205
206 /**
207 * Send a command to the Vim server.
208 * If the server isn't running, it will be started with the name
209 * VIMCODER#### where #### is the problem ID.
210 * @param command The command to send to the server.
211 * @param argument A single argument for the remote command.
212 * @throws Exception If the command could not be sent.
213 */
214 private void sendVimCommand(String command,
215 String argument) throws Exception
216 {
217 String[] arguments = {argument};
218 sendVimCommand(command, arguments);
219 }
220
221 /**
222 * Send a command to the Vim server.
223 * If the server isn't running, it will be started with the name
224 * VIMCODER#### where #### is the problem ID.
225 * @param command The command to send to the server.
226 * @param argument Arguments for the remote command.
227 * @throws Exception If the command could not be sent.
228 */
229 private void sendVimCommand(String command,
230 String[] arguments) throws Exception
231 {
232 String[] vimCommand = VimCoder.getVimCommand().split("\\s");
233 String[] flags = {"--servername", "VimCoder" + id, command};
234 vimCommand = Util.concat(vimCommand, flags);
235 vimCommand = Util.concat(vimCommand, arguments);
236 Process child = Runtime.getRuntime().exec(vimCommand, null, directory);
237
238 /* FIXME: This is a pretty bad hack. The problem is that the Vim
239 * process doesn't fork to the background on some systems, so we
240 * can't wait on the child. At the same time, calling this method
241 * before the previous child could finish initializing the server
242 * may result in multiple editor windows popping up. We'd also
243 * like to be able to get the return code from the child if we can.
244 * The workaround here is to stall the thread for a little while or
245 * until we see that the child exits. If the child never exits
246 * before the timeout, we will assume it is not backgrounding and
247 * that everything worked. This works as long as the Vim server is
248 * able to start within the stall period. */
249 long expire = System.currentTimeMillis() + 250;
250 while (System.currentTimeMillis() < expire)
251 {
252 Thread.yield();
253 try
254 {
255 int exitCode = child.exitValue();
256 if (exitCode != 0) throw new Exception("Vim process returned exit code " + exitCode + ".");
257 break;
258 }
259 catch (IllegalThreadStateException exception)
260 {
261 // The child has not exited; intentionally ignoring exception.
262 }
263 }
264 }
265
266
267 /**
268 * Read a template.
269 * We first look in the storage directory. If we can't find one, we
270 * look among the resources.
271 * @param tName The name of the template.
272 * @return The contents of the template file, or an empty string.
273 */
274 private String readTemplate(String tName)
275 {
276 File templateFile = new File(VimCoder.getStorageDirectory(), tName);
277 try
278 {
279 if (templateFile.canRead()) return Util.readFile(templateFile);
280 return Util.readResource(tName);
281 }
282 catch (IOException exception)
283 {
284 return "";
285 }
286 }
287
288
289 /**
290 * Convert an array of data types to an array of strings according to a
291 * given language.
292 * @param types The data types.
293 * @param language The language to use in the conversion.
294 * @return The array of string representations of the data types.
295 */
296 private String[] getStringTypes(DataType[] types, Language language)
297 {
298 String[] strings = new String[types.length];
299 for (int i = 0; i < types.length; ++i)
300 {
301 strings[i] = types[i].getDescriptor(language).replaceAll("\\s+", "");
302 }
303 return strings;
304 }
305
306 /**
307 * Combine the data types and parameter names into a comma-separated list of
308 * the method parameters.
309 * The result could be used inside the parentheses of a method
310 * declaration.
311 * @param types The data types of the parameters.
312 * @param names The names of the parameters.
313 * @param language The language used for representing the data types.
314 * @return The list of parameters.
315 */
316 private String getMethodParams(DataType[] types,
317 String[] names,
318 Language language)
319 {
320 String[] typeStrings = getStringTypes(types, language);
321 return Util.join(Util.combine(typeStrings, names, " "), ", ");
322 }
323
324 /**
325 * Combine the data types and parameter names into a group of variable
326 * declarations.
327 * Each declaration is separated by a new line and terminated with a
328 * semicolon.
329 * @param types The data types of the parameters.
330 * @param names The names of the parameters.
331 * @param language The language used for representing the data types.
332 * @return The parameters as a block of declarations.
333 */
334 private String getMethodParamDeclarations(DataType[] types,
335 String[] names,
336 Language language)
337 {
338 final String end = ";" + System.getProperty("line.separator");
339 String[] typeStrings = getStringTypes(types, language);
340 return Util.join(Util.combine(typeStrings, names, "\t"), end) + end;
341 }
342 }
343
This page took 0.049215 seconds and 4 git commands to generate.