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

Share

About Mathias

Software development engineer. Principal developer of DrJava. Recent Ph.D. graduate from the Department of Computer Science at Rice University.
This entry was posted in DrJava. Bookmark the permalink.

Leave a Reply