/* Similar Words #1 (formerly: Word Theory #1 - Similar Words) Written by: Keith Fenske, http://www.psc-consulting.ca/fenske/ Thursday, 5 January 2006 Java class name: SimilarWords1 Copyright (c) 2006 by Keith Fenske. Released under GNU Public License. This is a graphical Java 1.4 Swing (GUI) application to help find unique words or identifiers, by avoiding words that are similar to each other. Two words are considered similar by this program if you can convert one word into the other by: (1) Changing one letter: bat / cat; (2) Adding one letter: bat / boat; (3) Deleting one letter: frog / fog; or (4) Reversing two consecutive letters: two / tow. All of these changes are common typographical errors, and avoiding words that are too similar provides a degree of error checking. Not shown is a fifth common error: "off by one" where fingers are shifted one key in some direction, resulting in numbers like 1342 instead of 2453, or "hal" instead of "ibm" (a reference to the movie, 2001: A Space Odyssey). This is more of a problem with numeric keypads than with alphanumeric keyboards. Words in this program are defined to be any combination of letters or digits (that is, an alphanumeric string), and are converted to lowercase for processing. This range of words is what you might expect for identifiers such as login accounts, program module and variable names, or abbreviations. For convenience, there are two lists of words: one of input words and another of "known" words. The known words can be loaded from a file, or added to from the input list. The intention is to maintain a list of words that have been previously checked, stored in a file, and to test new words against this list before they are added. GNU General Public License (GPL) -------------------------------- SimilarWords1 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 3 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, see the http://www.gnu.org/licenses/ web page. Additional Comments ------------------- People make mistakes when they type numbers, words, almost anything. The most common mistakes are the four categories listed above. Most mistakes with letters don't result in proper words. Most mistakes with numbers do. Consider telephone numbers. If you change one digit or reverse two digits, you usually get a valid telephone number, which is a "wrong" number for you, but still someone answers and you have to excuse yourself. This happens because telephone numbers use almost all possible combinations of digits. It doesn't happen as often with words because there are many more combinations of letters than there are words, and words that we don't recognize become nonsense. To avoid this problem with telephone numbers, don't assign every number. How many to reserve depends upon how careful you want to be. Hint: prime numbers are very helpful here, especially numbers that are relatively prime to ten. Assign only those telephone numbers that have a certain remainder when divided by a chosen prime number. The prime number 3 is too small, five won't work at all, seven is better, eleven beats seven, etc. The bigger the prime number, the more error checking you have. What's the smallest prime number that will catch each of the four categories of mistakes listed above (individually)? What's the smallest prime number that will catch all four categories (collectively)? You can't use prime numbers on words. You can compare words to each other and choose only those words that can't be confused by the four categories listed above. */ import java.awt.*; // older Java GUI support import java.awt.event.*; // older Java GUI event support import java.io.*; // standard I/O import java.util.*; // lists, maps, vectors import java.util.regex.*; // regular expressions import javax.swing.*; // newer Java GUI support public class SimilarWords1 { /* constants */ static final String COPYRIGHT_NOTICE = "Copyright (c) 2006 by Keith Fenske. Released under GNU Public License."; static final String PROGRAM_TITLE = "Similar Words - by: Keith Fenske"; static final String REGULAR_WORD = "[0-9a-z]+"; // regular expression for alphanumeric word /* class variables */ static JButton addToListButton; // "Add To Word List" button static boolean checkAddFlag; // true if we check adding one letter static JCheckBox checkAddOption; // option to check adding one letter static boolean checkChangeFlag; // true if we check changing one letter static JCheckBox checkChangeOption; // option to check changing one letter static boolean checkDeleteFlag; // true if we check deleting one letter static JCheckBox checkDeleteOption; // option to check deleting one letter static boolean checkReverseFlag; // true if we check reversing two letters static JCheckBox checkReverseOption; // option to check reversing two letters static boolean errorsOnlyFlag; // true if we report only words with errors static JCheckBox errorsOnlyOption; // option to report errors or all words static JButton exitButton; // "Exit" button static JFileChooser fileChooser; // asks for input and output file names static Vector inputList; // converted to a word list static JTextField inputText; // user input for testing new words static JFrame mainFrame; // this application's window static JButton openListButton; // "Open Word List" button static JTextArea outputText; // generated output text static Pattern patternWord; // compiled static JButton saveListButton; // "Save Word List" button static JButton saveOutputButton; // "Save Output As" button static JButton showListButton; // "Show Word List" button static JButton testInputButton; // "Test Input Words" button static JButton testListButton; // "Test Word List" button static Vector wordList; // current list of known words /* main() method We run as a graphical application only. Set the window layout and then let the graphical interface run the show. */ public static void main(String[] args) { ActionListener action; // our shared action listener /* Initialize non-graphical local and class variables. */ checkAddFlag = true; // by default, check adding one letter checkChangeFlag = true; // by default, check changing one letter checkDeleteFlag = true; // by default, check deleting one letter checkReverseFlag = true; // by default, check reversing two letters errorsOnlyFlag = true; // by default, report only words with errors patternWord = Pattern.compile(REGULAR_WORD); // compile regular expression once only wordList = new Vector(); // by default, empty list for words /* Create the graphical interface as a series of little panels inside bigger panels. The intermediate panel names are of no lasting importance and hence are only numbered (panel1, panel2, etc). */ action = new SimilarWords1User(); // create our shared action listener fileChooser = new JFileChooser(); // create our shared file chooser /* Create a vertical box to stack buttons and options. */ Box panel1 = new Box(BoxLayout.Y_AXIS); panel1.add(Box.createVerticalStrut(9)); // extra space at panel top /* Create a horizontal panel to hold the action buttons. */ JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 50, 5)); openListButton = new JButton("Open Word List..."); openListButton.addActionListener(action); openListButton.setMnemonic(KeyEvent.VK_W); openListButton.setToolTipText("Read new word list from a text file."); panel2.add(openListButton); saveListButton = new JButton("Save Word List..."); saveListButton.addActionListener(action); saveListButton.setMnemonic(KeyEvent.VK_S); saveListButton.setToolTipText("Save current word list in a text file."); panel2.add(saveListButton); saveOutputButton = new JButton("Save Output As..."); saveOutputButton.addActionListener(action); saveOutputButton.setMnemonic(KeyEvent.VK_O); saveOutputButton.setToolTipText("Save generated output text in a file."); panel2.add(saveOutputButton); exitButton = new JButton("Exit"); exitButton.addActionListener(action); exitButton.setMnemonic(KeyEvent.VK_X); exitButton.setToolTipText("Close this program."); panel2.add(exitButton); panel1.add(panel2); /* Create a horizontal panel for options. */ JPanel panel3 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5)); panel3.add(new JLabel("Check:")); checkAddOption = new JCheckBox("add one letter", checkAddFlag); panel3.add(checkAddOption); checkChangeOption = new JCheckBox("change one", checkChangeFlag); panel3.add(checkChangeOption); checkDeleteOption = new JCheckBox("delete one", checkDeleteFlag); panel3.add(checkDeleteOption); checkReverseOption = new JCheckBox("reverse two", checkReverseFlag); panel3.add(checkReverseOption); panel3.add(Box.createHorizontalStrut(20)); panel3.add(new JLabel("Report:")); errorsOnlyOption = new JCheckBox("errors only", errorsOnlyFlag); panel3.add(errorsOnlyOption); panel1.add(panel3); /* Create a horizontal panel for the user's input text area. */ JPanel panel4 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5)); panel4.add(new JLabel("Input words:")); inputText = new JTextField(47); inputText.addActionListener(action); inputText.setText(""); inputText.setToolTipText("Type words to be tested here."); panel4.add(inputText); panel1.add(panel4); /* Create another horizontal panel for more control buttons. */ JPanel panel5 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5)); testInputButton = new JButton("Test Input Words"); testInputButton.addActionListener(action); testInputButton.setMnemonic(KeyEvent.VK_T); testInputButton.setToolTipText("Test input words for similarity."); panel5.add(testInputButton); panel5.add(Box.createHorizontalStrut(20)); addToListButton = new JButton("Add To Word List"); addToListButton.addActionListener(action); addToListButton.setMnemonic(KeyEvent.VK_A); addToListButton.setToolTipText("Add input words to word list."); panel5.add(addToListButton); panel5.add(Box.createHorizontalStrut(20)); showListButton = new JButton("Show Word List"); showListButton.addActionListener(action); showListButton.setMnemonic(KeyEvent.VK_H); showListButton.setToolTipText("Show word list in output text area."); panel5.add(showListButton); panel5.add(Box.createHorizontalStrut(20)); testListButton = new JButton("Test Word List"); testListButton.addActionListener(action); testListButton.setMnemonic(KeyEvent.VK_E); testListButton.setToolTipText("Test all words in word list."); panel5.add(testListButton); panel1.add(panel5); panel1.add(Box.createVerticalStrut(1)); // extra space at panel bottom /* Put above boxed options in a panel that is centered horizontally. */ JPanel panel6 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); panel6.add(panel1); /* Create a scrolling text area to hold the generated output. */ outputText = new JTextArea(6, 30); outputText.setEditable(false); // user can't change this text area outputText.setFont(new Font("Dialog", Font.PLAIN, 16)); outputText.setLineWrap(true); // allow text lines to wrap outputText.setMargin(new Insets(10, 12, 10, 12)); outputText.setText("\nThis program compares new words against a list of" + " known words, to see if the new words are too similar. That is, if" + " an input word can be converted into a known word by a simple" + " typographical error: add one letter, change one letter, delete one" + " letter, or reverse two letters.\n\n" + COPYRIGHT_NOTICE + "\n\n"); outputText.setWrapStyleWord(true); // try to wrap at white space /* Create the main window frame for this application. Stack buttons and options above the text area. Keep text in the center so that it expands horizontally and vertically. */ mainFrame = new JFrame(PROGRAM_TITLE); Container panel7 = mainFrame.getContentPane(); // where content meets frame panel7.setLayout(new BorderLayout(5, 5)); panel7.add(panel6, BorderLayout.NORTH); panel7.add(new JScrollPane(outputText), BorderLayout.CENTER); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.setLocation(50, 50); // top left corner of application window mainFrame.setSize(700, 500); // initial size of application window mainFrame.validate(); // do the application window layout mainFrame.setVisible(true); // show the application window /* Let the graphical interface run the application now. */ } // end of main() method // ------------------------------------------------------------------------- // /* addInputToList() method Add input words from the user to the current word list. We have to clean up the input first, then make sure that those words aren't already in the list of known words. */ static void addInputToList() { int i; // index variable String word; // one word from the input int wordCount; // number of words added to word list /* Copy input words to the current word list. */ getInputWords(); // parse and reformat input words, if any wordCount = 0; // no words added yet to word list for (i = 0; i < inputList.size(); i ++) // for each word in input list { word = (String) inputList.get(i); // get one word from input list if (wordList.contains(word)) // have we seen this word before? { putOutput("Input word \"" + word + "\" is already in word list."); } else { wordCount ++; // one more valid word added to word list wordList.add(word); // and put that word into the list } } Collections.sort(wordList); // sort into ascending string order /* Tell the user what we found. There are more special cases here than are truly necessary, but it's fun to format message strings in something that looks like proper English. */ if (wordCount > 1) // if at least two words were found { putOutput("Added " + wordCount + " new words to word list, which now has " + wordList.size() + " words."); } else if (wordCount > 0) // if only one word was found { if (wordList.size() > 1) putOutput("Added one new word to word list, which now has " + wordList.size() + " words."); else putOutput( "Added one new word to word list, which has only that word."); } else // no new words, but how big is word list? { if (wordList.size() > 1) putOutput("No new words added to word list, which has " + wordList.size() + " words."); else if (wordList.size() > 0) putOutput("No new words added to word list, which has only one word."); else putOutput("No new words added to word list, which is empty."); } } // end of addInputToList() method /* canAddLetter() method Return if the first string can be changed into the second string by adding one character. The second string must be one character longer than the first string. */ static boolean canAddLetter(String first, String second) { int i; // index variable int length; // length of strings length = first.length(); // get length of first string if ((length + 1) != second.length()) // first must be one char shorter return (false); // no, first can't be changed into second /* Skip over the first part of both strings that is the same. */ i = 0; // start comparing at first character while ((i < length) && (first.charAt(i) == second.charAt(i))) i ++; // keep comparing until difference or end if (i == length) // if everything is same except last char return (true); // then last char in second is the addition /* We found a difference in the middle of the strings. Skip one character in the second (longer) string and check if the remainder matches. */ while (i < length) { if (first.charAt(i) != second.charAt(i + 1)) // another difference? return (false); // then can't change first into second i ++; // next index to look at } return (true); // can insert one char to get second string } // end of canAddLetter() method /* canChangeLetter() method Return if the first string can be changed into the second string by replacing one character. For this to be possible, both strings must have the same length, and at most one character can be different. If the two strings are identical, this method will also return . */ static boolean canChangeLetter(String first, String second) { boolean differ; // true if a difference has been found int i; // index variable int length; // length of strings length = first.length(); // get length of first string if (length != second.length()) // are they the same length? return (false); // no, first can't be changed into second differ = false; // no differences found yet for (i = 0; i < length; i ++) // for each character in the strings { if (first.charAt(i) != second.charAt(i)) // same character in both? if (differ) // no, was a previous difference found? return (false); // yes, a second difference means failure else differ = true; // no, this is the first difference } return (true); // strings must differ by less than one char } // end of canChangeLetter() method /* canDeleteLetter() method Return if the first string can be changed into the second string by deleting one character. The first string must be one character longer than the second string. */ static boolean canDeleteLetter(String first, String second) { return (canAddLetter(second, first)); // symmetrical } /* canReverseLetters() method Return if the first string can be changed into the second string by reversing two characters. For this to be possible, both strings must have the same length, and at most two consecutive characters can be different. If the two strings are identical, this method will also return . */ static boolean canReverseLetters(String first, String second) { int differ; // number of characters that are different int i; // index variable int length; // length of strings int start; // index of first character that differs length = first.length(); // get length of first string if (length != second.length()) // are they the same length? return (false); // no, first can't be changed into second /* Count differences, until we hit the end of the string or two of the differences are not consecutive. */ differ = 0; // no differences found yet start = -1; // just to keep compiler happy for (i = 0; i < length; i ++) // for each character in the strings { if (first.charAt(i) != second.charAt(i)) // same character in both? { differ ++; // one more character is different if (differ == 1) // is this the first difference? start = i; // yes, save index of first characters else if (i != (start + 1)) // are characters consecutive? return (false); // no, too many differences or too far apart } } /* There are two or fewer differences. Check for true equality and for being able to swap (reverse) two characters. */ if (differ == 0) // if the two strings are identical return (true); else if ((differ == 2) // if two consecutive differences && (first.charAt(start) == second.charAt(start + 1)) && (first.charAt(start + 1) == second.charAt(start))) { return (true); // yes, first can be changed into second } else return (false); // everything else means failure } // end of canReverseLetters() method /* checkOptions() method Check the user's options. They're just a bunch of flags, but if they are all turned off at the same time, then there's nothing for us to process. We return if the options are okay, and if there is a problem. */ static boolean checkOptions() { checkAddFlag = checkAddOption.isSelected(); checkChangeFlag = checkChangeOption.isSelected(); checkDeleteFlag = checkDeleteOption.isSelected(); checkReverseFlag = checkReverseOption.isSelected(); errorsOnlyFlag = errorsOnlyOption.isSelected(); if ((checkAddFlag || checkChangeFlag || checkDeleteFlag || checkReverseFlag) == false) { putOutput("Please select at least one of the options (check boxes)."); return (false); // nothing more to do } return (true); // tell the user there were no problems } // end of checkOptions() method /* compareLists() method Check each word in the first list to see if and how many times it conflicts with words in the second list. If the first and second lists are the same Java object, then identical words won't be compared against each other. */ static void compareLists( Vector firstList, // normally, these are user's input words Vector secondList) // normally, these are from known word list { int firstCount; // number of words in first list with errors String firstWord; // one word from first list int i, j; // index variables int secondCount; // number of conflicting words in second list String secondOutput; // for building up list of second words String secondWord; // one word from second list /* Loop through all words in the first list, and compare with all words in the second list, except if the two words are from the same list (so that we don't compare a word against itself). */ firstCount = 0; // no words with errors yet in first list for (i = 0; i < firstList.size(); i ++) { firstWord = (String) firstList.get(i); // get one word from first list secondCount = 0; // no conflicting words yet in second list secondOutput = ""; // start with an empty string for (j = 0; j < secondList.size(); j ++) { secondWord = (String) secondList.get(j); // get word from second list if ((firstList == secondList) && firstWord.equals(secondWord)) { /* Don't compare a word against itself. */ } else if ((checkAddFlag && canAddLetter(firstWord, secondWord)) || (checkChangeFlag && canChangeLetter(firstWord, secondWord)) || (checkDeleteFlag && canDeleteLetter(firstWord, secondWord)) || (checkReverseFlag && canReverseLetters(firstWord, secondWord))) { secondCount ++; // this second word conflicts with first word secondOutput += " " + secondWord; // save word in output list } } if (secondCount > 1) // if there are at least two conflicts { firstCount ++; // one more first word has a problem putOutput(firstWord + " has " + secondCount + " conflicts:" + secondOutput + "."); } else if (secondCount > 0) // if there is exactly one conflict { firstCount ++; // one more first word has a problem putOutput(firstWord + " has one conflict:" + secondOutput + "."); } else if (errorsOnlyFlag == false) // should we report all words? putOutput(firstWord + " has no conflicts."); } /* Print a summary of what we found. */ if (firstCount > 1) // if at least two first words had problems putOutput("Found " + firstCount + " of " + firstList.size() + " words with conflicts."); else if (firstCount > 0) // if one first word had problems if (firstList.size() > 1) // is there more than one first word? putOutput("Found one word of " + firstList.size() + " with conflicts."); else // no, there is only one first word putOutput("The only word has conflicts."); else putOutput("No conflicts detected."); } // end of compareLists() method /* getInputWords() method Take the user's input text and parse it into words. The result goes into the global variable . */ static void getInputWords() { int i; // index variable String input; // input text field from user Matcher matcher; // looks for regular expression String output; // validated text field for display String word; // one word from the input /* Initialize local and global variables. */ input = inputText.getText().toLowerCase(); // convert input to lowercase inputList = new Vector(); // create empty list for input words /* Loop for each word in the user's input text field. */ matcher = patternWord.matcher(input); // what input should look like while (matcher.find()) // for each word in user's input { word = matcher.group(); // get word that was found if (inputList.contains(word) == false) // ignore words we've seen before inputList.add(word); // only add new words to word list } /* Replace the user's input text field with a list that has been sorted and cleaned up. */ if (inputList.size() > 0) // if at least one word was found { Collections.sort(inputList); // sort into ascending string order output = (String) inputList.firstElement(); // first word for (i = 1; i < inputList.size(); i ++) output += " " + (String) inputList.get(i); // second and following } else output = ""; // no words were found inputText.setText(output); // set or reset user input field } // end of getInputWords() method /* openWordList() method Read a new word list from a text file chosen by the user. These words become a lowercase list of "known" words stored in the global variable . We ignore everything that isn't a word by our definition. The only error messages are for duplicate words. */ static void openWordList() { BufferedReader input; // input stream String inputLine; // one input line as a string String inputLower; // converted to lowercase Matcher matcher; // looks for regular expression String word; // one word from the input /* Initialize local and global variables. */ wordList = new Vector(); // create empty list for new words /* Ask the user for an input file name. */ fileChooser.setDialogTitle("Open Word List..."); if (fileChooser.showOpenDialog(mainFrame) != JFileChooser.APPROVE_OPTION) return; // user cancelled file selection dialog box /* Read lines from input file. */ try { input = new BufferedReader(new FileReader(fileChooser.getSelectedFile())); // try to open input file while ((inputLine = input.readLine()) != null) // try to read one input line { inputLower = inputLine.toLowerCase(); // convert all input to lowercase /* Break the input apart, looking only for alphanumeric words and ignoring everything else. */ matcher = patternWord.matcher(inputLower); // what input should look like while (matcher.find()) // for each word in this input line { word = matcher.group(); // get word that was found if (wordList.contains(word)) // have we seen this word before? { putOutput("Duplicate word found (" + word + "): " + inputLine); } else wordList.add(word); // add new word to word list } } input.close(); // try to close input file } catch (IOException ioe) { putOutput("Can't read word list from file: " + ioe.getMessage()); return; // give up and return to caller } /* Tell the user how many words we found. */ if (wordList.size() > 1) // if at least two words were found { Collections.sort(wordList); // sort into ascending string order putOutput("Found " + wordList.size() + " words from \"" + (String) wordList.firstElement() + "\" to \"" + (String) wordList.lastElement() + "\"."); } else if (wordList.size() > 0) // if only one word was found putOutput("Found only one word: \"" + (String) wordList.firstElement() + "\"."); else // if no words were found putOutput("Didn't find any words."); } // end of openWordList() method /* putOutput() method Append a complete line of text to the end of the output text area. We add a newline character at the end of the line, not the caller. By forcing all output to go through this same method, one complete line at a time, the generated output is cleaner and could be redirected later (if implemented). The output text area is forced to scroll to the end, after the text line is written, by selecting character positions that are much too large (and which are allowed by the definition of the JTextComponent.select() method). This is easier and faster than manipulating the scroll bars directly. */ static void putOutput(String text) { outputText.append(text + "\n"); // put caller's text into output text area outputText.select(999999999, 999999999); // force scroll to end of text } /* saveOutputText() method Ask the user for an output file name, create or replace that file, and copy the contents of our output text area to that file. */ static void saveOutputText() { FileWriter output; // output file stream /* Ask the user for an output file name. */ fileChooser.setDialogTitle("Save Output As..."); if (fileChooser.showSaveDialog(mainFrame) != JFileChooser.APPROVE_OPTION) return; // user cancelled file selection dialog box /* Write lines to output file. */ try { output = new FileWriter(fileChooser.getSelectedFile()); // try to open output file outputText.write(output); // couldn't be much easier for writing! output.close(); // try to close output file } catch (IOException ioe) { putOutput("Can't write output text to file: " + ioe.getMessage()); } } // end of saveOutputText() method /* saveWordList() method Save the current word list into a text file chosen by the user. */ static void saveWordList() { int i; // index variable BufferedWriter output; // output file stream /* Ask the user for an output file name. */ fileChooser.setDialogTitle("Save Word List..."); if (fileChooser.showSaveDialog(mainFrame) != JFileChooser.APPROVE_OPTION) return; // user cancelled file selection dialog box /* Write lines to output file. */ try { output = new BufferedWriter(new FileWriter(fileChooser.getSelectedFile())); // try to open output file for (i = 0; i < wordList.size(); i ++) // for each word in word list { output.write((String) wordList.get(i)); // write one word per line output.newLine(); // and add a newline character as necessary } output.close(); // try to close output file } catch (IOException ioe) { putOutput("Can't write word list to file: " + ioe.getMessage()); } } // end of saveWordList() method /* showWordList() method Show all words currently in the known word list. We don't do much formatting here, because the output text area is wrapped with lines broken at spaces. */ static void showWordList() { int i; // index variable String output; // for building up a single output string if (wordList.size() > 1) // if there are at least two words in list { output = "The known word list has " + wordList.size() + " words: " + wordList.firstElement(); // start with first known word for (i = 1; i < wordList.size(); i ++) // append second and following output += ", " + wordList.get(i); // ... words to the output string output += "."; // add a final period for a nice touch putOutput(output); // show the output string to the user } else if (wordList.size() > 0) // if there is only one word in list { putOutput("The known word list has only one word: " + wordList.firstElement() + "."); } else // if the list is empty { putOutput("The known word list is empty."); } } // end of showWordList() method /* testInputWords() method Test input words supplied by the user for similarity with each other and the current word list. */ static void testInputWords() { /* Check the user's input text field to see if there really is anything for us to do. */ getInputWords(); // parse and reformat input words, if any if (inputList.isEmpty()) { putOutput("Please type one or more words in the input text area."); return; // nothing more to do } else if ((inputList.size() == 1) && wordList.isEmpty()) { putOutput( "One input word can't be compared against an empty word list."); return; // nothing more to do } /* Check the user's options. */ if (checkOptions() == false) return; // do nothing if there is a problem /* Clear the output text area before we start the report. */ outputText.setText(""); // clear output text area /* First, compare the input words against themselves. */ if (inputList.size() > 1) { putOutput("Comparing your input words against themselves:"); compareLists(inputList, inputList); } /* Second, compare the input words against the list of known words. */ if (wordList.isEmpty() == false) { if (inputList.size() > 1) // only need blank line if above printed putOutput(""); putOutput("Comparing your input words against list of known words:"); compareLists(inputList, wordList); } } // end of testInputWords() method /* testWordList() method Normally, when we have a list of known words, we only check new input words against the known list. However, the user may want us to completely check the known words too. This can happen, for example, if the words are loaded from a file. */ static void testWordList() { /* There's nothing to do if we haven't loaded a list of known words from a file, or added words from the user's input. */ if (wordList.size() < 2) { putOutput("Please add at least two words to the known word list."); return; // nothing more to do } /* Check the user's options. */ if (checkOptions() == false) return; // do nothing if there is a problem /* Clear the output text area before we start the report. */ outputText.setText(""); // clear output text area /* Compare words in the known list against themselves. */ putOutput("Comparing words in the known list against themselves:"); compareLists(wordList, wordList); } // end of testWordList() method /* userButton() method This method is called by our action listener actionPerformed() to process buttons, in the context of the main SimilarWords1 class. */ static void userButton(ActionEvent event) { Object source = event.getSource(); // where the event came from if (source == addToListButton) // "Add To Word List" button { addInputToList(); // add input words to current word list } else if (source == exitButton) // "Exit" button { System.exit(0); // close this program } else if (source == inputText) // user's input text field for words { getInputWords(); // parse input words, but do nothing more } else if (source == openListButton) // "Open Word List" button { openWordList(); // read new word list from a text file } else if (source == saveListButton) // "Save Word List" button { saveWordList(); // save current word list in a text file } else if (source == saveOutputButton) // "Save Output As" button { saveOutputText(); // save generated output text in a file } else if (source == showListButton) // "Show Word List" button { showWordList(); // show word list in output text area } else if (source == testInputButton) // "Test Input Words" button { testInputWords(); // test input words for similarity } else if (source == testListButton) // "Test Word List" button { testWordList(); // test all words in word list } else { putOutput("Error in userButton(): ActionEvent not recognized: " + event); } } // end of userButton() method } // end of SimilarWords1 class // ------------------------------------------------------------------------- // /* SimilarWords1User class This class listens to input from the user and passes back event parameters to static methods in the main class. */ class SimilarWords1User implements ActionListener { /* button listener */ public void actionPerformed(ActionEvent event) { SimilarWords1.userButton(event); } } // end of SimilarWords1User class /* Copyright (c) 2006 by Keith Fenske. Released under GNU Public License. */