| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,14 +3,26 @@ | |||
| 3 | 3 | import edu.rice.cs.drjava.model.DJError; | |
| 4 | 4 | import edu.rice.cs.drjava.ui.SmartSourceFilter; | |
| 5 | 5 | import edu.rice.cs.plt.reflect.JavaVersion; | |
| 6 | + import edu.rice.cs.util.ArgumentTokenizer; | ||
| 7 | + | ||
| 6 | 8 | import javax.swing.filechooser.FileFilter; | |
| 7 | 9 | import java.io.File; | |
| 8 | 10 | import java.io.IOException; | |
| 11 | + import java.lang.reflect.Constructor; | ||
| 9 | 12 | import java.util.*; | |
| 10 | 13 | import javax.tools.*; | |
| 11 | 14 | ||
| 12 | - public class JavaxToolsCompiler implements CompilerInterface { | ||
| 13 | 15 | ||
| 16 | + | ||
| 17 | + public class JavaxToolsCompiler implements CompilerInterface { | ||
| 18 | + /** The set of class names that are run as ACM Java Task Force library programs. */ | ||
| 19 | + protected static final Set<String> ACM_PROGRAM_CLASSES = new HashSet<String>(); | ||
| 20 | + static { | ||
| 21 | + Collections.addAll(ACM_PROGRAM_CLASSES, new String[] { | ||
| 22 | + "acm.program.Program", | ||
| 23 | + "acm.graphics.GTurtle" | ||
| 24 | + }); | ||
| 25 | + } | ||
| 14 | 26 | private final JavaCompiler compiler; | |
| 15 | 27 | ||
| 16 | 28 | public JavaxToolsCompiler() { | |
@@ -119,11 +131,249 @@ public List<File> additionalBootClassPathForInteractions() { | |||
| 119 | 131 | return new ArrayList<File>(); | |
| 120 | 132 | } | |
| 121 | 133 | ||
| 134 | + /** Transform the command line to be interpreted into something the Interactions JVM can use. | ||
| 135 | + * This replaces "java MyClass a b c" with Java code to call MyClass.main(new String[]{"a","b","c"}). | ||
| 136 | + * "import MyClass" is not handled here. | ||
| 137 | + * transformCommands should support at least "run", "java" and "applet". | ||
| 138 | + * @param interactionsString unprocessed command line | ||
| 139 | + * @return command line with commands transformed */ | ||
| 122 | 140 | public String transformCommands(String interactionsString) { | |
| 123 | - // TODO: Implement command transformation logic (this is for interpreter) | ||
| 141 | + if (interactionsString.startsWith("java ")) { | ||
| 142 | + interactionsString = transformJavaCommand(interactionsString); | ||
| 143 | + } | ||
| 144 | + else if (interactionsString.startsWith("applet ")) { | ||
| 145 | + interactionsString = transformAppletCommand(interactionsString); | ||
| 146 | + } | ||
| 147 | + else if (interactionsString.startsWith("run ")) { | ||
| 148 | + interactionsString = transformRunCommand(interactionsString); | ||
| 149 | + } | ||
| 124 | 150 | return interactionsString; | |
| 125 | 151 | } | |
| 126 | 152 | ||
| 153 | + public static String transformJavaCommand(String s) { | ||
| 154 | + // check the return type and public access before executing, per bug #1585210 | ||
| 155 | + String command = | ||
| 156 | + "try '{'\n" + | ||
| 157 | + " java.lang.reflect.Method m = {0}.class.getMethod(\"main\", java.lang.String[].class);\n" + | ||
| 158 | + " if (!m.getReturnType().equals(void.class)) throw new java.lang.NoSuchMethodException();\n" + | ||
| 159 | + "'}'\n" + | ||
| 160 | + "catch (java.lang.NoSuchMethodException e) '{'\n" + | ||
| 161 | + " throw new java.lang.NoSuchMethodError(\"main\");\n" + | ||
| 162 | + "'}'\n" + | ||
| 163 | + "{0}.main(new String[]'{'{1}'}');"; | ||
| 164 | + return _transformCommand(s, command); | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + public static String transformAppletCommand(String s) { | ||
| 168 | + return _transformCommand(s,"edu.rice.cs.plt.swing.SwingUtil.showApplet(new {0}({1}), 400, 300);"); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + /** This method performs the "smart run". Unfortunately, we don't get the right static error messages. | ||
| 172 | + * @param s full command line, i.e. "run MyClass 1 2 3" | ||
| 173 | + * @param c class to be run, i.e. MyClass.class | ||
| 174 | + * @throws Throwable if something goes wrong | ||
| 175 | + */ | ||
| 176 | + public static void runCommand(String s, Class<?> c) throws Throwable { | ||
| 177 | + if (s.endsWith(";")) s = _deleteSemiColon(s); | ||
| 178 | + List<String> tokens = ArgumentTokenizer.tokenize(s, true); | ||
| 179 | + String[] args = new String[tokens.size() - 2]; | ||
| 180 | + for (int i = 2; i < tokens.size(); i++) { | ||
| 181 | + String t = tokens.get(i); | ||
| 182 | + args[i - 2] = t.substring(1, t.length() - 1); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + boolean isProgram = false; | ||
| 186 | + boolean isApplet = false; | ||
| 187 | + Class<?> oldC = c; | ||
| 188 | + while(c != null) { | ||
| 189 | + if (ACM_PROGRAM_CLASSES.contains(c.getName())) { isProgram = true; break; } | ||
| 190 | + c = c.getSuperclass(); | ||
| 191 | + } | ||
| 192 | + c = oldC; | ||
| 193 | + if (!isProgram) { | ||
| 194 | + try { | ||
| 195 | + // if this doesn't throw, c is a subclass of Applet | ||
| 196 | + c.asSubclass(java.applet.Applet.class); | ||
| 197 | + isApplet = true; | ||
| 198 | + } catch(ClassCastException cce) { } | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + java.lang.reflect.Method m = null; | ||
| 202 | + if (isApplet) { | ||
| 203 | + try { | ||
| 204 | + m = c.getMethod("main", java.lang.String[].class); | ||
| 205 | + if (!m.getReturnType().equals(void.class)) { m = null; } | ||
| 206 | + } | ||
| 207 | + catch (java.lang.NoSuchMethodException e) { m = null; } | ||
| 208 | + if (m == null) { | ||
| 209 | + java.applet.Applet instance = null; | ||
| 210 | + if (args.length == 0) { | ||
| 211 | + try { | ||
| 212 | + // try default (nullary) constructor first | ||
| 213 | + Constructor<?> ctor = c.getConstructor(); | ||
| 214 | + instance = java.applet.Applet.class.cast(ctor.newInstance()); | ||
| 215 | + } | ||
| 216 | + catch(NoSuchMethodException | InstantiationException | IllegalAccessException nsme) { | ||
| 217 | + instance = null; | ||
| 218 | + } | ||
| 219 | + catch(java.lang.reflect.InvocationTargetException ite) { | ||
| 220 | + if (ite.getCause()!=null) { | ||
| 221 | + throw ite.getCause(); | ||
| 222 | + } | ||
| 223 | + else { | ||
| 224 | + System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); | ||
| 225 | + } | ||
| 226 | + } | ||
| 227 | + if (instance == null) { | ||
| 228 | + try { | ||
| 229 | + // try String[] constructor next | ||
| 230 | + Constructor<?> ctor = c.getConstructor(String[].class); | ||
| 231 | + instance = java.applet.Applet.class.cast(ctor.newInstance(new Object[] { new String[0] })); | ||
| 232 | + } | ||
| 233 | + catch(NoSuchMethodException | InstantiationException | IllegalAccessException nsme) { | ||
| 234 | + instance = null; | ||
| 235 | + } | ||
| 236 | + catch(java.lang.reflect.InvocationTargetException ite) { | ||
| 237 | + if (ite.getCause()!=null) { | ||
| 238 | + throw ite.getCause(); | ||
| 239 | + } | ||
| 240 | + else { | ||
| 241 | + System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); | ||
| 242 | + return; | ||
| 243 | + } | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + if (instance == null) { | ||
| 247 | + System.err.println("Static Error: This applet does not have a default constructor or a constructor "+ | ||
| 248 | + "accepting String[]."); | ||
| 249 | + return; | ||
| 250 | + } | ||
| 251 | + } | ||
| 252 | + else { | ||
| 253 | + try { | ||
| 254 | + // try String[] constructor | ||
| 255 | + Constructor<?> ctor = c.getConstructor(String[].class); | ||
| 256 | + instance = java.applet.Applet.class.cast(ctor.newInstance(new Object[] { args })); | ||
| 257 | + } | ||
| 258 | + catch(NoSuchMethodException | InstantiationException | IllegalAccessException nsme) { | ||
| 259 | + instance = null; | ||
| 260 | + } | ||
| 261 | + catch(java.lang.reflect.InvocationTargetException ite) { | ||
| 262 | + if (ite.getCause()!=null) { | ||
| 263 | + throw ite.getCause(); | ||
| 264 | + } | ||
| 265 | + else { | ||
| 266 | + System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); | ||
| 267 | + return; | ||
| 268 | + } | ||
| 269 | + } | ||
| 270 | + if (instance == null) { | ||
| 271 | + System.err.println("Static Error: This applet does not have a constructor accepting String[]."); | ||
| 272 | + return; | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + edu.rice.cs.plt.swing.SwingUtil.showApplet(instance, 400, 300); | ||
| 276 | + } | ||
| 277 | + } | ||
| 278 | + else { | ||
| 279 | + try { | ||
| 280 | + m = c.getMethod("main", java.lang.String[].class); | ||
| 281 | + if (!m.getReturnType().equals(void.class)) { | ||
| 282 | + System.err.println("Static Error: This class does not have a static void main method accepting String[]."); | ||
| 283 | + m = null; | ||
| 284 | + } | ||
| 285 | + } | ||
| 286 | + catch (java.lang.NoSuchMethodException e) { | ||
| 287 | + System.err.println("Static Error: This class does not have a static void main method accepting String[]."); | ||
| 288 | + m = null; | ||
| 289 | + } | ||
| 290 | + } | ||
| 291 | + if (m != null) { | ||
| 292 | + if (isProgram) { | ||
| 293 | + String[] newArgs = new String[args.length+1]; | ||
| 294 | + newArgs[0] = "code="+c.getName(); | ||
| 295 | + System.arraycopy(args, 0, newArgs, 1, args.length); | ||
| 296 | + args = newArgs; | ||
| 297 | + } | ||
| 298 | + try { | ||
| 299 | + m.setAccessible(true); | ||
| 300 | + m.invoke(null, new Object[] { args }); | ||
| 301 | + } | ||
| 302 | + catch(SecurityException | IllegalAccessException se) { | ||
| 303 | + System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); | ||
| 304 | + } | ||
| 305 | + catch(java.lang.reflect.InvocationTargetException ite) { | ||
| 306 | + if (ite.getCause()!=null) { | ||
| 307 | + throw ite.getCause(); | ||
| 308 | + } | ||
| 309 | + else { | ||
| 310 | + System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); | ||
| 311 | + } | ||
| 312 | + } | ||
| 313 | + } | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | + /** This is a method that automatically detects if | ||
| 317 | + * a) the class is an ACM Java Task Force program (subclass of acm.program.Program) | ||
| 318 | + * b) an applet | ||
| 319 | + * c) a class with a static main method | ||
| 320 | + * | ||
| 321 | + * If a), then DrJava inserts "code=MyClass" as argument 0. | ||
| 322 | + * If b), then DrJava performs the same as "applet MyClass" (see above). | ||
| 323 | + * If c), then DrJava executes MyClass.main (traditional java behavior). | ||
| 324 | + * | ||
| 325 | + * @param s the command to be transformed | ||
| 326 | + * @return the transformed command | ||
| 327 | + */ | ||
| 328 | + public String transformRunCommand(String s) { | ||
| 329 | + if (s.endsWith(";")) s = _deleteSemiColon(s); | ||
| 330 | + List<String> args = ArgumentTokenizer.tokenize(s, true); | ||
| 331 | + final String classNameWithQuotes = args.get(1); // this is "MyClass" | ||
| 332 | + final String className = | ||
| 333 | + classNameWithQuotes.substring(1, classNameWithQuotes.length() - 1); // removes quotes, becomes MyClass | ||
| 334 | + | ||
| 335 | + // we pass MyClass.class just to get a "Static Error: Undefined class 'MyClass'" | ||
| 336 | + String ret = JavacCompiler.class.getName()+".runCommand(\""+s.toString()+"\", "+className+".class)"; | ||
| 337 | + // System.out.println(ret); | ||
| 338 | + return ret; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + /** Assumes a trimmed String. Returns a string of the call that the interpreter can use. | ||
| 342 | + * The arguments get formatted as comma-separated list of strings enclosed in quotes. | ||
| 343 | + * Example: _transformCommand("java MyClass arg1 arg2 arg3", "{0}.main(new String[]'{'{1}'}');") | ||
| 344 | + * returns "MyClass.main(new String[]{\"arg1\",\"arg2\",\"arg3\"});" | ||
| 345 | + * NOTE: the command to run is constructed using {@link java.text.MessageFormat}. That means that certain characters, | ||
| 346 | + * single quotes and curly braces, for example, are special. To write single quotes, you need to double them. | ||
| 347 | + * To write curly braces, you need to enclose them in single quotes. Example: | ||
| 348 | + * MessageFormat.format("Abc {0} ''foo'' '{'something'}'", "def") returns "Abc def 'foo' {something}". | ||
| 349 | + * @param s the command line, either "java MyApp arg1 arg2 arg3" or "applet MyApplet arg1 arg2 arg3" | ||
| 350 | + * @param command the command to execute, with {0} marking the place for the class name and {1} the place for the arguments | ||
| 351 | + * @return the transformed command | ||
| 352 | + */ | ||
| 353 | + protected static String _transformCommand(String s, String command) { | ||
| 354 | + if (s.endsWith(";")) s = _deleteSemiColon(s); | ||
| 355 | + List<String> args = ArgumentTokenizer.tokenize(s, true); | ||
| 356 | + final String classNameWithQuotes = args.get(1); // this is "MyClass" | ||
| 357 | + final String className = classNameWithQuotes.substring(1, classNameWithQuotes.length() - 1); // removes quotes, becomes MyClass | ||
| 358 | + final StringBuilder argsString = new StringBuilder(); | ||
| 359 | + boolean seenArg = false; | ||
| 360 | + for (int i = 2; i < args.size(); i++) { | ||
| 361 | + if (seenArg) argsString.append(","); | ||
| 362 | + else seenArg = true; | ||
| 363 | + argsString.append(args.get(i)); | ||
| 364 | + } | ||
| 365 | + return java.text.MessageFormat.format(command, className, argsString.toString()); | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + /** Deletes the last character of a string. Assumes semicolon at the end, but does not check. Helper | ||
| 369 | + * for _transformCommand(String,String). | ||
| 370 | + * @param s the String containing the semicolon | ||
| 371 | + * @return a substring of s with one less character | ||
| 372 | + */ | ||
| 373 | + protected static String _deleteSemiColon(String s) { return s.substring(0, s.length() - 1); } | ||
| 374 | + | ||
| 375 | + | ||
| 376 | + | ||
| 127 | 377 | public boolean isSourceFileForThisCompiler(File f) { | |
| 128 | 378 | if (f == null) return false; | |
| 129 | 379 | String fileName = f.getName(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments