- A Concurrent Affair - https://www.concurrentaffair.org -

Compiled “Run” Commands in DrJava

I had mentioned a while ago that the way we put together the commands that get interpreted when the user presses the “Run” button [1] or types in java or applet or run in the Interactions Pane is very messy.

I had told myself that I should try to rewrite this as Java code that doesn’t get interpreted in our Interactions Pane, but that actually gets statically compiled. I did that, and the code was a whole lot more maintainable. Compare this code to the previous code [1]:

/** This method performs the "smart run". Unfortunately, we don't get the right static error messages.
* @param s full command line, i.e. "run MyClass 1 2 3"
* @param c class to be run, i.e. MyClass.class
*/
@SuppressWarnings("unchecked")
public static void runCommand(String s, Class c) throws Throwable {
if (s.endsWith(";")) s = _deleteSemiColon(s);
List tokens = ArgumentTokenizer.tokenize(s, true);
final String classNameWithQuotes = tokens.get(1); // this is "MyClass"
final String className =
classNameWithQuotes.substring(1, classNameWithQuotes.length() - 1); // removes quotes, becomes MyClass
String[] args = new String[tokens.size() - 2];
for (int i = 2; i < tokens.size(); i++) { String t = tokens.get(i); args[i - 2] = t.substring(1, t.length() - 1); } boolean isProgram = false; boolean isApplet = false; Class oldC = c; while(c != null) { if ("acm.program.Program".equals(c.getName()) || "acm.graphics.GTurtle".equals(c.getName())) { isProgram = true; break; } c = c.getSuperclass(); } c = oldC; if (!isProgram) { try { // if this doesn't throw, c is a subclass of Applet c.asSubclass(java.applet.Applet.class); isApplet = true; } catch(ClassCastException cce) { } } java.lang.reflect.Method m = null; if (isApplet) { try { m = c.getMethod("main", java.lang.String[].class); if (!m.getReturnType().equals(void.class)) { m = null; } } catch (java.lang.NoSuchMethodException e) { m = null; } if (m==null) { java.applet.Applet instance = null; if (args.length==0) { try { // try default (nullary) constructor first Constructor ctor = c.getConstructor(); instance = java.applet.Applet.class.cast(ctor.newInstance()); } catch(NoSuchMethodException nsme) { instance = null; } catch(InstantiationException ie) { instance = null; } catch(IllegalAccessException iae) { instance = null; } catch(java.lang.reflect.InvocationTargetException ite) { if (ite.getCause()!=null) { throw ite.getCause(); } else { System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); } } if (instance==null) { try { // try String[] constructor next Constructor ctor = c.getConstructor(String[].class); instance = java.applet.Applet.class.cast(ctor.newInstance(new Object[] { new String[0] })); } catch(NoSuchMethodException nsme) { instance = null; } catch(InstantiationException ie) { instance = null; } catch(IllegalAccessException iae) { instance = null; } catch(java.lang.reflect.InvocationTargetException ite) { if (ite.getCause()!=null) { throw ite.getCause(); } else { System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); return; } } } if (instance==null) { System.err.println("Error: This applet does not have a default constructor or a constructor "+ "accepting String[]."); return; } } else { try { // try String[] constructor Constructor ctor = c.getConstructor(String[].class); instance = java.applet.Applet.class.cast(ctor.newInstance(new Object[] { args })); } catch(NoSuchMethodException nsme) { instance = null; } catch(InstantiationException ie) { instance = null; } catch(IllegalAccessException iae) { instance = null; } catch(java.lang.reflect.InvocationTargetException ite) { if (ite.getCause()!=null) { throw ite.getCause(); } else { System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); return; } } if (instance==null) { System.err.println("Error: This applet does not have a constructor accepting String[]."); return; } } edu.rice.cs.plt.swing.SwingUtil.showApplet(instance, 400, 300); } } else { try { m = c.getMethod("main", java.lang.String[].class); if (!m.getReturnType().equals(void.class)) { System.err.println("Error: This class does not have a static void main method accepting String[]."); m = null; } } catch (java.lang.NoSuchMethodException e) { System.err.println("Error: This class does not have a static void main method accepting String[]."); m = null; } } if (m != null) { if (isProgram) { String[] newArgs = new String[args.length+1]; newArgs[0] = "code="+c.getName(); System.arraycopy(args, 0, newArgs, 1, args.length); args = newArgs; } try { m.setAccessible(true); m.invoke(null, new Object[] { args }); } catch(SecurityException se) { System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); } catch(IllegalAccessException iae) { System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); } catch(java.lang.reflect.InvocationTargetException ite) { if (ite.getCause()!=null) { throw ite.getCause(); } else { System.err.println("Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'."); } } } }

It's actual Java code that is statically compiled, not a string that is concatenated at runtime and that might be erroneous.

Unfortunately, we don't get the right static error messages. I can print something like "Static Error: Undefined class 'Foo'" as output, but it doesn't have the right font and doesn't invoke the right listeners, so the "Auto-Import" dialog doesn't show up, for example.

For now, I'm using the old string concatenation routine again.

[2] [3]Share [4]