View Javadoc

1   // AppletIO.java, created Oct 5, 2004 8:40:10 PM 2004 by jwhaley
2   // Copyright (C) 2004 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package jwutil.gui;
5   
6   import java.awt.Dimension;
7   import java.awt.Insets;
8   import java.io.BufferedReader;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.io.InputStreamReader;
12  import java.io.OutputStream;
13  import java.io.PrintStream;
14  import java.io.UnsupportedEncodingException;
15  import java.io.Writer;
16  import java.lang.reflect.Field;
17  import java.lang.reflect.InvocationTargetException;
18  import java.lang.reflect.Method;
19  import javax.swing.JApplet;
20  import javax.swing.JFrame;
21  import javax.swing.JScrollPane;
22  import javax.swing.JSplitPane;
23  import javax.swing.JTextArea;
24  import javax.swing.event.DocumentEvent;
25  import javax.swing.event.DocumentListener;
26  import javax.swing.text.BadLocationException;
27  import jwutil.io.FillableReader;
28  import jwutil.io.ReaderInputStream;
29  import jwutil.reflect.Reflect;
30  
31  /***
32   * AppletIO
33   * 
34   * @author jwhaley
35   * @version $Id: AppletIO.java 2249 2005-04-29 02:32:27Z joewhaley $
36   */
37  public class AppletIO extends JApplet {
38      
39      /***
40       * Version ID for serialization.
41       */
42      private static final long serialVersionUID = 4121128139021169972L;
43      
44      public static String DEFAULT_ENCODING = "UTF-8";
45      
46      /***
47       * AppletWriter takes anything written to it and puts it in the output area.
48       * 
49       * @author jwhaley
50       * @version $Id: AppletIO.java 2249 2005-04-29 02:32:27Z joewhaley $
51       */
52      public class AppletWriter extends Writer {
53          /* (non-Javadoc)
54           * @see java.io.Writer#write(char[], int, int)
55           */
56          public void write(char[] cbuf, int off, int len) throws IOException {
57              if (len == 0) {
58                  return;
59              }
60              String str = new String(cbuf, off, len);
61              outputArea.append(str);
62              jumpToEndOfOutput();
63          }
64  
65          /* (non-Javadoc)
66           * @see java.io.Flushable#flush()
67           */
68          public void flush() throws IOException {
69              // Nothing to do.
70          }
71  
72          /* (non-Javadoc)
73           * @see java.io.Closeable#close()
74           */
75          public void close() throws IOException {
76              // Nothing to do.
77          }
78      }
79      
80      /***
81       * AppletOutputStream takes anything written to it and puts it in the output
82       * area.
83       * 
84       * TODO: The write() method doesn't handle multibyte characters correctly.
85       * If you want correct usage of multibyte characters, use AppletWriter
86       * instead.
87       * 
88       * @author jwhaley
89       * @version $Id: AppletIO.java 2249 2005-04-29 02:32:27Z joewhaley $
90       */
91      public class AppletOutputStream extends OutputStream {
92          
93          /* (non-Javadoc)
94           * @see java.io.OutputStream#write(int)
95           */
96          public void write(int b) throws IOException {
97              outputArea.append(new String(new byte[]{(byte) b}));
98              jumpToEndOfOutput();
99          }
100 
101         /* (non-Javadoc)
102          * @see java.io.OutputStream#write(byte[], int, int)
103          */
104         public void write(byte b[], int off, int len) throws IOException {
105             if (len == 0) {
106                 return;
107             }
108             String str = new String(b, off, len, DEFAULT_ENCODING);
109             outputArea.append(str);
110             jumpToEndOfOutput();
111         }
112     }
113     
114     /***
115      * Listens for newline inputs in the input area, and sends that line
116      * to inputWriter.
117      * 
118      * @author jwhaley
119      * @version $Id: AppletIO.java 2249 2005-04-29 02:32:27Z joewhaley $
120      */
121     public class TextAreaListener implements DocumentListener {
122     
123         /* (non-Javadoc)
124          * @see javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEvent)
125          */
126         public void insertUpdate(DocumentEvent e) {
127             int length = e.getLength();
128             if (length == 0) return;
129             int offset = e.getOffset();
130             try {
131                 String s = inputArea.getText(offset, length);
132                 int i;
133                 while ((i = s.indexOf('\n')) >= 0) {
134                     int lineNum = inputArea.getLineOfOffset(offset);
135                     int sOff = inputArea.getLineStartOffset(lineNum);
136                     int eOff = inputArea.getLineEndOffset(lineNum);
137                     String line = inputArea.getText(sOff, eOff - sOff);
138                     if (outputArea != null) {
139                         outputArea.append(line);
140                         jumpToEndOfOutput();
141                     }
142                     if (inputWriter != null) {
143                         inputWriter.write(line);
144                     }
145                     offset = eOff;
146                     s = s.substring(i + 1);
147                 }
148             } catch (IOException x) {
149                 x.printStackTrace();
150             } catch (BadLocationException x) {
151                 x.printStackTrace();
152             }
153         }
154 
155         /* (non-Javadoc)
156          * @see javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEvent)
157          */
158         public void removeUpdate(DocumentEvent e) {
159             // Ignore.
160         }
161 
162         /* (non-Javadoc)
163          * @see javax.swing.event.DocumentListener#changedUpdate(javax.swing.event.DocumentEvent)
164          */
165         public void changedUpdate(DocumentEvent e) {
166             // Ignore.
167         }
168     }
169  
170     /***
171      * Scroll to the end of the output area.
172      */
173     public void jumpToEndOfOutput() {
174         outputArea.setCaretPosition(outputArea.getDocument().getLength());
175     }
176     
177     JTextArea outputArea;
178     JTextArea inputArea;
179     Writer inputWriter;
180     Method method;
181     Object[] methodArgs;
182 
183     protected void loadAppletParameters() {
184         // Get the applet parameters.
185         String className = getParameter("class");
186         String methodName = getParameter("method");
187         if (methodName == null) methodName = "main";
188         method = Reflect.getDeclaredMethod(className, methodName);
189         Class[] pTypes = method.getParameterTypes();
190         methodArgs = new Object[pTypes.length];
191         Object[] args;
192         if (pTypes.length == 1 && pTypes[0] == String[].class) {
193             int nParams = 0;
194             while (getParameter("arg"+nParams) != null)
195                 ++nParams;
196             methodArgs[0] = args = new String[nParams];
197         } else {
198             args = methodArgs;
199         }
200         for (int i = 0; i < args.length; ++i) {
201             String arg = getParameter("arg"+i);
202             args[i] = arg;
203         }
204     }
205     
206     public void init() {
207         loadAppletParameters();
208         
209         //Execute a job on the event-dispatching thread:
210         //creating this applet's GUI.
211         try {
212             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
213                 public void run() {
214                     PrintStream out = createGUI();
215                     launch(out, method, methodArgs);
216                 }
217             });
218         } catch (Exception e) { 
219             System.err.println("createGUI didn't successfully complete");
220         }
221     }
222     
223     PrintStream createGUI() {
224         outputArea = new JTextArea();
225         outputArea.setMargin(new Insets(5, 5, 5, 5));
226         outputArea.setEditable(false);
227         inputArea = new JTextArea();
228         inputArea.setMargin(new Insets(5, 5, 5, 5));
229         JScrollPane top = new JScrollPane(outputArea);
230         JScrollPane bottom = new JScrollPane(inputArea,
231             JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
232             JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
233         JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, top, bottom);
234         //splitPane.setDividerLocation(350);
235         splitPane.setResizeWeight(1.0);
236         getContentPane().add(splitPane);
237         // Provide minimum sizes for the two components in the split pane.
238         Dimension minimumSize = new Dimension(400, 40);
239         top.setMinimumSize(minimumSize);
240         bottom.setMinimumSize(minimumSize);
241         bottom.setPreferredSize(minimumSize);
242         // Use this listener to listen to changes to the text area.
243         DocumentListener myListener = new TextAreaListener();
244         inputArea.getDocument().addDocumentListener(myListener);
245         // Redirect System.out/System.err to our text area.
246         try {
247             InputStream in;
248             FillableReader fin = new FillableReader();
249             inputWriter = fin.getWriter();
250             in = new ReaderInputStream(fin);
251 
252             // Support for JDK1.3, which doesn't allow encoding on PrintStream
253             PrintStream out, err;
254             try {
255                 err = out = new PrintStream(new AppletOutputStream(), true, DEFAULT_ENCODING);
256             } catch (NoSuchMethodError _) {
257                 err = out = new PrintStream(new AppletOutputStream(), true);
258             }
259             try {
260                 System.setOut(out);
261                 System.setErr(err);
262                 // Redirect System.in from our input area.
263                 System.setIn(in);
264             } catch (SecurityException x) {
265                 //outputArea.append("Note: Cannot reset stdio: " + x.toString());
266                 x.printStackTrace();
267                 initStreams(method.getDeclaringClass(), in, out, err);
268             }
269             return out;
270         } catch (UnsupportedEncodingException x) {
271             outputArea.append(x.toString());
272             x.printStackTrace();
273             return null;
274         }
275     }
276     
277     public static void main(String[] s) throws SecurityException,
278         NoSuchMethodException, ClassNotFoundException {
279         AppletIO applet = new AppletIO();
280         
281         if (s.length < 1) {
282             applet.method = AppletIO.class.getDeclaredMethod("example", new Class[0]);
283             applet.methodArgs = new Object[0];
284         } else {
285             applet.method = Class.forName(s[0]).getDeclaredMethod("main",
286                 new Class[]{String[].class});
287             String[] s2 = new String[s.length - 1];
288             System.arraycopy(s, 1, s2, 0, s2.length);
289             applet.methodArgs = new Object[]{s2};
290         }
291         
292         JFrame f = new JFrame();
293         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
294         f.getContentPane().add(applet);
295         f.setSize(500, 400);
296         f.setLocation(200, 200);
297         applet.createGUI();
298         f.setVisible(true);
299         
300         System.out.println("Starting " + applet.method.getDeclaringClass().getName()+
301             "."+applet.method.getName()+"()");
302         launch(System.out, applet.method, applet.methodArgs);
303     }
304     
305     public static void initStreams(Class c, InputStream in, PrintStream out, PrintStream err) {
306         Field f;
307         try {
308             f = c.getDeclaredField("in");
309             f.set(null, in);
310         }
311         catch (NoSuchFieldException _) {}
312         catch (IllegalAccessException _) {}
313         try {
314             f = c.getDeclaredField("out");
315             f.set(null, out);
316         }
317         catch (NoSuchFieldException _) {}
318         catch (IllegalAccessException _) {}
319         try {
320             f = c.getDeclaredField("err");
321             f.set(null, err);
322         }
323         catch (NoSuchFieldException _) {}
324         catch (IllegalAccessException _) {}
325     }
326     
327     public static void launch(final PrintStream out, final Method m, final Object[] args) {
328         new Thread() {
329             public void run() {
330                 try {
331                     m.invoke(null, args);
332                 } catch (InvocationTargetException e) {
333                     e.getTargetException().printStackTrace();
334                 } catch (IllegalArgumentException e) {
335                     e.printStackTrace();
336                 } catch (IllegalAccessException e) {
337                     e.printStackTrace();
338                 } finally {
339                     if (out != null) out.println("Terminated.");
340                 }
341             }
342         }.start();
343     }
344     
345     // For an example: A method that just consumes System.in.
346     public static void example() {
347         try {
348             BufferedReader in = new BufferedReader(new InputStreamReader(
349                 System.in, DEFAULT_ENCODING));
350             for (;;) {
351                 String s = in.readLine();
352                 System.out.println("IN: " + s);
353             }
354         } catch (IOException x) {
355             x.printStackTrace();
356         }
357     }
358 }