import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import javax.swing.*;

/**
   Program demonstrujący działanie wątku roboczego
   wykonującego czasochłonne zadanie.
*/
public class SwingWorkerTest
{
   public static void main(String[] args) throws Exception
   {
      JFrame frame = new SwingWorkerFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }
}

/**
   Ramka z obszarem tekstowym służącym prezentacji zawartości
   ładowanego pliku, z menu posiadającym operacje Open i Cancel
   oraz paskiem statusu pokazującym informację o postępie procesu
   ładowania.
*/
class SwingWorkerFrame extends JFrame
{
   public SwingWorkerFrame()
   {
      chooser = new JFileChooser();
      chooser.setCurrentDirectory(new File("."));

      textArea = new JTextArea();
      add(new JScrollPane(textArea));
      setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

      statusLine = new JLabel();
      add(statusLine, BorderLayout.SOUTH);

      JMenuBar menuBar = new JMenuBar();
      setJMenuBar(menuBar);

      JMenu menu = new JMenu("File");
      menuBar.add(menu);

      openItem = new JMenuItem("Open");
      menu.add(openItem);
      openItem.addActionListener(new 
         ActionListener()
         {
            public void actionPerformed(ActionEvent event)
            {
               // okno dialogowe wyboru pliku
               int result = chooser.showOpenDialog(null);

               // plik wybrany?
               if (result == JFileChooser.APPROVE_OPTION)
               {
                  readFile(chooser.getSelectedFile());
               }
            }
         });

      cancelItem = new JMenuItem("Cancel");
      menu.add(cancelItem);
      cancelItem.setEnabled(false);
      cancelItem.addActionListener(new 
         ActionListener()
         {
            public void actionPerformed(ActionEvent event)
            {
               if (workerThread != null) workerThread.interrupt();
            }
         });
   } 

   /**
      Wczytuje asynchronicznie zawartość pliku 
      aktualizując interfejs użytkownika w trakcie tego procesu.
      @param file wczytywany plik
   */
   public void readFile(final File file)
   {
      Runnable task = new
         SwingWorkerTask()
         {
            public void init()
            {
               lineNumber = 0;
               openItem.setEnabled(false);                              
               cancelItem.setEnabled(true);
            }
            
            public void update()
            {
               statusLine.setText("" + lineNumber);
            }
            
            public void finish()
            {
               workerThread = null;
               openItem.setEnabled(true);
               cancelItem.setEnabled(false);     
               statusLine.setText("Done");
            }
            
            public void work()
            {
               try
               {
                  Scanner in = new Scanner(new FileInputStream(file));
                  textArea.setText("");
                  while (!Thread.currentThread().isInterrupted() && in.hasNextLine()) 
                  { 
                     lineNumber++;
                     line = in.nextLine();  
                     textArea.append(line);
                     textArea.append("\n");
                     doUpdate();
                  }
               }
               catch (IOException e)
               {
                  JOptionPane.showMessageDialog(null, "" + e);
               }
            }
            
            private String line;
            private int lineNumber;
         };

      workerThread = new Thread(task);
      workerThread.start();
   }
         
   private JFileChooser chooser;
   private JTextArea textArea;
   private JLabel statusLine;
   private JMenuItem openItem;
   private JMenuItem cancelItem;
   private Thread workerThread;

   public static final int DEFAULT_WIDTH = 450;
   public static final int DEFAULT_HEIGHT = 350;  
}

/**
   Utwórz klasę pochodną poniższej klasy abstrakcyjnej
   dla reprezentacji zadania, które aktualizuje interfejs użytkownika.
*/
abstract class SwingWorkerTask implements Runnable
{
   /**
      Kod zadania umieść wewnątrz tej metody. Pamiętaj, aby wywoływać doUpdate(), a nie update(), 
      aby aktualizować informację o postępie zadania.
   */
   public abstract void work() throws InterruptedException;

   /**
      Zastąp tę metodę implementacją operacji 
      aktualizujących interfejs użytkownika 
      przed rozpoczęciem zadania.
   */
   public void init() {}
   /**
      Zastąp tę metodę implementacją operacji 
      aktualizujących interfejs użytkownika 
      po wykonaniu każdej partii zadania.
   */
   public void update() {}
   /**
      Zastąp tę metodę implementacją operacji 
      aktualizujących interfejs użytkownika 
      po wykonaniu zadania.
   */
   public void finish() {}

   private void doInit()
   {
      EventQueue.invokeLater(new
         Runnable()
         {
            public void run() { init(); }
         });    
   }

   /**
      Wywołuj tę metodę z wnętrza work() 
      aby zaktualizować interfejs użytkownika
      po wykonaniu każdej partii zadania.
   */
   protected final void doUpdate()
   {
      if (done) return;
      EventQueue.invokeLater(new
         Runnable()
         {
            public void run() { update(); }
         });      
   }

   private void doFinish()
   {
      EventQueue.invokeLater(new
         Runnable()
         {
            public void run() { finish(); }
         });
   }

   public final void run()
   {      
      doInit();
      try
      {
         done = false;
         work();
      }
      catch (InterruptedException ex)
      {
      }
      finally
      {
         done = true;
         doFinish();
      }
   }

   private boolean done;
}

