Jump to content

Grinding Java - Searching the WWW in Java:grinding1.java

From EDM2
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.lang.*;
import java.util.Vector;

/**
 * The WWWUtility class is the main part of the WWWUtility.
 * its responsibility is the UI and the execution of the
 * first instance of the SearchEngine class.
 * Normally I would hold the UI as a separate class to keep it
 * more abstracted from the program and thus more generic but
 * for simplicity I kept it here.
**/
public class WWWUtility extends Frame implements
         URLLocatedListener , // this interface contains a method that
                              // will be fired if the search engine locates
                              // a URL fitting the criteria.
         WindowListener , ActionListener
{
   public static void main(String argv[])
   {
      new WWWUtility();
   }

   /**
    * The constructor calls the superclass with the string for the windows
    * title.
   **/
   public WWWUtility()
   {
      super("WWW utility");
      loadUserInterface();
   }

   /**
    * This method is called by the user through the UI, and it creates
    * the first instance of the SearchEngine class.
    * it accepts the following parameters:
    * url - the url to search. You can use a url to refer to a file on your HD too.
    * stringToSearch - the search string. You could improve here by accepting a search
    * structure which will support advanced boolean searches. It should be very
    * simple to implement.
    * numberOfServersToBranchTo - if this equals 0 then every link that is not in this
    * domain will be ignored. If this number is above 0 then every link that is not
    * on this server will be branched to with  numberOfServersToBranchTo - 1 as a
    * branching factor. This feature gives us a feel of distance.
    * The concept of distance can be expanded to include length of URL string or
    * number of pages from the source page rather than servers.
   **/
   public void beginSearch(URL url,String stringToSearch,int numberOfServersToBranchTo)
   {
      progressIndicator = new ProgressIndicator(this); // create a progress
          // indicator for the advance of the search. (ProgressIndicator.java)

      progressIndicator.addActionListener(this); // Listen to the progress
               // indicators actions. If the user presses the stop
               // search button an event will be fired.

      searchEngine = new SearchEngine(progressIndicator, url,
                              numberOfServersToBranchTo, stringToSearch);
               // create an instance of the search engine.

      searchEngine.addFoundListener(this); // Each search engine will send
               // an event to this class if it finds something.

      Thread t = new Thread(searchEngine); // put the search engine on a
                                           // new thread.
      t.start(); // launch the thread.
   }

   /**
    * the method defined in URLLocatedListener. This method gets called when a
    * url is found or with a null parameter when it is time to end the search.
   **/
   synchronized public void searchStringFoundInURL(URL url)
   {
      if(url != null)
         outputView.add(url.toString());
      else
         progressIndicator.dispose();
   }

   /**
    * Invoked when actions occur.
    * This method is a part of the ActionListener interface.
   **/
   public void actionPerformed(ActionEvent e)
   {
      // listening to menu commands.
      if((e.getSource() == searchMenu) &&
         (e.getActionCommand().equals("Begin search")))
         new SearchDialog(this);

      if((e.getSource() == fileMenu) &&
         (e.getActionCommand().equals("Exit")))
         System.exit(0);

      if(e.getSource() == progressIndicator)
      {
         searchEngine.stopSearching();
      }
   }

   /**
    * Invoked when a window has been opened.
    * This method was created by the WindowListener interface.
   **/
   public void windowOpened(WindowEvent e)
   {
   }

   /**
    * Invoked when a window is in the process of being closed.
    * The close operation can be overridden at this point.
    * This method was created by the WindowListener interface.
   **/
   public void windowClosing(WindowEvent e)
   {
      System.exit(0);
   }

   /**
    * Invoked when a window has been closed.
    * This method was created by the WindowListener interface.
   **/
   public void windowClosed(WindowEvent e)
   {
   }

   /**
    * Invoked when a window is iconified.
    * This method was created by the WindowListener interface.
   **/
   public void windowIconified(WindowEvent e)
   {
   }

   /**
    * Invoked when a window is de-iconified.
    * This method was created by the WindowListener interface.
   **/
   public void windowDeiconified(WindowEvent e)
   {
   }

   /**
    * Invoked when a window is activated.
    * This method was created by the WindowListener interface.
   **/
   public void windowActivated(WindowEvent e)
   {
   }

   /**
    * Invoked when a window is de-activated.
    * This method was created by the WindowListener interface.
   **/
   public void windowDeactivated(WindowEvent e)
   {
   }

   /**
    * this method sets up the pulldown menu and the list of the URLs that were
found.
    * In the AWT one of the most important concepts is the avoidance of using exact
    * sizes to define the size of a menu, that is why I added a line at the bottom of the
    * window. That line would cause the window to be big enough to contain a line of this
    * size without me specifying the actual size.
    * This is a slight hack, I could have gotten the size of the font and resized the window
    * according to that.
   **/
   private void loadUserInterface()
   {
      addWindowListener(this); // so I will know when the windows close
                               // button is pressed.

      setLayout(new BorderLayout());

      setMenuBar(menuBar); // attach the menu bar.

      menuBar.add(fileMenu);
      fileMenu.add("Exit");
      menuBar.add(searchMenu);
      searchMenu.add("Begin search");
      searchMenu.add("Stop search"); // not implemented...

      searchMenu.addActionListener(this); // so we can tell when a menu
                                          // option is selected.
      helpMenu.addActionListener(this);
      fileMenu.addActionListener(this);

      add("Center",outputView);
      add("South",statusLine);
      pack();
      show();
   }

   public static Runtime runtimeInformation = Runtime.getRuntime(); // the
                          // runtime class is a very important part of the VM.
                          // Checkout the notes I made of it below.

   private Label statusLine = new Label ("Mini HTML search engine, written by Shai Almog 5/97");
   private ProgressIndicator progressIndicator; // a dialog for the search's progress.
   private SearchEngine searchEngine; // the Search engine in this class.
   private List outputView = new List(20,false); // the results of the search appear here.

   private Menu fileMenu = new Menu("File"); // the menu items in this program.
   private Menu searchMenu = new Menu("Search");
   private MenuBar menuBar = new MenuBar();
}

