Grinding Java - Searching the WWW in Java:grinding1.java
Appearance
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"); }