import java.io.*;
import java.util.*;
import java.util.concurrent.*;

public class BlockingQueueTest
{
   public static void main(String[] args)
   {
      Scanner in = new Scanner(System.in);
      System.out.print("Podaj katalog bazowy (e.g. /usr/local/jdk5.0/src): ");
      String directory = in.nextLine();
      System.out.print("Podaj poszukiwane słowo (e.g. volatile): ");
      String keyword = in.nextLine();     

      final int FILE_QUEUE_SIZE = 10;
      final int SEARCH_THREADS = 100;
      
      BlockingQueue<File> queue = new ArrayBlockingQueue<File>(FILE_QUEUE_SIZE);

      FileEnumerationTask enumerator = new FileEnumerationTask(queue, new File(directory));
      new Thread(enumerator).start();
      for (int i = 1; i <= SEARCH_THREADS; i++)
         new Thread(new SearchTask(queue, keyword)).start();
   }
}

/**
   Wyszukuje wszystkie pliki w katalogu i podkatalogach.
*/
class FileEnumerationTask implements Runnable
{
   /**
      Konstruktor.
      @param queue kolejka blokująca, do której wstawiane sa pliki
      @param startingDirectory katalog, od którego rozpoczyna się wyszukiwanie
   */
   public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory)
   {
      this.queue = queue;
      this.startingDirectory = startingDirectory;      
   }

   public void run()
   {
      try
      {
         enumerate(startingDirectory);
         queue.put(DUMMY); 
      }
      catch (InterruptedException e) {}
   }

   /**
      Rekurencyjnie wyszukuje wszystkie pliki w katalogu i jego podkatalogach.
      @param directory katalog, od którego rozpoczyna się wyszukiwanie
   */
   public void enumerate(File directory) throws InterruptedException
   {     
      File[] files = directory.listFiles();
      for (File file : files)      {
         if (file.isDirectory()) enumerate(file);
         else queue.put(file); 
      }
   }

   public static File DUMMY = new File("");

   private BlockingQueue<File> queue;
   private File startingDirectory;
}

/**
   Wyszukuje słowo kluczowe w pliku.
*/
class SearchTask implements Runnable
{
   /**
      Konstruktor.
      @param queue kolejka, z której pobierane są pliki
      @param keyword poszukiwane słowo kluczowe
   */
   public SearchTask(BlockingQueue<File> queue, String keyword)
   {
      this.queue = queue;
      this.keyword = keyword;
   }

   public void run()
   {
      try
      {
         boolean done = false;
         while (!done)
         {
            File file = queue.take();
            if (file == FileEnumerationTask.DUMMY) { queue.put(file); done = true; }
            else search(file);
         }
      }
      catch (IOException e) { e.printStackTrace(); }
      catch (InterruptedException e) {}
   }

   /**
      Wyszukuje i wyświetla wiersze zawierające słowo kluczowe.
      @param file przeszukiwany plik
   */
   public void search(File file) throws IOException
   {     
      Scanner in = new Scanner(new FileInputStream(file));
      int lineNumber = 0;
      while (in.hasNextLine())
      {
         lineNumber++;         
         String line = in.nextLine();
         if (line.contains(keyword))
            System.out.printf("%s:%d:%s%n", file.getPath(), lineNumber, line);
      }
      in.close();
   }

   private BlockingQueue<File> queue;
   private String keyword;
}

