I had mentioned a while ago that the way we put together the commands that get interpreted when the user presses the “Run” button 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /** 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<String> 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.