home *** CD-ROM | disk | FTP | other *** search
/ com!online 2002 May / comcd0502.iso / homepage / javaspecial / 03_01 / sitesearcher / AdvSiteSearcher / AdvSiteSearcher.java < prev    next >
Encoding:
Java Source  |  2000-08-18  |  20.1 KB  |  515 lines

  1. //AdvSiteSearcher c1999 The Gilbert Post by David Faden
  2. //The applet and code are distributed as linkware...
  3. //If you use this applet or a variant on its code,
  4. //include a link to The Gilbert Post, 
  5. //http://www.geocities.com/Athens/Parthenon/1911
  6. //The Gilbert Post and David Faden take no responsibility
  7. //for anything bad that happens as a result of using this applet
  8. //or a derivative based on its code. USE AT YOUR OWN RISK. (big letters)
  9. //Please send reports of problems to gilbertnews@hotmail.com, anyway, though.
  10.  
  11. //I have not commented this source especially well (for one thing, it will not play 
  12. //well with JavaDoc so go ahead and send your questions about the source to me...
  13. //I may know the answer.
  14. //
  15. //It should be evident that I'm not a professional programmer...any professionals out there,
  16. //I'd like to hear your advise (as long as it's polite). Thank you. --David
  17.  
  18. import java.awt.*;
  19. import java.applet.*;
  20. import java.io.*;
  21. import java.net.*;
  22. import java.util.*;
  23. //These classes are designed to work with Netscape 3.x+ and so use JDK 1.0
  24.  
  25. // 5/31/1999 fixed a bug, discovered by Dave Langers, in showPage(URL url) that 
  26. // caused the page to be loaded in both
  27. // the target and top (default) windows
  28. // Also, fixed a bug that might cause the progress bar to show 100% (after an errored 
  29. // SearchThread called foundNoMatch(int i)) while files were still being searched.
  30. //
  31. // 6/2/1999 fixed a very stupid bug on my part, also discovered by Dave Langers,
  32. // that made it impossible for queries
  33. // containing uppercase characters to ever be found -- the scanned lines were
  34. // converted to lowercase while the keywords/phrases were not
  35. //
  36. // 6/4/1999 renamed to AdvSiteSearcher to differentiate from older SiteSearcher
  37. // Plan to release one more version of SiteSearcher using SearchSieves
  38. //
  39. // 6/9/1999 made many major revisions: renamed SearchThread to DocSearcher because
  40. // it is no longer a subclass of Thread (it instead implements Runnable), polished
  41. // the use of SearchSieves, began tentative support for caching, added the ability
  42. // to demand exact matches, and to ignore text in between lesser than and greater than
  43. // signs (probably HTML)
  44. //
  45. // 7/27/1999 acting on user requests, updated parameters to add control over the display text...
  46. // mostly useful to non-English speakers who want to "localize" the applet...
  47. // There are a few other barriers to full internationalization: the date
  48. // format used in the search results should be changeable, and the incoming byte stream
  49. // should be converted according to the text encoding into a char stream. There is also a
  50. // problem, discovered by Guy Capra, with ALL java text components in Netscape and MSIE's VMs
  51. // on Macintoshes with AZERTY keyboards: a key press on the AZERTY keyboard seems to be being
  52. // incorrectly interpreted as a press on the corresponding QWERTY key. I have not been able
  53. // to figure out a solution...for now, you can find my present failed efforts at
  54. // http://www.geocities.com/gilbertnews/linkware/qwerty_azerty/.
  55. // Also, wrote out full name of List (java.awt.List) to hopefully make this compile with JDK1.2.
  56. // 
  57. // 11/25/1999 squashed a bug (in the run method) which was messing up the "dirs" parameter...
  58. // I'm not sure how this one slipped through. Corrected the spelling of "exclude" in the default
  59. // text of the applet. Kudos to Matthew Hutton for noticing this.
  60. //
  61. // 4/12/2000 fixed a "Y2K bug reported by several alert users...  I am not sure what 
  62. // I was thinking when I wrote the portion of code calling Date.getYear()... Perhaps that it
  63. // returns the decade? Anyway, in reality, getYear() returns the number of years
  64. // since 1900. Files with modification dates beyond 1999 were listed with dates greater than
  65. // 99 (100 for 2000).
  66. // Note: the whole Date class is deprecated in JDK 1.1
  67. // The code actually changed is found in DocSearcher.java.
  68. // 
  69. // 4/12/2000 added code that causes the DocSearcher's runner Thread to wait
  70. // when it is not "doing anything." This should be more efficient than in the
  71. // previous incarnation, where runner would sleep, then periodically wake up to 
  72. // see if there was anything to search.
  73.  
  74. public class AdvSiteSearcher extends Applet implements Runnable {
  75.  DocSearcher[] workers;
  76.  URL[] urls;
  77.  String[] pageinfo;//name, size, last modified
  78.  int[] matches;
  79.  int nummatches=0;
  80.  int numreported=0; 
  81.  int numWorkers=0;
  82.  java.awt.List list;//=new java.awt.List(5,false);
  83.  Button search,stop;
  84.  Checkbox HTMLbox,Exactbox;
  85.  TextField searchbox;
  86.  ProgressBar progressbar;
  87.  String target;
  88.  URL docbase;
  89.  boolean displayMessage;
  90.  String message;//Message to be displayed in applet
  91.  //to let the user know what's happening before the GUI is finished being set up
  92.  static final String searchTokenSeparators="\"&|+ \t\r\n,*?";
  93.  URL thegilbertpost=null;
  94.  String dirs=null;//dirs is one-time-use variable used to pass
  95.  //parameter info to AdvSiteSearcher's run to parse out links from dir listings.
  96.  Color bordercolor=Color.darkGray;
  97.  Insets insets=new Insets(5,5,5,5);
  98.  
  99.  public void init() {
  100.    //first initialize the variables
  101.    target=getParameter("target");
  102.    docbase=getDocumentBase();
  103.    try {
  104.      thegilbertpost=new URL("http://www.geocities.com/Athens/Parthenon/1911/");
  105.    }
  106.    catch(MalformedURLException eww) {/*should never occur*/}
  107.    if((dirs=getParameter("dirs"))!=null) {
  108.      Thread tempthread=new Thread(this);
  109.      tempthread.start();
  110.    }
  111.    else finishInit();
  112.  }
  113.  
  114.  //Finish initializing the applet after AdvSiteSearcher's run has possibly been run
  115.  //to collect links from directory listings...
  116.  private void finishInit() {
  117.    int cachesize=2000;
  118.    String cacheparam=getParameter("cachesize");
  119.    if (cacheparam!=null) {
  120.      try {
  121.        cachesize=Integer.parseInt(cacheparam);
  122.      }
  123.      catch(NumberFormatException e) {cachesize=2000;}
  124.    }
  125.    String files=getParameter("files");
  126.    if(dirs!=null) {
  127.      if(files==null) files=dirs;
  128.      else files=dirs+files;
  129.    }
  130.    if(files!=null) {
  131.      StringTokenizer st=new StringTokenizer(files,"\n\r \t,",false);
  132.      int num=st.countTokens();
  133.      urls=new URL[num];
  134.      workers=new DocSearcher[num];
  135.      pageinfo=new String[num];
  136.      matches=new int[num];
  137.      numWorkers=num;
  138.      String currToken;
  139.      for(int i=0;i<num;i++) {
  140.         currToken=st.nextToken();
  141.         pageinfo[i]=currToken;
  142.         matches[i]=-1;
  143.         try {
  144.           urls[i]=new URL(docbase,currToken);
  145.           workers[i]=new DocSearcher(this,urls[i],i,cachesize);
  146.         }
  147.         catch(MalformedURLException mued) {
  148.           urls[i]=null;
  149.           //waste an Object
  150.           workers[i]=new DocSearcher(this,urls[i],i,cachesize);
  151.           workers[i].setErrored();
  152.           System.out.println(mued);
  153.         }
  154.      }
  155.    }
  156.    else { 
  157.      displayMessage=true; 
  158.      System.out.println("AdvSiteSearcher Applet can't start");
  159.      System.out.println("Missing required parameter(s): files/dirs"); 
  160.      message="Can't continue: missing both \"files\" & \"dirs\" parameters.";
  161.      repaint();
  162.      return;
  163.    }
  164.      
  165.    //Set up GUI
  166.    resize(350,300);
  167.    setLayout(new BorderLayout());
  168.    //get color parameters
  169.    Color color=null;
  170.    if((color=getColor(getParameter("bgcolor")))!=null) setBackground(color);
  171.    else setBackground(Color.gray);
  172.    if((bordercolor=getColor(getParameter("bordercolor")))==null) bordercolor=Color.darkGray;
  173.    if((color=getColor(getParameter("fgcolor")))!=null) setForeground(color);
  174.    else setForeground(Color.black);
  175.    //Lots of Panels
  176.    Panel ptotal=new Panel();
  177.    ptotal.setLayout(new BorderLayout());
  178.    Panel pcontrols=new Panel();
  179.    pcontrols.setLayout(new GridLayout(3,1));//searchbox,checkboxes,progressbar
  180.    //Parameters to allow control over the text in the applet
  181.    //  Though this needed ability is very easy to implement, I'm still faced with
  182.    //  the dilemma of what to name the parameters. Perhaps this is a sign of my insanity,
  183.    //  but I worry about whether to name them for their functionality (like "search_btn_txt") 
  184.    //  or their English versions (like "Search_en")...for now, functionality:
  185.    //  search_btn_txt
  186.    //  stop_btn_txt
  187.    //  xhtml_chkbx_txt
  188.    //  exact_chkbx_txt
  189.    //  pbar_msg_txt , text the progressbar displays as it progresses
  190.    //  searchbox_label_txt
  191.    String search_btn_txt=getParameter("search_btn_txt","Search");
  192.    String stop_btn_txt=getParameter("stop_btn_txt","Stop");
  193.    String xhtml_chkbx_txt=getParameter("xhtml_chkbx_txt","Exclude HTML");
  194.    String exact_chkbx_txt=getParameter("exact_chkbx_txt","Exact matches only");
  195.    String pbar_msg_txt=getParameter("pbar_msg_txt","Searching...");
  196.    String searchbox_label_txt=getParameter("searchbox_label_txt","Search for:");
  197.    //
  198.    Panel ptop=new Panel();
  199.    ptop.setLayout(new BorderLayout());
  200.    Panel p=new Panel();
  201.    p.add(new Label(searchbox_label_txt,Label.RIGHT));
  202.    p.add(searchbox=new TextField(20));
  203.    if((color=getColor(getParameter("searchboxbgcolor")))!=null) {
  204.      searchbox.setBackground(color);
  205.    }
  206.    else searchbox.setBackground(Color.white);
  207.    if((color=getColor(getParameter("searchboxfgcolor")))!=null) {
  208.      searchbox.setForeground(color);
  209.    }
  210.    else searchbox.setForeground(Color.black);
  211.    String initsearchwrds=getParameter("startwords");
  212.    if(initsearchwrds!=null) searchbox.setText(initsearchwrds);
  213.    //Color buttonbgcolor,buttonfgcolor;
  214.    p.add(search=new Button(search_btn_txt));//search button
  215.    p.add(stop=new Button(stop_btn_txt));//stop button
  216.    if((color=getColor(getParameter("buttonbgcolor")))!=null) {
  217.      search.setBackground(color);
  218.      stop.setBackground(color);
  219.    }
  220.    if((color=getColor(getParameter("buttonfgcolor")))!=null) {
  221.      search.setForeground(color);
  222.      stop.setForeground(color);
  223.    }
  224.    ptop.add("Center",p);
  225.    pcontrols.add(ptop);
  226.    Panel p2=new Panel();
  227.    p2.setLayout(new FlowLayout(FlowLayout.CENTER));
  228.    p2.add(HTMLbox=new Checkbox(xhtml_chkbx_txt));//Exclude HTML checkbox
  229.    p2.add(Exactbox=new Checkbox(exact_chkbx_txt));//Exact matches only checkbox
  230.    if((color=getColor(getParameter("checkboxbgcolor")))!=null) {
  231.      HTMLbox.setBackground(color);
  232.      Exactbox.setBackground(color);
  233.    }
  234.    //else let default thing happen
  235.    if((color=getColor(getParameter("checkboxfgcolor")))!=null) {
  236.      HTMLbox.setForeground(color);
  237.      Exactbox.setForeground(color);
  238.    }
  239.    //else let default happen
  240.    pcontrols.add(p2);
  241.    Panel p3=new Panel();
  242.    Color pbaroncolor,pbaroffcolor;
  243.    if((pbaroncolor=getColor(getParameter("pbaroncolor")))==null) pbaroncolor=Color.cyan;
  244.    if((pbaroffcolor=getColor(getParameter("pbaroffcolor")))==null) pbaroffcolor=Color.blue.brighter().brighter().brighter();
  245.    p3.add(progressbar=new ProgressBar(pbaroncolor,pbaroffcolor,300,20,numWorkers,pbar_msg_txt));
  246.    pcontrols.add(p3);
  247.    ptotal.add("North",pcontrols);
  248.    ptotal.add("Center",list=new java.awt.List(5,false));
  249.    if((color=getColor(getParameter("listbgcolor")))==null) color=Color.lightGray;
  250.    list.setBackground(color);
  251.    if((color=getColor(getParameter("listfgcolor")))==null) color=Color.black;
  252.    list.setForeground(color);
  253.    //stop Netscape from crashing
  254.    list.addItem("[AdvSiteSearcher c1999 The Gilbert Post]");
  255.    add("Center",ptotal);
  256.    validate();
  257.  }
  258.  
  259.  //Maybe this should be changed to getParameter(String,Object)
  260.  //so it can handle all of our needs?
  261.  public String getParameter(String name, String alt) {
  262.    String val=getParameter(name);
  263.    if(val!=null) return val;
  264.    return alt;
  265.  }
  266.  
  267.  public Color getColor(String s) {
  268.    if(s==null) return null;
  269.    s=s.toLowerCase();
  270.    if(s.startsWith("#")) {
  271.      if(s.length()!=7) return null;
  272.      else {
  273.        try {
  274.          int num=Integer.parseInt(s.substring(1,7),16);//parse a hex. string to dec.
  275.          return new Color(num);
  276.        }
  277.        catch(NumberFormatException e) {
  278.          return null;
  279.        }
  280.      }
  281.    }
  282.    else if("black".equals(s)) return Color.black;
  283.    else if("blue".equals(s)) return Color.blue;
  284.    else if("darkblue".equals(s)) return Color.blue.darker().darker().darker();
  285.    else if("lightblue".equals(s)) return Color.blue.brighter().brighter().brighter();
  286.    else if("cyan".equals(s)) return Color.cyan;
  287.    else if("darkgray".equals(s)) return Color.darkGray;
  288.    else if("lightgray".equals(s)) return Color.lightGray;
  289.    else if("green".equals(s)) return Color.green;
  290.    else if("gray".equals(s)) return Color.gray;
  291.    else if("magenta".equals(s)) return Color.magenta;
  292.    else if("orange".equals(s)) return Color.orange;
  293.    else if("pink".equals(s)) return Color.pink;
  294.    else if("red".equals(s)) return Color.red;
  295.    else if("white".equals(s)) return Color.white;
  296.    else if("yellow".equals(s)) return Color.yellow;
  297.    else return Color.getColor(s);
  298.  }
  299.  
  300.  //This probably belongs in a separate class...
  301.  //but I'd like to keep the additional downloads to a mininum.
  302.  //Parse links from HTML file(s) so that people can specify a directory
  303.  //from which we will pull the server's listing of files.
  304.  public void run() {
  305.    System.out.println("AdvSiteSearcher: Loading links from directory listings...");
  306.    displayMessage=true;
  307.    message="Loading links from directory listing(s)...";
  308.    repaint();
  309.    StringBuffer sb=new StringBuffer();//collect the internal URLs then add them to
  310.    //the files parameter.
  311.    //This is not a robust method for picking up links...
  312.    //however, it should work for the machine generated output from the server.
  313.    SearchSieve linkfinder=new SearchSieve("<a href=".toCharArray(),false);
  314.    StringTokenizer st=new StringTokenizer(dirs,"\n\r \t,",false);
  315.    String currToken;
  316.    BufferedInputStream bis;
  317.    URL url;
  318.    char c;
  319.    char prevc=(char)-1;
  320.    int i;
  321.    boolean inAHREF=false;
  322.    StringBuffer currlink=new StringBuffer();
  323.    String templink;//temporarily hold newly discovered links in here
  324.    //to discern whether they'll do us any good.
  325.    for(;;) {
  326.       if(!st.hasMoreTokens()) break;
  327.       currToken=st.nextToken();
  328.       try {
  329.         url=new URL(docbase,currToken);
  330.         System.out.println(url);
  331.         bis=new BufferedInputStream(url.openStream());
  332.       }
  333.       catch(Exception ewhatever) {System.out.println(ewhatever); continue;}
  334.       for(;;) {
  335.          try {
  336.            i=bis.read();
  337.          }
  338.          catch(IOException ieee) {
  339.            System.out.println(ieee);
  340.            try {
  341.              bis.close();
  342.              bis=null;
  343.              break;
  344.            }
  345.            catch(IOException ewetried) { System.out.println(ewetried); break;}
  346.          }
  347.          if(i==-1) break;
  348.          if(!inAHREF) c=Character.toLowerCase((char)i);
  349.          else c=(char)i;//Preserve the case if getting link
  350.          if(prevc==' ' && c==' ') continue;//cut out extra spaces
  351.          if(!inAHREF) inAHREF=linkfinder.addChar(c);
  352.          else {
  353.            if((c=='\"' || c=='\'' || c==' ' || c=='>')) {
  354.              if(currlink.length()>0) {
  355.                inAHREF=false;
  356.                templink=currlink.toString().toLowerCase();
  357.                //Weed out links to higher/lower dirs
  358.                if((!templink.startsWith("/") && !templink.startsWith("http://") && !templink.startsWith("../")) && (templink.endsWith(".html")||templink.endsWith(".htm")|| templink.endsWith(".shtml")||templink.endsWith(".txt")||templink.endsWith(".diz"))) {
  359.                   //The following code is a hack allowing webmasters to sneak an html file into "dirs"
  360.                   if(!currToken.endsWith(".html") && !currToken.endsWith(".htm")) {
  361.                     sb.append(currToken);
  362.                     if(!currToken.endsWith("/")) sb.append('/');
  363.                   }
  364.                  sb.append(currlink.toString());
  365.                  sb.append(",");
  366.                  System.out.println("  Added file "+currlink.toString()+" to the search.");
  367.                }
  368.                currlink.setLength(0);
  369.              }
  370.            }
  371.            else currlink.append(c);
  372.          }
  373.          prevc=c;
  374.       }
  375.    }
  376.    if(sb.length()>0) dirs=sb.toString();
  377.    else dirs=null;
  378.    displayMessage=false;
  379.    repaint();
  380.    finishInit();
  381.  }
  382.  
  383.  public Insets insets() {
  384.    return insets;
  385.  }
  386.  
  387.  public void paint(Graphics g) {
  388.    //Don't worry too much about making this pretty...
  389.    //It's just to let the viewer (probably a disappointed webmaster)
  390.    //know that there's something (probably bad) going on.
  391.    if(displayMessage) g.drawString(message,7,size().height/2);
  392.    //draw border
  393.    g.setColor(bordercolor);
  394.    g.drawRect(insets.left,insets.top,size().width-2*insets.left, size().height-2*insets.top);
  395.  }
  396.  
  397.  public void reset() {
  398.    stopAllSearches();
  399.    progressbar.reset();
  400.    list.clear();
  401.    //stop Netscape from crashing
  402.    list.addItem("[AdvSiteSearcher c1999 The Gilbert Post]");
  403.    nummatches=0;
  404.  }
  405.  
  406.  public void stopAllSearches() {
  407.    for(int i=0;i<workers.length;i++) workers[i].stopSearch();
  408.  }
  409.  
  410.  //I can't remember why I synchronized this...
  411.  //then maybe it doesn't need to be?
  412.  //Oh, yeah...I was worried that the wrong index would end
  413.  //up getting attached to the corresponding list item.
  414.  //int i==the index of the DocSearcher that found a match
  415.  public synchronized void foundMatch(int i) {
  416.    if(nummatches<matches.length);
  417.    matches[nummatches]=i;
  418.    nummatches++;
  419.    list.addItem(pageinfo[i]);
  420.    progressbar.plus(1);
  421.  }
  422.  
  423.  //A DocSearcher that is errored can only ever call this once
  424.  //because its errored flag is set
  425.  public void foundNoMatch(int i) {
  426.    if(workers[i].isErrored()) {
  427.      numWorkers--;
  428.      progressbar.setMax(numWorkers);
  429.    }
  430.    else progressbar.plus(1);
  431.  }
  432.  
  433.  //called after a DocSearcher opens a connection for the
  434.  //first time and can get some extra info like last modified date
  435.  //and file size
  436.  public void addInfo(int i,String more) {
  437.    pageinfo[i]+="   "+more;
  438.  }
  439.  
  440.  public boolean action(Event evt, Object arg) {
  441.    if(evt.target == list) {
  442.      stopAllSearches();
  443.      int index=list.getSelectedIndex();
  444.      if(index!=-1) showPage(index);
  445.    }
  446.    else if(evt.target == searchbox) {
  447.      search(searchbox.getText(),Exactbox.getState(),HTMLbox.getState());
  448.    }
  449.    else if(evt.target == stop) stopAllSearches();
  450.    else if(evt.target == search) search(searchbox.getText(),Exactbox.getState(),HTMLbox.getState());
  451.    return true;
  452.  }
  453.  
  454.  protected void search(String s,boolean bexact,boolean cutHTML) {
  455.    reset();
  456.    StringTokenizer st=new StringTokenizer(s,searchTokenSeparators,true);
  457.    Vector tempv=new Vector();
  458.    String currWord="";//this probably should be a StringBuffer
  459.    String currToken="";
  460.    boolean insideQuote=false;
  461.    while(st.hasMoreTokens()) {
  462.      currToken=st.nextToken();
  463.      if(searchTokenSeparators.indexOf(currToken)!=-1) {
  464.        if("\"".equals(currToken)) {
  465.          insideQuote=!insideQuote;
  466.          if(insideQuote) currWord="";
  467.          else {
  468.            if(currWord!="") tempv.addElement((new String(currWord)).toLowerCase());
  469.            currWord="";
  470.          }
  471.        }
  472.        else if(insideQuote) currWord+=currToken;
  473.      }
  474.      else if(!insideQuote) {
  475.        tempv.addElement((new String(currToken)).toLowerCase());
  476.        currWord="";
  477.      }
  478.      else if(insideQuote) {
  479.        currWord+=currToken;
  480.      }
  481.    }
  482.    if(currWord!="") tempv.addElement((new String(currWord)).toLowerCase());
  483.    String[] ss=new String[tempv.size()];
  484.    tempv.copyInto(ss);
  485.    for(int i2=0;i2<workers.length;i2++) workers[i2].searchFor(ss,bexact,cutHTML);
  486.  }
  487.  
  488.  protected void showPage(int ii) {
  489.    //result of klooge to stop Netscape from crashing
  490.    if(ii==0) {
  491.      stopAllSearches();
  492.      getAppletContext().showDocument(thegilbertpost,"_top");
  493.    }
  494.    int i=ii-1;
  495.    if(i<0) return;
  496.    stopAllSearches();
  497.    if(target!=null) getAppletContext().showDocument(urls[matches[i]],target);
  498.    else getAppletContext().showDocument(urls[matches[i]]);
  499.  }
  500.  
  501.  public void stop() {
  502.    stopAllSearches();
  503.    for(int i=0;i<workers.length;i++) workers[i].stopRunning();
  504.    //don't tie up CPU time when we're hidden or about to die
  505.    //when stopped should probably add an additional timer thread to
  506.    //null all the workers
  507.  }
  508.  
  509.  public void destroy() {
  510.    //Dump more
  511.    for(int i=0;i<workers.length;i++) workers[i].trashCache();
  512.  }
  513.  
  514. }//end AdvSiteSearcher.java
  515.