Most people use a command-line terminal window when they run a Java application. If the application uses stdout and stderr for log messages, one can see these messages on the terminal.
For a Java/Swing application, wouldn't it be nice to render those print streams in a Swing window? This is easier than expected.
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 | import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import javax.swing.JTextArea; /** * Redirect System.out and System.err to some UI window. */ public final class Log { private static class RedirectingOutputStream extends ByteArrayOutputStream { private final JTextArea textArea; RedirectingOutputStream(JTextArea textArea) { this.textArea = textArea; } @Override public void flush() throws IOException { final String line = toString(); textArea.append(line); reset(); // else toString() would give all output again next time } } public static void redirectOut(JTextArea outputArea) { final ByteArrayOutputStream outStream = new RedirectingOutputStream(outputArea); System.setOut(new PrintStream(outStream, true)); // true: autoFlush on newlines } public static void redirectErr(JTextArea errorArea) { final ByteArrayOutputStream errStream = new RedirectingOutputStream(errorArea); System.setErr(new PrintStream(errStream, true)); // true: autoFlush on newlines } private Log() {} // do not instantiate } |
Static implementations are not beautiful, but sometimes you can't get over it.
Because Java's System.out and System.err are static,
the Log redirection methods above are static too.
Because there are utility methods System.setOut() and System.setErr()
we can redirect these streams to wherever we want.
The RedirectingOutputStream starting on line 11 does all the work.
It extends ByteArrayOutputStream and overrides just the flush() method
to send everything to the obtained textarea on line 22.
This only works when the wrapping PrintStream was set to auto-flush,
which means it calls flush() on every newline it detects.
You can see the auto-flush setting on line 29 and 33 ("true") where
the ByteArrayOutputStream gets packed into a PrintStream.
Here is a test-main to try this out:
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 | public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JButton stdoutButton = new JButton("Write to System.out"); stdoutButton.addActionListener(event -> System.out.println("Hello System.out at "+new Date())); JButton stderrButton = new JButton("Write to System.err"); stderrButton.addActionListener(event -> System.err.println("Hello System.err at "+new Date())); JToolBar toolbar = new JToolBar(); toolbar.add(stdoutButton); toolbar.add(stderrButton); JTextArea textArea = new JTextArea(); textArea.setLineWrap(true); textArea.setWrapStyleWord(true); Log.redirectOut(textArea); Log.redirectErr(textArea); JPanel panel = new JPanel(new BorderLayout()); panel.add(new JScrollPane(textArea), BorderLayout.CENTER); panel.add(toolbar, BorderLayout.NORTH); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(panel); frame.setSize(new Dimension(400, 200)); frame.setLocationRelativeTo(null); frame.setVisible(true); }); } |
You see the redirection happening on line 17 and 18. Here is a screenshot of the test-application:
Click the buttons on top to generate outputs.
Swing was the first platform-independent object-oriented user-interface system that provided all kinds of components like tables and trees. Meanwhile it is a frozen project, but still contained in the Java runtime environment and thus available everywhere without complicated dependencies. Swing couldn't make it to small devices, but did JavaFX?


Keine Kommentare:
Kommentar veröffentlichen