// In order to get this to work as an applet, I've had to bust the // internal security! You can't make them much richer than that... import java.awt.*; import java.awt.event.*; import java.util.zip.*; import java.io.*; import java.net.*; import java.util.*; import java.text.DateFormat; public class Search extends java.applet.Applet { public String getAppletInfo() { return "Applet to search the FEISTFANS-L archive"; } public void init() { try { // Hard coded! *sigh* base = new URL("http://www.cs.man.ac.uk/~fellowsd/REF/"); } catch (MalformedURLException e) { // Ignore - it won't happen! } buildUI(); } public synchronized void destroy() { if (getSearcher() != null) { getSearcher().stop(); } } public synchronized void start() { if (getSearcher() != null) { getSearcher().resume(); } } public synchronized void stop() { if (getSearcher() != null) { getSearcher().suspend(); } } public TextField searchPattern; public TextArea results; public Label amWorking, whichArchive; public boolean showHeaders; public void setShow(boolean flag) { showHeaders = flag; } public class Button_Search implements ActionListener { Search s; public Button_Search(Search s) { this.s = s; } public void actionPerformed(ActionEvent e) { s.searchPattern.requestFocus(); s.results.setText(""); s.search(s.searchPattern.getText()); } } public class Button_Clear implements ActionListener { Search s; public Button_Clear(Search s) { this.s = s; } public void actionPerformed(ActionEvent e) { s.killSearcher(); s.results.setText(""); s.searchPattern.setText(""); s.searchPattern.requestFocus(); } } public class Checkbox_Flag implements ItemListener { Search s; public Checkbox_Flag(Search s) { this.s = s; } public void itemStateChanged(ItemEvent e) { s.setShow(((Checkbox)(e.getSource())).getState()); } } public void buildUI() { this.setLayout( new BorderLayout()); Panel p = new Panel(); p.setLayout( new BorderLayout()); this.add(p, "North"); Label l = new Label("Text to search for:"); searchPattern = new TextField(); searchPattern.requestFocus(); p.add(l, "West"); p.add(searchPattern); this.add(results = new TextArea("", 24,80, TextArea.SCROLLBARS_BOTH)); results.setEditable(false); p = new Panel(); p.setLayout( new BorderLayout()); Panel q = new Panel(); this.add(p, "South"); Button doSearch = new Button("Start Search"); doSearch.addActionListener( new Button_Search( this)); Button clear = new Button("Clear"); clear.addActionListener( new Button_Clear( this)); Checkbox sh = new Checkbox("Show all (reasonable) headers", false); sh.addItemListener( new Checkbox_Flag( this)); p.add(q, "North"); q.add(sh); q.add(doSearch); q.add(clear); amWorking = new Label("Status: idle"); whichArchive = new Label(""); p.add(amWorking, "West"); p.add(whichArchive); } /** * The separator between messages. */ public static final String sep = " "; /** * The separator between the full headers and the useful ones! */ public static final String skipbefore = "*** EOOH ***"; public URL base; public static String encoding = "ISO-8859-1"; public Searcher searcher; public synchronized Searcher getSearcher() { return searcher; } public synchronized void setSearcher(Searcher searcher) { this.searcher = searcher; } public BufferedReader openDataStream(String filename) throws MalformedURLException, IOException { // Get the net stream URL url = new URL(base, filename); InputStream netStream = url.openStream(); // Get the reader (possibly with gunzipping...) Reader r; if (filename.endsWith(".gz")) { InputStream is = new GZIPInputStream(netStream); r = new InputStreamReader(is, encoding); } else { r = new InputStreamReader(netStream, encoding); } // Get the buffered input stream, and return... return new BufferedReader(r); } public static final String visualsep = "--------------------------------------------------------------------------------\n"; public class Searcher extends Thread { Search ui; String manifestFile; String searchString; private Calendar calendar; private DateFormat formatter; public Searcher(Search ui, String searchString, String manifestFile) { super(); this.ui = ui; this.searchString = searchString; this.manifestFile = manifestFile; this.start(); calendar = Calendar.getInstance(); formatter = DateFormat.getDateInstance(DateFormat.MEDIUM); } public Searcher(Search ui, String searchString) { this(ui, searchString, "manifest.txt"); } void puts(String s) { ui.results.append(s); } void progress(String s) { ui.whichArchive.setText(s); } private Date parseDateFromString(String s, int start, int end) { int y,m,d; d = Integer.parseInt(s.substring(start, start+1)); m = Integer.parseInt(s.substring(start+3, start+4)); y = Integer.parseInt(s.substring(start+6, end-1)); if (y<50) { y += 2000; } else if (y<100) { y += 1900; } synchronized(calendar) { calendar.set(y,m,d); return calendar.getTime(); } } private String printDateToString(Date date) { return formatter.format(date); } void progress(String head, String filename, String tail) { if (filename.startsWith("archive.")) { int datestart1 = 8; int dateend1 = filename.indexOf(",",datestart1); int datestart2 = dateend1+1; int dateend2 = filename.indexOf(".",datestart2); if (dateend2<0) { dateend2 = filename.length(); } Date date1 = parseDateFromString(filename, datestart1, dateend1); Date date2 = parseDateFromString(filename, datestart2, dateend2); progress("(examining archive period from " + printDateToString(date1) + " to " + printDateToString(date2) + ")"); } else { progress(head + filename + tail); } } Enumeration getFilenames(String manifestFile) throws IOException { BufferedReader manifest = ui.openDataStream(manifestFile); Stack filenameStack = new Stack(); try { while (true) { String s = manifest.readLine(); if (s == null) { break; } s = s.trim(); if (s.length() <= 0 || s.startsWith("<")) { break; } filenameStack.push(s); } } catch (EOFException e) {} manifest.close(); return filenameStack.elements(); } boolean finishedflag, matchedflag; String getMessage(BufferedReader is, String pat) throws IOException { StringBuffer message = new StringBuffer(); matchedflag = false; boolean skipping = true; boolean showHeaders = ui.showHeaders; try { while (true) { String s = is.readLine(); if (s == null) { finishedflag = true; break; } if (s.equals(sep)) { break; } else if (skipping) { if (s.equals(skipbefore)) { skipping = false; } continue; } if (!showHeaders) { if (s.length() == 0) { showHeaders = true; } else if (!s.startsWith("Date:") && !s.startsWith("From:") && !s.startsWith("Subject:")) { continue; } } if (!matchedflag && s.indexOf(pat) >= 0) { matchedflag = true; } message.append(s).append("\n"); } } catch (EOFException e) { finishedflag = true; } return message.toString(); } Cursor savedCursor; public synchronized void makeBusy() { if (savedCursor == null) { setSearcher(this); savedCursor = ui.results.getCursor(); ui.results.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); ui.amWorking.setText("Status: busy"); } } public synchronized void makeIdle() { if (savedCursor != null) { ui.results.setCursor(savedCursor); ui.amWorking.setText("Status: idle"); progress(""); setSearcher(null); savedCursor = null; } } public void run() { makeBusy(); puts("Looking for \"" + searchString + "\"\n"); try { progress("(reading manifest)"); Enumeration filenames = getFilenames(manifestFile); if (searchString.length()==0 || !filenames.hasMoreElements()) { puts("empty search!"); } else { while (filenames.hasMoreElements()) { String filename = (String)filenames.nextElement(); progress("(looking in ", filename, ")"); BufferedReader stream = ui.openDataStream(filename); finishedflag = false; while (!finishedflag) { String msg = getMessage(stream, searchString); if (matchedflag) { puts(visualsep + msg); } } } puts(visualsep + "Search completed."); } } catch (MalformedURLException mue) { puts("MalformedURLException thrown! Detail follows:\n" + mue.toString() + "\n"); } catch (UnknownHostException uhe) { puts("Could not find host \"" + base.getHost() + "\"\n"); } catch (IOException ioe) { puts("IOException thrown! Details follows:\n" + ioe.toString() + "\n"); } makeIdle(); } } public synchronized void killSearcher() { try { getSearcher().stop(); getSearcher().makeIdle(); } catch (Exception e) {} } public synchronized void search(String searchString) { killSearcher(); new Searcher(this, searchString); } }