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:
/** 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.