/**
 * this internal class implements a dialog for selecting the search options.
 * this way I avoided concentrating to much logic into one black box (the
 * WWWUtility class).
**/
class SearchDialog extends Dialog implements ActionListener
{
   SearchDialog(WWWUtility owner)
   {
      super(owner,"Search dialog");
      this.owner = owner;
      loadUserInterface();
   }

   /**
    * Invoked when actions occur.
    * This method is a part of the ActionListener interface.
   **/
   public void actionPerformed(ActionEvent e)
   {
      if(e.getSource() == beginSearch)
      {
         // if the begin search button was pressed then check the validity of the
         // data in the fields and run the begin search method in the WWWUtility.
         dispose();
         Integer serverHops;
         try
         {
            // convert the server hops to an integer.
            serverHops = Integer.decode(numberOfServersToBranchTo.getText());
            owner.beginSearch(new URL(URLToSearch.getText()),
               searchString.getText(),
               serverHops.intValue());
         }
         catch (MalformedURLException err)
         {
            System.out.println("Error in the URL syntax: " + err.getMessage());
         }
         catch(NumberFormatException err)
         {
            System.out.println("Error in reading the number of the maximum server hops.");
         }
      }
      if(e.getSource() == cancelSearch)
      {
         // if cancel was pressed then get rid of this fialog.
         dispose();
      }
      if(e.getSource() == browseFiles)
      {
         // if browse was pressed then load a file dialog and run set the
         // results to the search url field.
         FileDialog fileNameToSearch =
               new FileDialog(owner,"Select file to search",FileDialog.LOAD);
         fileNameToSearch.show();
         URLToSearch.setText("file://"+fileNameToSearch.getDirectory() +
                     fileNameToSearch.getFile());
      }
   }

   /**
    * this method sets up the UI for the dialog.
    * notice the creation of panels to group the objects.
    * BTW The dialog still looks ugly because I have no design taste whatsoever
   **/
   private void loadUserInterface()
   {
      setLayout(new GridLayout(4,1)); // the grid layout tries to put the
                            // objects on a grid similar to a spreadsheet.

      beginSearch.addActionListener(this); // listen to when one of the buttons gets pressed.
      cancelSearch.addActionListener(this);
      browseFiles.addActionListener(this);

      // set the three entry fields and their labels.
      // each entry field is grouped with its label in one panel so they
      // will not get away from one another.
      Panel p = new Panel();
      p.setLayout(new BorderLayout());
      add(p);
      p.add("West",new Label("URL:"));
      p.add("East",URLToSearch);

      p = new Panel();
      p.setLayout(new BorderLayout());
      add(p);
      p.add("West",new Label("Search string:"));
      p.add("East",searchString);

      p = new Panel();
      p.setLayout(new BorderLayout());
      add(p);
      p.add("West",new Label("Maximum server hops:"));
      p.add("East",numberOfServersToBranchTo);

      // put the buttons on the bottom of the dialog one after the other.
      p = new Panel();
      p.setLayout(new FlowLayout());
      p.add(cancelSearch);
      p.add(browseFiles);
      p.add(beginSearch);
      add(p);

      pack();
      show();
   }

   private WWWUtility owner; // pointer to the WWWUtility both as a
                             // frame owning this and so we can call
                             // the search method.

   // The entries and buttons to map the search.
   private TextField numberOfServersToBranchTo = new TextField("0",40);
   private TextField searchString = new TextField("",40);
   private TextField URLToSearch = new TextField("http://www.edm2.com/",40);
   private Button beginSearch = new Button("Begin search");
   private Button browseFiles = new Button("Browse");
   private Button cancelSearch = new Button("Cancel");
}