Tips&TricksFacciamo il punto della situazione: abbiamo già parlato della redirezione dell’output su file in Java nell’articoletto “Redirigere lo standard output“. Come abbiamo potuto vedere, sono sufficienti pochissime righe di codice per ottenere la redirezione.

Ma questo potrebbe non bastare: se stiamo sviluppando un’applicazione che fa uso di interfacce grafiche e vogliamo riutilizzare delle vecchie classi da noi scritte (o scritte da altri), la redirezione su file potrebbe non essere sufficiente. Se vogliamo, ad esempio, che tutto l’output generato da quelle classi venga visualizzato in un componente grafico, la redirezione su file non ci aiuta.

Ecco, quindi, che un po’ di tempo fa mi ingegnai per trovare una soluzione a questo problema.

Fu allora che progettai una classe che mi permettesse di redirigere tutto l’output all’interno di una JTextArea, senza rinunciare alla comodità della redirezione dell’output.

Il codice della classe è il seguente:

import java.io.*;
import javax.swing.JTextArea;
public class AreaOutputStream extends OutputStream {
  private JTextArea jta;
  private boolean attivo;
 
 
public AreaOutputStream(JTextArea jta) {
    this.jta = jta;
    attivo = true;
  }
 
public void close() throws IOException {
    attivo = false;
  }
 
public void flush() {}
 
public void write(byte[] b) throws IOException {
    write(b, 0, b.length);
  }
  
public void write(byte[] b, int off, int len)
                           throws IOException {
    if ( attivo ) {
      String s = "";
      for(int i=0; i<len; i++) s += (char) b[i+off];
      if (jta != null) {
        jta.append(s);
      } else {
        throw new IOException(
          "No JTextArea specified for output"
        );
      }
    } 
  }
 
public void write(int b) throws IOException {
    if ( attivo ) {
      if (jta != null) {
        jta.append("" + ((char) b));
      } else {
        throw new IOException(
          "No JTextArea specified for output"
        );
      }
    }
  }
}

E qui sotto vediamo un semplice esempio di utilizzo. Costruiamo una finestra con una JTextArea, quindi richiamiamo un’ipotetica classe Elaborazione che dovrebbe utilizzare System.out o System.err per i messaggi in output:

public class Finestra extends JFrame {
   private JTextArea jta;
   private AreaOutputStream out;
  
public Finestra() {
      getContentPane().setLayout(
         new BorderLayout();
      );
     
jta = new JTextArea();
      out = new AreaOutputStream( jta );
     
System.setOut( out );
      System.setErr( out );
     
getContentPane().add(jta, BorderLayout.CENTER);
     
setTitle("Esempio");
      setSize(800, 600);
      setDefaultCloseOperation( EXIT_ON_CLOSE );
      setVisible( true );
      Elaborazione e = new Elaborazione();
   }
  
public static void main(String [] args) {
      Finestra f = new Finestra();
   }
}

Come possiamo vedere, non ci dobbiamo nemmeno preoccupare di gestire eventuali eccezioni in fase di costruzione della classe AreaOutputStream: essa, infatti, non può generare eccezioni nel costruttore, che effettua solamente un’assegnazione.

La classe estende OutputStream quindi, a tutti gli effetti, essa è un OutputStream, che può essere utilizzato in qualunque contesto si richieda questo tipo di oggetti. Essendo OutputStream una classe astratta è necessario implementarne tutti i metodi astratti, compreso flush(). Nel nostro caso, non utilizzando alcun buffer (non ce n’è bisogno, tutti i dati vengono inviati alla JTextArea immediatamente) è sufficiente implementarlo vuoto. Per altre eventuali delucidazioni, mi potete tranquillamente lasciare un commento.

In questo modo possiamo tranquillamente veder apparire nella nostra JTextArea tutto l’output generato dalla classe Elabora durante la sua esecuzione. Come sempre non mi rimane che augurarvi una Buona Programmazione.

Tags: