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
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.