import java.awt.*; import java.awt.event.*; import java.applet.*; import java.io.*; import java.net.*; // Search applet // // Author: Richard Everitt (G4ZFE, g4zfe@g4zfe.com) // Last updated: 18th October 2004 // Version 1.20 // Purpose - this applet searches contest logs for a specified callsign. // The results are then displayed. // /* * Copyright (c) 1997-2004 Richard Everitt G4ZFE * g4zfe@g4zfe.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * * */ /* * Applet parameters * debug, "true", trace information is written to the Java console * "false" or not present, no trace info written * * log1Name .. log6Name, String to display next to radio button * * URL1Name .. URL6Name, URL (including directory) to read log from * * fileExtn, file extension for log files (defaults to TXT). * (not used any more) * * wildcards, determines if wildcards may be used (e.g G4* will * list all G4 callsigns from the log) * values = true, false (default = true) * * multiLog, determines if the logname should be displayed before * each match. This is useful if the applet is used * with several log files. * values = true, false (default = false) * * searchAllLogs, used to search all specified logs (up to maximum of 6). * Checkboxes are _not_ displayed. multiLog also set to true * values = true, false (default = false) * * appletFontName, the Font to use for the applet buttons and checkbox text * (default = Helvetica) * (e.g TimesRoman, Helvetica, Courier, Dialog, DialogInput) * resultFontName, the Font to use when returning search results in the search window * (default = Courier) * resultFontSize, the size of the Font to use when returning search results in the search window * (default = 12) * Example: * * * * * * * * * */ // v1.0a - working prototype. // v1.1. Note this applet is *still* under development! // Latest version at g4zfe.com/logsearch.html // - tidied up code slightly // - used IP address throughout to allow for use through a // firewall (previously had a firewall special version of // the applet) // v1.2. Had to remove IP addresses as Demon have changed the address // and URLs containing IP addresses are no longer working // v1.3 Added debug mode applet parameter // Made search a separate thread // v1.4 Added log1Name .. log5Name and URL1Name .. URL5name parameters // Made applet search in a separate directory (pointed to by // parameter URLxName) for each contest/log-file. // Above changes added to generalise the applet for use by others // v1.5 Added fileExtn parameter to allow Win 3.1 users to choose file // extension for log files (defaults to .txt) // Added wildcard facility (e.g search for K1* would show all K1s) // v1.6 Log files on server are now expected to be a.txt to z.txt. // This simplifies use for win 3.1 users and makes sense as the // files are text files and not really html files. // The fileExtn parameter can be used to keep using .HTML files // if required - note filename must be lower case but extension // can be upper case. // v1.7 Log files can now be stored as either 0.txt-z.txt or // 0.TXT-Z.TXT. // Increased number of contest radio buttons to 6 // v1.8 Check for 404 error from server if log file cannot be found // Allow fileExtn parameter to be used to override the upper case // then lower case filename search rule (this will increase // performance) // Added "stop" button. // v1.9 If upper case filename not found then lower case used. // v1.10 Added parameter to disable wildcard searches // Ability to search log files with no date and no time // information (for the paranoid) // Added parameter to display callsign of log. This is // useful if the applet is being used to search more // than one log. // v1.11 Callsigns such as F/G4ZFE were not being accepted // as valid callsigns in free format log files. // If only one log is being searched then do not // display checkboxes to select log. // v1.12 In free format log mode "J3E" and "A1A" were being // detected as callsigns!! Using CW and SSB were OK. // Allow callsign search to work with ADIF comments // Ignore all HTTP headers // v1.13 In free format log mode dates "21/10/1998" were being // parsed as callsigns. // v1.14 Updated to Java 2 // Enter key now works! // Allow fonts to be changed // Change display font to make search results readable // v1.15 In free format log mode "PSK31" was being detected as // a callsign. // v1.16 Allow '/' and '\' callsigns in callsigns i.e. 9m2\g4zfe and 9m2/g4zfe // Added autoWildcards parameter for PA5ET // v1.17 Allow for "AO21" and "UO14" as bands. Previously these bands were // interpreted as callsigns. // v1.18 Correctly interpret long callsigns such as Z3100SL (thanks to JJ1BDX/3 for // reporting the bug) // v1.19 Added searchAllLogs parameter. This will not display a checkbox for each log // and allows all logs (up to a maximum of 6) to be searched at once // v1.20 Compiled for JVM version 1.1 public class search extends Applet implements Runnable, ActionListener, ItemListener { String callsign = ""; // Callsign to search for int count = 0; // Number of QSOs found int numberLogs = 0; // Number of logs to open (in searchAllLogs mode) boolean logSelected = false; // Flag to stop search if no // log has been selected TextArea outputArea; // Search results are displayed here TextField callsignField; // Search callsign entered here Button search; // The search button Button clear; // The clear button Button abort; // The stop button Checkbox checkbox1 = null; // Checkboxes to select log/contest Checkbox checkbox2 = null; Checkbox checkbox3 = null; Checkbox checkbox4 = null; Checkbox checkbox5 = null; Checkbox checkbox6 = null; String log1Name = null; // String to display beside above String log2Name = null; String log3Name = null; String log4Name = null; String log5Name = null; String log6Name = null; String URL1Name = null; // URL to read log file from String URL2Name = null; String URL3Name = null; String URL4Name = null; String URL5Name = null; String URL6Name = null; private volatile Thread searchThread = null; // Search thread // Parameters boolean debugMode; // Allow debug messages to be written to the Java Console String fileExtn; // File extension parameter boolean wildcards; // Wildcard '*' allowed/disallowed in callsign searches boolean autoWildcards; // Automatically add '*' wildcard. To be used if the callsign // is not at the end of the line (default) boolean multiLog; // Applet works with > 1 logs boolean singleLog; // Applet used for only 1 log boolean searchAllLogs; // Search all logs at once String appletFontName = null; // Font for applet button text and checkboxes text String resultFontName = null; // Font for the result window String resultFontSize = null; // Size of the font for the result window public void destroy() { searchThread = null; // Stop the search thread } // This is where we start. Display the GUI public void init () { // Read applet parameters getAppletParameters (); Panel p; // The GUI consists of three panels arranged using the // Border layout manager // Panel 1 - describing text + contest checkboxes // Panel 2 - callsign entry field + buttons // Panel 3 - output text area setLayout (new FlowLayout ()); p = new Panel(); if ((log1Name != null && log2Name == null) || (URL1Name != null && URL2Name == null)) { // Only one log so don't bother with contest // checkboxes singleLog = true; numberLogs = 1; // Keep track of number of logs in searchAllLogs mode } else { // More than one log so display contest // checkboxes singleLog = false; } if (singleLog == false) { if (log1Name != null) { if (searchAllLogs == true) { // Keep track of number of logs in searchAllLogs mode numberLogs++; } else { checkbox1 = new Checkbox (" " + log1Name + " "); checkbox1.setFont (new Font(appletFontName, Font.BOLD, 12)); checkbox1.addItemListener(this); p.add (checkbox1); } } if (log2Name != null) { if (searchAllLogs == true) { // Keep track of number of logs in searchAllLogs mode numberLogs++; } else { checkbox2 = new Checkbox (" " + log2Name + " "); checkbox2.setFont (new Font(appletFontName, Font.BOLD, 12)); checkbox2.addItemListener(this); p.add (checkbox2); } } if (log3Name != null) { if (searchAllLogs == true) { // Keep track of number of logs in searchAllLogs mode numberLogs++; } else { checkbox3 = new Checkbox (" " + log3Name + " "); checkbox3.setFont (new Font(appletFontName, Font.BOLD, 12)); checkbox3.addItemListener(this); p.add (checkbox3); } } if (log4Name != null) { if (searchAllLogs == true) { // Keep track of number of logs in searchAllLogs mode numberLogs++; } else { checkbox4 = new Checkbox (" " + log4Name + " "); checkbox4.setFont (new Font(appletFontName, Font.BOLD, 12)); checkbox4.addItemListener(this); p.add (checkbox4); } } if (log5Name != null) { if (searchAllLogs == true) { // Keep track of number of logs in searchAllLogs mode numberLogs++; } else { checkbox5 = new Checkbox (" " + log5Name + " "); checkbox5.setFont (new Font(appletFontName, Font.BOLD, 12)); checkbox5.addItemListener(this); p.add (checkbox5); } } if (log6Name != null) { if (searchAllLogs == true) { // Keep track of number of logs in searchAllLogs mode numberLogs++; } else { checkbox6 = new Checkbox (" " + log6Name + " "); checkbox6.setFont (new Font(appletFontName, Font.BOLD, 12)); checkbox6.addItemListener(this); p.add (checkbox6); } } add ("North",p); } p = new Panel(); p.add (new Label ("Please enter callsign to search for:")); callsignField = new TextField ("",12); p.add (callsignField); search = new Button (" search "); search.setFont (new Font(appletFontName, Font.BOLD, 14)); p.add (search); clear = new Button (" clear "); clear.setFont (new Font(appletFontName, Font.BOLD, 14)); p.add (clear); abort = new Button (" stop "); abort.setFont (new Font(appletFontName, Font.BOLD, 14)); p.add (abort); add ("Center",p); search.addActionListener(this); clear.addActionListener(this); abort.addActionListener(this); callsignField.addActionListener(this); p = new Panel(); outputArea = new TextArea ("",12,60); outputArea.setFont (new Font(resultFontName, Font.PLAIN, Integer.valueOf(resultFontSize).intValue())); p.add (outputArea); add ("South",p); // Display version number outputArea.setText ("G4ZFE Log search applet v1.20 - 18/Oct/04"); // Always set text input field to white for readability callsignField.setBackground (Color.white); outputArea.setBackground (Color.white); } // read in Applet parameters public void getAppletParameters () { // Debug parameter String arg1 = getParameter ("debug"); String arg2 = getParameter ("wildcards"); String arg3 = getParameter ("multiLog"); String arg4 = getParameter ("autoWildcards"); String arg5 = getParameter ("searchAllLogs"); // Strings to display beside check box log1Name = getParameter ("log1Name"); log2Name = getParameter ("log2Name"); log3Name = getParameter ("log3Name"); log4Name = getParameter ("log4Name"); log5Name = getParameter ("log5Name"); log6Name = getParameter ("log6Name"); // URL to read log from URL1Name = getParameter ("URL1Name"); URL2Name = getParameter ("URL2Name"); URL3Name = getParameter ("URL3Name"); URL4Name = getParameter ("URL4Name"); URL5Name = getParameter ("URL5Name"); URL6Name = getParameter ("URL6Name"); // File extension parameter fileExtn = getParameter ("fileExtn"); // Autowildcard parameter debugMode = (arg1 == null) ? false : true; if (arg2 == null || arg2.equalsIgnoreCase ("true")) wildcards = true; else wildcards = false; if (arg3 == null || arg3.equalsIgnoreCase ("false")) multiLog = false; else multiLog = true; if (arg4 == null || arg4.equalsIgnoreCase ("false")) autoWildcards = false; else autoWildcards = true; if (arg5 == null || arg5.equalsIgnoreCase ("false")) searchAllLogs = false; else { searchAllLogs = true; multiLog=true; // If search all logs mode then need to display the logname // as well } // Font name for buttons and checkboxes appletFontName = getParameter ("appletFontName"); if (appletFontName == null) appletFontName = "Helevetica"; // Font name for result window resultFontName = getParameter ("resultFontName"); if (resultFontName == null) resultFontName = "Courier"; // Size of font for result window resultFontSize = getParameter ("resultFontSize"); if (resultFontSize == null) resultFontSize = "12"; } // Display parameter information public String[][] getParameterInfo() { String[][] info = { {"debug","boolean","Write debug messages to Java Console"}, {"log1Name","String","String to describe log 1"}, {"log2Name","String","String to describe log 2"}, {"log3Name","String","String to describe log 3"}, {"log4Name","String","String to describe log 4"}, {"log5Name","String","String to describe log 5"}, {"log6Name","String","String to describe log 6"}, {"URL1Name","String","Server directory for log 1"}, {"URL2Name","String","Server directory for log 2"}, {"URL3Name","String","Server directory for log 3"}, {"URL4Name","String","Server directory for log 4"}, {"URL5Name","String","Server directory for log 5"}, {"URL6Name","String","Server directory for log 6"}, {"fileExtn","String","file extension for server files"}, {"wildcards","boolean","True=allow wildcards, False=disallow"}, {"searchAllLogs","boolean","True=search all logs at once, False=search logs one at a time"}, {"appletFontName","String","Font for buttons and checkbox text"}, {"resultFontName","String","Font for applet result window"}, {"appletFontSize","String","Size of font for applet result window"}, }; return info; } // Display applet information public String getAppletInfo() { return "G4ZFE search applet v1.20 - g4zfe@g4zfe.com"; } // Function disableButtons // Purpose - grey out buttons in GUI whilst search is active protected void disableButtons () { search.setEnabled(true); clear.setEnabled(false); } // Function enableButtons // Purpose - enable buttons once search has completed protected void enableButtons () { search.setEnabled(true); clear.setEnabled(true); } public void start() { } public void stop() { if (searchThread != null) { // Stop the thread searchThread = null; } } public void itemStateChanged(ItemEvent check) { if (checkbox1 != null && checkbox1.getState() == true) { logSelected = true; return; } if (checkbox2 != null && checkbox2.getState() == true) { logSelected = true; return; } if (checkbox3 != null && checkbox3.getState() == true) { logSelected = true; return; } if (checkbox4 != null && checkbox4.getState() == true) { logSelected = true; return; } if (checkbox5 != null && checkbox5.getState() == true) { logSelected = true; return; } if (checkbox6 != null && checkbox6.getState() == true) { logSelected = true; return; } } public void actionPerformed (ActionEvent e) { Object arg = e.getSource(); // Check if the stop button has been pressed if (arg == abort) { // Stop the thread searchThread = null; // Enable buttons for another search enableButtons(); abort.setEnabled(false); // Reset counts etc count = 0; } // Check if the clear button has been pressed if (arg == clear) { // Clear output area outputArea.setText (""); // Clear input area callsignField.setText (""); // Reset all checboxes if (checkbox1 != null) checkbox1.setState (false); if (checkbox2 != null) checkbox2.setState(false); if (checkbox3 != null) checkbox3.setState(false); if (checkbox4 != null) checkbox4.setState(false); if (checkbox5 != null) checkbox5.setState(false); if (checkbox6 != null) checkbox6.setState(false); logSelected = false; } // Check if the search button has been pressed if (arg == search || arg == callsignField) { // Check that a callsign has been entered callsign = callsignField.getText(); if (callsign.length() == 0) { // No - display error // Clear output area outputArea.setText (""); outputArea.setText ("Please enter a callsign before starting the search\n"); // Do not start the search searchThread = null; return; } // Make sure that a log has been selected before attempting to start the search if (singleLog != true && searchAllLogs !=true) { // Check that a contest has been selected if (logSelected == false) { // Clear output area outputArea.setText (""); outputArea.setText ("Please select a contest/log to search\n"); return; } } // We`re ready to start the search now! // Disable buttons until search completes disableButtons(); abort.setEnabled(true); // Clear the output area outputArea.setText (""); count = 0; outputArea.setText ("Starting search\n"); searchThread = new Thread (this); searchThread.start(); } } public void run() { startSearch (callsign); } // This routine initiates the search in each of the contests void startSearch (String callsign) { Thread thisThread = Thread.currentThread(); int i = 0; // loop counter in searchAllLogs mode String urls[] = {URL1Name,URL2Name,URL3Name,URL4Name,URL5Name,URL6Name}; String logs[] = {log1Name,log2Name,log3Name,log4Name,log5Name,log6Name}; while (searchThread == thisThread) { if (singleLog == true) openURL (callsign, URL1Name, log1Name); else if (searchAllLogs == true) { while (i <= numberLogs-1) { openURL (callsign, urls[i], logs[i]); i++; } } else { if (checkbox1 != null && checkbox1.getState() == true) openURL (callsign, URL1Name, log1Name); if (checkbox2 != null && checkbox2.getState() == true) openURL (callsign, URL2Name, log2Name); if (checkbox3 != null && checkbox3.getState() == true) openURL (callsign, URL3Name, log3Name); if (checkbox4 != null && checkbox4.getState() == true) openURL (callsign, URL4Name, log4Name); if (checkbox5 != null && checkbox5.getState() == true) openURL (callsign, URL5Name, log5Name); if (checkbox6 != null && checkbox6.getState() == true) openURL (callsign, URL6Name, log6Name); } outputArea.setText(outputArea.getText() + "Search completed ... " + count + " QSOs found\n"); // Search completed, so enable buttons searchThread = null; enableButtons(); abort.setEnabled(false); } } // This routine connects to the server and obtains the contest log // according to the callsign. The contest log is then searched // in to determine if a QSO has been made void openURL (String call, String urlName, String logName) { String url[] = {"",""}; int index = 0; boolean fileOpened = false; boolean posCallsignFound = false; BufferedReader dis = null; URL doc = null; String lowerCaseChars = "abcdefghijklmnopqrstuvwxyz"; int callsignPosition = 0; int commentPosition = 0; // Convert callsign to upper case. The first character of the // callsign is used to determine which log file to open // i.e. look for G4ZFE in the IARU logs in file // G.TXT (note upper case filename) // This is done to reduce the size of the file to search // (and download time!) String callsign = call.toUpperCase (); String fname = call.toLowerCase(); if (autoWildcards) { callsign += "*"; } if (fileExtn == null) { url[0] = urlName + callsign.charAt(0) + ".TXT"; url[1] = urlName + fname.charAt(0) + ".txt"; } else { // Allow for other file extensions. if (lowerCaseChars.indexOf(fileExtn.charAt(0)) != -1) { // File extension begins with a lower case // character, so search for lower case filenames first url[0] = urlName + fname.charAt(0) + "." + fileExtn; url[1] = urlName + callsign.charAt(0) + "." + fileExtn.toUpperCase(); } else { // File extension begins with an upper case // character, so search for upper case filenames first url[0] = urlName + callsign.charAt(0) + "." + fileExtn; url[1] = urlName + fname.charAt(0) + "." + fileExtn.toUpperCase(); } } while (fileOpened == false && index <= 1) { if (debugMode) System.out.println ("Opening url " + url[index] + "\n"); // Open connection to log file try { doc = new URL (url[index]); dis = new BufferedReader (new InputStreamReader(doc.openStream())); } catch (Exception e) { if (index == 0) { // Try lower case file names index++; continue; } else { // Connection to server could not be made outputArea.setText (outputArea.getText() + "Sorry .. " + logName + " log not found!\n"); System.out.println ("openStream(): Exception: " + e + "\n"); return; } } fileOpened = true; try { String line; String subString = ""; // Read a line at a time from the log file while ((line = dis.readLine()) != null) { // Check for 404 File not found // error from server if (line.indexOf("HTTP") != -1) { if (line.indexOf("404") != -1) { fileOpened = false; index++; doc = null; dis.close(); dis = null; if (debugMode) System.out.println ("HTTP error: " + line + "\n"); break; } // Ignore all HTTP lines continue; } //Ignore Apache WWW server HTTP headers if (line.indexOf("Date:") != -1 || line.indexOf("Server:") != -1 || line.indexOf("Last-Modified:") != -1 || line.indexOf("ETag:") != -1 || line.indexOf("Content-Length:") != -1 || line.indexOf("Accept-Ranges:") != -1 || line.indexOf("Connection:") != -1 || line.indexOf("Content-Type:") != -1) continue; // Ignore blank lines if (line.length() == 0) continue; try { // If this is the first time that this log // has been opened then determine the // column position of the callsign if (posCallsignFound == false) { callsignPosition = callsignColumn (line); posCallsignFound = true; } // Read the callsign from the log // If ADIF comments are being used then only read up // to the start of the comment commentPosition = line.indexOf ("**"); if (commentPosition != -1) subString = line.substring(callsignPosition, commentPosition-1); else subString = line.substring(callsignPosition, line.length()); // Trim any white space subString = subString.trim(); // Check if the callsign is in this line searchLine (subString, callsign, logName, line); } catch (StringIndexOutOfBoundsException eString) { if (index != 0) { // Probably an incorrectly formatted log file outputArea.setText (outputArea.getText() + "Incorrectly formatted log file: " + url + "\n"); System.out.println ("subString(): Exception: " + eString + "\n"); } } } } catch (IOException e) { // This exception may have been caused by the file not being // found. Try opening the lower case version of this filename if (index == 0) { // Try lower case file names fileOpened = false; index++; continue; } // Both the lower and upper case filenames could not be found. // This is a real problem outputArea.setText (outputArea.getText() + "Error whilst reading contest log file..\n"); System.out.println ("readLine(): Exception: " + e + "\n"); enableButtons(); abort.setEnabled(false); return; } } if (index > 1) outputArea.setText (outputArea.getText() + "Sorry .. " + logName + " log not found!\n"); else if (count == 0) { // If no QSOs have been found then say so if (logName != null) outputArea.setText (outputArea.getText() + "Sorry .. not in " + logName + " log!\n"); else outputArea.setText (outputArea.getText() + "Sorry .. not in log!\n"); } } // This routine takes a line from the log and returns // the column position that the callsign is in int callsignColumn (String line) { int i=0; int numberCharactersSkip=0; while (i <= line.length()) { numberCharactersSkip = checkValidCallsign (line.substring (i, line.length())); if (numberCharactersSkip == 0) { if (debugMode) { System.out.println ("callsign is " + line.substring(i,line.length()) + "\n"); } return (i); } i = i + numberCharactersSkip; } return 0; } // This routine takes a string and attempts to determine // if the string is a callsign. // Returns: number of chracters to skip if mode/date found. // Return 0 if callsign found int checkValidCallsign (String text) { String upperText = text.toUpperCase(); // Ignore dates "10/11/98" if (isNumeric (upperText.charAt (0)) && isNumeric (upperText.charAt (1)) && isNumeric (upperText.charAt (2))) return (1); // Ignore mode "F3E" if (upperText.charAt(0) == 'F' && upperText.charAt(1) == '3' && upperText.charAt(2) == 'E' && upperText.charAt(3) == ' ') return (3); // Ignore mode "J3E" if (upperText.charAt(0) == 'J' && upperText.charAt(1) == '3' && upperText.charAt(2) == 'E' && upperText.charAt(3) == ' ') return (3); // Ignore mode "A1A" if (upperText.charAt(0) == 'A' && upperText.charAt(1) == '1' && upperText.charAt(2) == 'A' && upperText.charAt(3) == ' ') return (3); // Ignore mode "PSK31" if (upperText.charAt(0) == 'P' && upperText.charAt(1) == 'S' && upperText.charAt(2) == 'K' && upperText.charAt(3) == '3' && upperText.charAt(4) == '1') return (5); // Ignore band "AO21" if (upperText.charAt(0) == 'A' && upperText.charAt(1) == 'O' && upperText.charAt(2) == '2' && upperText.charAt(3) == '1' && upperText.charAt(4) == ' ') return (5); // Ignore band "UO14" if (upperText.charAt(0) == 'U' && upperText.charAt(1) == 'O' && upperText.charAt(2) == '1' && upperText.charAt(3) == '4' && upperText.charAt(4) == ' ') return (5); // Check callsign type: 2E0ABC if (isNumeric (upperText.charAt (0)) && isAlpha (upperText.charAt (1)) && isNumeric (upperText.charAt (2))) return (0); // Check callsign type: G2AAA if (isAlpha (upperText.charAt (0)) && isNumeric (upperText.charAt (1)) && isAlpha (upperText.charAt (2))) return (0); // Check callsign type: GM2AAA if (isAlpha (upperText.charAt (0)) && isAlpha (upperText.charAt (1)) && isNumeric (upperText.charAt (2))) return (0); // Check callsign type: HAM4AA if (isAlpha (upperText.charAt (0)) && isAlpha (upperText.charAt (1)) && isAlpha (upperText.charAt (2)) && isNumeric (upperText.charAt (3))) return (0); // Check callsign type: 3DA5AA if (isNumeric (upperText.charAt (0)) && isAlpha (upperText.charAt (1)) && isAlpha (upperText.charAt (2)) && isNumeric (upperText.charAt (3))) return (0); // Check callsign type: H22A if (isAlpha (upperText.charAt (0)) && isNumeric (upperText.charAt (1)) && isNumeric (upperText.charAt (2)) && isAlpha (upperText.charAt (3))) return (0); // check callsign type: Z3100SL if (isAlpha (upperText.charAt (0)) && isNumeric (upperText.charAt (1)) && isNumeric (upperText.charAt (2)) && isNumeric (upperText.charAt (3)) && isNumeric (upperText.charAt (4))) return (0); return (1); } boolean isAlpha (char ch) { if (ch >= 'A' && ch <='Z' || ch == '/' || ch == '\\') return true; else return false; } boolean isNumeric (char ch) { if (ch >= '0' && ch <='9' || ch == '/' || ch == '\\') return true; else return false; } void searchLine (String subString, String callsign, String logName, String line) { // Check for exact match of callsigns if (subString.equalsIgnoreCase (callsign) == true) { if (multiLog) outputArea.setText (outputArea.getText() + "(" + logName + ") " + line + "\n"); else outputArea.setText (outputArea.getText() + line + "\n"); // One more QSO found count++; } // Check if wildcard int posWildcard = callsign.indexOf ("*"); if (posWildcard != -1 && wildcards) { // Yes - do wildcard search String wildcard = callsign.substring(0,posWildcard); if (subString.startsWith(wildcard)) { if (multiLog) outputArea.setText (outputArea.getText() + "(" + logName + ") " + line + "\n"); else outputArea.setText (outputArea.getText() + line + "\n"); // One more QSO found count++; } } } }