This time, I will try to make it clear. I have run it for 2 days. No problem. But there are still lots of TODO. The version now is 0.1
  I will try to give it a name and write a detail readme about how to use it. Of course need finish the TODO in the program.
  /*  * Config.java  * Created on Sep 29, 2004  * TODO Write the log to file.  */ package com.henry.vocabulary; 
import java.io.*; import java.util.logging.*; import java.util.*; 
/**  * @author henry.pan  * TODO Add unit test for this class  */ public class Config {          private static Logger logger = Logger.getLogger("Config");          private static String configFile = "config.ini";          /* here is the config and the default value */     private static String wordsFileName = "words.txt";     private static int sleepInterval    = 6000 * 2;          /**      * @return The config of the file storing the words.      */     public static String getWordsFileName() {         return wordsFileName;     }          public static int getSleepInterval() {         return sleepInterval;     }          /**      * Read configuration from config file.      * Config file is defined.      */     public static void getTheConfiguration() {         File file = new File(configFile);         if (!file.exists()) {             logger.severe("The config file does not exist!" + Calendar.getInstance().getTime());         }         try {             StringBuffer sb = new StringBuffer();             BufferedReader br = new BufferedReader(new FileReader(configFile));             String s;             String[] configValues;             while ((s = br.readLine()) != null) {                 if (s.equals("")) {                     continue;                 }                 if (s.substring(0,0).equals("#")) {                     continue;                 }                 configValues = s.split("=");                 parseConfigValue(configValues);             }         } catch (IOException ioe) {             logger.severe("Error when reading confi file!");             logger.severe(ioe.getStackTrace().toString());             logger.severe(ioe.toString());         }     }          /**      * @param configValues The config paire values.       *            configValues[0] is the config name      *            configValues[1] is the config value      */     private static void parseConfigValue(String[] configValues) {         assert configValues.length > 1 : "The config format is incorrect!";                  try {             if (configValues[0].trim().toLowerCase() == "WordsFile") {                 wordsFileName = configValues[1].trim();             }                          if (configValues[0].trim().toLowerCase() == "SleepInterval") {                 sleepInterval = Integer.parseInt( configValues[1].trim());             }             } catch (Exception e) {             logger.severe("The configuration's format is incorrect!");             logger.severe(e.getStackTrace().toString());             logger.severe(e.toString());         }              } 
}
 
 
  /*  * MainFrame.java  * Created on Sep 28, 2004  *  */ package com.henry.vocabulary; 
import java.awt.*; import java.awt.event.*; import java.util.Calendar; import java.util.logging.*; 
import javax.swing.*; 
/*******************************************************************************  * Title:  *   *   * Description:  *   *   * Copyright: Copyright (c) 2004  *   *   * Company:  *   * @author Henry *  * @version 1.0  * TODO Add unit test for this class  */ public class MainFrame extends JFrame { 
    Logger logger = Logger.getLogger("MainWin");          /* Used to manipulate the words */     WordsReview wr = new WordsReview();          JPanel contentPane;     GridBagLayout gridBagLayout1 = new GridBagLayout();     JTextField word = new JTextField();     JRadioButton remember = new JRadioButton();     JRadioButton forget = new JRadioButton();     JButton next = new JButton();     ButtonGroup buttonGroup1 = new ButtonGroup(); /* Construct the frame */ 
    public MainFrame() {         enableEvents(AWTEvent.WINDOW_EVENT_MASK);         try {             jbInit();             wordsInit();         } catch (Exception e) {             e.printStackTrace();         }     } 
    /* Component initialization */     private void jbInit() throws Exception {         contentPane = (JPanel) this.getContentPane();         word.setFont(new java.awt.Font("Dialog", 0, 16));         word.setSelectionStart(0);         word.setText("");         contentPane.setLayout(gridBagLayout1);         this.setSize(new Dimension(600, 300));         this.setTitle("TAFU Vocabulary");         remember.setFont(new java.awt.Font("Dialog", 0, 16));         remember.setText("I remember it well.");         forget.setFont(new java.awt.Font("Dialog", 0, 16));         remember.setSelected(true);         forget.setText("I forget it.");         next.setFont(new java.awt.Font("Dialog", 0, 16));         next.setText("Next");         next.addActionListener(new Frame1_next_actionAdapter(this));         contentPane.add(word, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,                 GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(                         17, 9, 0, 31), 494, 39));         contentPane.add(remember, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,                 GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,                 new Insets(26, 0, 1, 0), 0, 0));         contentPane.add(forget, new GridBagConstraints(1, 1, 1, 2, 0.0, 0.0,                 GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,                 new Insets(25, 0, 37, 0), 0, 0));         contentPane.add(next, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0,                 GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(                         0, 9, 59, 13), 44, 11));         buttonGroup1.add(forget);         buttonGroup1.add(remember);     } 
    /* Overridden so we can exit when window is closed */     protected void processWindowEvent(WindowEvent e) {         super.processWindowEvent(e);         if (e.getID() == WindowEvent.WINDOW_CLOSING) {             logger.info("Close the window!");             try {                 wr.saveToFile();             } catch (Exception ex) {                 System.out.println(ex.getStackTrace());                 System.out.println(ex);             }             System.exit(0);         }     } 
    private void wordsInit() {         try {             wr.readFromFile();             getNextWord();         } catch (Exception e) {             System.out.println(e.getStackTrace());             System.out.println(e);         }     } 
    private void getNextWord() {         String s = this.wr.getWord();         if (s == null) {             this.setVisible(false);             this.wr.saveToFile();             while (s == null) {                 logger.info("Get a null word. No word needs review now. Goto Sleep." + Calendar.getInstance().getTime());                 try {                     Thread.sleep(Config.getSleepInterval());                     logger.info(" -- After sleep --" + Calendar.getInstance().getTime());                     this.wr.readFromFile();                     s = this.wr.getWord();                 } catch (InterruptedException ie) {                     System.out.println(ie.getStackTrace());                     System.out.println(ie);                 }             }             this.setVisible(true);         }         this.word.setText(s);         this.remember.setSelected(true);     } 
    void next_actionPerformed(ActionEvent e) {         if (this.forget.isSelected()) {             wr.saveWord(false);         } else {             wr.saveWord(true);         }         getNextWord();     } 
} 
class Frame1_next_actionAdapter implements java.awt.event.ActionListener {     MainFrame adaptee; 
    Frame1_next_actionAdapter(MainFrame adaptee) {         this.adaptee = adaptee;     } 
    public void actionPerformed(ActionEvent e) {         adaptee.next_actionPerformed(e);     } }
 
  /*  * MainWin.java  * Created on Sep 28, 2004  */ package com.henry.vocabulary; 
import javax.swing.UIManager; import java.awt.*; 
 /*******************************************************************************  * Title:  *   *   * Description:  *   *   * Copyright: Copyright (c) 2004  *   *   * Company:  *   * @author not attributable *  * @version 1.0  * TODO Make sure there is only one instance running.  */ public class MainWin {     boolean packFrame = false;           
    /* Construct the application */     public MainWin() {         MainFrame frame = new MainFrame(); 
        /*          * Validate frames that have preset sizes         * Pack frames that have useful preferred size info, e.g. from their         * layout         */         if (packFrame) {             frame.pack();         } else {             frame.validate();         }         /* Center the window */         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();         Dimension frameSize = frame.getSize();         if (frameSize.height > screenSize.height) {             frameSize.height = screenSize.height;         }         if (frameSize.width > screenSize.width) {             frameSize.width = screenSize.width;         }         frame.setLocation((screenSize.width - frameSize.width) / 2,                 (screenSize.height - frameSize.height) / 2);         frame.setVisible(true);     }          
     /* Main method */     public static void main(String[] args) {                  boolean assertionsEnabled = false;                  /* Note intentioal side effect of assignment. */         assert assertionsEnabled = true;         if (!assertionsEnabled) {             throw new RuntimeException("Assertions disabled");         }                  try {             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());         } catch (Exception e) {             e.printStackTrace();         }         new MainWin();         System.out.println("Exit the MainWin.main().");     } }
 
 
  /*******************************************************************************  * Word.java  *   * Created on 2004-9-26  */ package com.henry.vocabulary; 
import java.util.*; import java.text.*; 
public class Word {     private static final SimpleDateFormat format = new SimpleDateFormat(             "yyyy-MM-dd HH:mm:ss"); 
    private static final int[] memoryCurveMinute = {10, 30, 60, 180, 300, 500};     private static final int[] memoryCurveDay = { 1, 2, 4, 8, 12, 20, 36, 54, 87,             160, 360 ,680}; 
    private String _wordValue;     private Date   _nextReviewTime;     private int    _reviewTimes;     private int    _forgetTimes;              /**      * *      *       * @param _wordValue      *            The _wordValue to set.      */     public void setWordValue(String _wordValue) {         this._wordValue = _wordValue;     } 
    /**      * *      *       * @return Returns the _wordValue.      */     public String getWordValue() {         return _wordValue;     } 
    public Word(String wordValue, String nextReviewTime, int reviewTimes,             int forgetTimes) {         try {             setWordValue(wordValue);             _nextReviewTime = format.parse(nextReviewTime);             _reviewTimes = reviewTimes;             _forgetTimes = forgetTimes;         } catch (ParseException pe) {             System.out.println("The input is not a date!");             throw new RuntimeException(pe);         }     } 
    public Word(String strWord) {         try {             String[] values = strWord.split(",");             setWordValue(values[0]);             if(values.length > 2) {                 _nextReviewTime = format.parse(values[1]);                 _reviewTimes = Integer.parseInt(values[2].trim());             } else {                 Calendar yesterday = Calendar.getInstance();                 yesterday.add(Calendar.DATE, -1);                 _nextReviewTime =  yesterday.getTime();                 _reviewTimes = 0;                             }             if (values.length > 3) {                 /* For compatible to the old version */                 _forgetTimes = Integer.parseInt(values[3]);             } else {                 _forgetTimes = 0;             }         } catch (ParseException pe) {             System.out.println("The input is not a date!");             throw new RuntimeException(pe);         }     } 
    public void forget() {         Calendar nextTime = Calendar.getInstance();         nextTime.add(Calendar.MINUTE, -1);         _nextReviewTime = nextTime.getTime();         _reviewTimes = 0;         _forgetTimes++;     } 
    public void remember() {         Calendar nextTime = Calendar.getInstance();         if (this._reviewTimes < memoryCurveMinute.length) {             nextTime.add(Calendar.MINUTE, memoryCurveMinute[_reviewTimes]);         } else if (_reviewTimes - memoryCurveMinute.length < memoryCurveDay.length) {             nextTime.add(Calendar.DATE, memoryCurveDay[_reviewTimes - memoryCurveMinute.length]);         } else {             nextTime.add(Calendar.YEAR, 100); /* Never need review this word */         }                          if(nextTime.get(Calendar.DATE) != Calendar.getInstance().get(Calendar.DATE)) {             nextTime.set(Calendar.HOUR, 0);             nextTime.set(Calendar.MINUTE, 0);             nextTime.set(Calendar.SECOND, 0);         }         _nextReviewTime = nextTime.getTime();                 _reviewTimes++;     } 
    public String toString() {         String s = getWordValue() + "," + format.format(_nextReviewTime) + ","                 + _reviewTimes + "," + _forgetTimes;         return s;     }           
    /**      * Judge if this word need to be reviewed now. * Comment for isNeedReview      */     public boolean isNeedReview() {         if (this._nextReviewTime.after(Calendar.getInstance().getTime())) {             return false;         }         return true;     }          public boolean equals (Object o) {         if (this._wordValue.equalsIgnoreCase(((Word)o).getWordValue())) {             return true;         }         return false;     }          public boolean isNeedNotReviewAnyMore() {         Calendar later50Years = Calendar.getInstance();         later50Years.add(Calendar.YEAR, 50);         if (this._nextReviewTime.after(later50Years.getTime())) {             return true;         }         return false;     }   } 
 
  /*   * WordsReview.java   *   * Created on 2004-9-26   *   *   */ 
package com.henry.vocabulary; 
import java.util.*; import java.util.logging.*; import java.io.*; 
/**  * TODO Add unit test for this class  *   * @author Henry *  */ public class WordsReview { 
    static Logger logger = Logger.getLogger("WordsReview"); 
    List wordList = null; 
    List wordNeedReviewList = null; 
    Word currentWord = null; 
    Map wordMap = null; 
    /**      * *      *       * @param fileName      */     private void readFromFile(String fileName) {         assert (this.wordList != null) : "Doesnt init the wordList when readFromFile.";         createWordsFileIfItNotExist();         StringBuffer sb = null;         BufferedReader br = null;         try {             sb = new StringBuffer();             br = new BufferedReader(new FileReader(fileName));             String s;             while ((s = br.readLine()) != null) {                 this.currentWord = new Word(s);                 if (!this.wordMap.containsKey(this.currentWord.getWordValue())) {                     this.wordMap.put(this.currentWord.getWordValue(),                             this.currentWord);                     this.wordList.add(this.currentWord);                     if (this.currentWord.isNeedReview()) {                         this.wordNeedReviewList.add(this.currentWord);                     }                 } else {                     this.currentWord = (Word) this.wordMap.get(this.currentWord                             .getWordValue());                     this.currentWord.forget();                 }             }             this.currentWord = null;             br.close();         } catch (FileNotFoundException e) {             System.out.println(e);             System.out.println(e.getStackTrace());         } catch (IOException ioe) {             System.out.println(ioe);             System.out.println(ioe.getStackTrace());         } finally {             /* br.close(); */         }     } 
    /*      * TODO After create the words file. Add the advertise words in the file.      * Such as http://blog.csdn.net/tafu      *      * TODO Put this function out of here.        */     private void createWordsFileIfItNotExist() {         File f = new File(Config.getWordsFileName());         if (!f.exists()) {             try {                 f.createNewFile();             } catch (IOException ioe) {                 logger.severe("Cannot create file to save the words!"                         + Calendar.getInstance().getTime());                 logger.severe(ioe.getStackTrace().toString());                 logger.severe(ioe.toString());             }         }     } 
    /**      * *      *       * @param fileName      */     private void saveToFile(String fileName) {         try {             PrintWriter printWriter = new PrintWriter(new BufferedWriter(                     new FileWriter(fileName)));             Iterator iterator = this.wordList.iterator();             while (iterator.hasNext()) {                 printWriter.println((Word) iterator.next());             }             printWriter.close();         } catch (IOException ioe) {             System.out.println(ioe.getStackTrace());             System.out.println(ioe);         }     } 
    public void saveToFile() {         this.saveToFile(Config.getWordsFileName());     } 
    /**      * * Get a word, which needs to be reviewed. *      *       * @return      */     public String getWord() {         if (this.wordNeedReviewList.isEmpty()) {             return null;         }         Random random = new Random();         int ramdonIndex = random.nextInt() % this.wordNeedReviewList.size();         if (ramdonIndex < 0) {             ramdonIndex = -ramdonIndex;         }         currentWord = (Word) this.wordNeedReviewList.get(ramdonIndex);         return currentWord.getWordValue();     } 
    public void saveWord(boolean isRemember) {         if (this.currentWord == null) {             System.out.println("Should not come here!");             System.out.println("When you call saveWord to save the word, ");             System.out.println(" you should get the word first.");             return;         }         if (!isRemember) {             this.currentWord.forget();         } else {             this.currentWord.remember();         }         this.wordNeedReviewList.remove(this.currentWord);         if (this.currentWord.isNeedNotReviewAnyMore()) {             this.wordList.remove(this.currentWord);             this.wordMap.remove(this.currentWord.getWordValue());         }         this.currentWord = null;     } 
    public void regenerateNeedReviewList() {         this.wordNeedReviewList = new ArrayList();         for (int i = 0; i < this.wordList.size(); i++) {             if (((Word) this.wordList.get(i)).isNeedReview()) {                 this.wordNeedReviewList.add((Word) this.wordList.get(i));             }         }         this.currentWord = null;     } 
    public void readFromFile() {         this.wordList = new ArrayList();         this.wordNeedReviewList = new LinkedList();         this.wordMap = new HashMap();         this.readFromFile(Config.getWordsFileName());     } 
}
 
  /*  * WordTest.java  * Created on Sep 30, 2004  *  */ package com.henry.vocabulary.junit; 
import junit.framework.TestCase; import com.henry.vocabulary.*; import java.util.*; import java.text.*; 
/**  * @author henry.pan  *  */ public class WordTest extends TestCase { 
    /*      * @see TestCase#setUp()      */     protected void setUp() throws Exception {         super.setUp();     } 
    /*      * @see TestCase#tearDown()      */     protected void tearDown() throws Exception {         super.tearDown();     } 
    public void testSetWordValue() {         Word w = new Word("testWord");         w.setWordValue("newValue");         TestCase.assertEquals("newValue",w.getWordValue());         w = null;             } 
    public void testGetWordValue() {         Word w = new Word("testWord");         TestCase.assertEquals("testWord",w.getWordValue());         w = null;     } 
    public void testForget() {         Word w = new Word("testWord");                  Calendar calendar = Calendar.getInstance();         calendar.add(Calendar.MINUTE, -1);         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:");         String s = df.format(calendar.getTime());                  w.forget();         /* System.out.println(w); */         /* System.out.println("testWord," + s + "\\d\\d,0,\\d+"); */                 TestCase.assertTrue(w.toString().matches("testWord," + s + "\\d\\d,0,\\d+"));         w.forget();         w.remember();         w.remember();         w.forget();         TestCase.assertTrue(w.toString().matches("testWord," + s + "\\d\\d,0,\\d+"));         w = null;     } 
    public void testRemember() {     } 
    public void testIsNeedReview() {         Calendar calendar = Calendar.getInstance();         calendar.add(Calendar.MINUTE, -1);         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");         String s = df.format(calendar.getTime());                 Word w = new Word("testWord," + s + ",3,2");         TestCase.assertTrue(w.isNeedReview());         w = null;     } 
    /*      * Class under test for boolean equals(Object)      */     public void testEqualsObject() {         Word w1 = new Word("testWord");         Word w2 = new Word("testWord");         w1.forget();         w2.remember();         TestCase.assertTrue(w1.equals(w2));     } 
    public void testIsNeedNotReviewAnyMore() {         Calendar calendar = Calendar.getInstance();         calendar.add(Calendar.YEAR, 100);         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");         String s = df.format(calendar.getTime());                 Word w = new Word("testWord," + s + "3,2");         TestCase.assertTrue(w.isNeedNotReviewAnyMore());         w = null;             } 
}
  //========================================== And the test data in words.txt file. distract,2004-10-01 00:00:00,7,0 marvel,2004-10-01 00:00:00,7,0 vaunt,2004-10-01 00:00:00,7,3 syllabus,2004-10-01 00:00:00,7,0 typo,2004-10-01 00:00:00,7,0 parallel,2004-10-01 00:00:00,7,0 paradigm,2004-10-01 00:00:00,7,0 mediocre,2004-10-01 00:00:00,7,2 stuck,2004-10-01 00:00:00,7,0 auxiliary,2004-10-01 00:00:00,7,1 somehow,2004-10-01 00:00:00,7,1 even number,2004-10-01 00:00:00,7,0 lore,2004-10-01 00:00:00,7,1 compel,2004-10-01 00:00:00,7,1 inclination,2004-10-01 00:00:00,7,1 garble,2004-10-01 00:00:00,7,1 innate,2004-10-01 00:00:00,7,0 imperative,2004-10-01 00:00:00,7,0 even up,2004-10-01 00:00:00,7,1 preemptive,2004-10-01 00:00:00,7,0 fathom,2004-10-01 00:00:00,7,0 moon cake,2004-10-01 00:00:00,7,0 chug,2004-10-01 00:00:00,7,0 unnecessary,2004-10-01 00:00:00,7,0 state,2004-10-01 00:00:00,7,0 largely,2004-10-01 00:00:00,7,0 elaborate,2004-10-01 00:00:00,7,0 gliph,2004-10-01 00:00:00,7,0 pug,2004-10-01 00:00:00,7,0 recipe,2004-10-01 00:00:00,7,0 adjective,2004-10-01 00:00:00,7,0 spam,2004-10-01 00:00:00,7,0 loaf,2004-10-01 00:00:00,7,0 circus,2004-10-01 00:00:00,7,0 incessantly,2004-10-01 00:00:00,7,1 cannibalize,2004-10-01 00:00:00,7,0 eligible,2004-10-01 00:00:00,7,0 parenthetic,2004-10-01 00:00:00,7,1 cue,2004-10-01 00:00:00,7,0 rejuvenate,2004-10-01 12:00:00,7,0 back away,2004-10-01 12:00:00,7,0 ballistic,2004-10-01 12:00:00,7,0 interrogate,2004-10-01 12:00:00,7,0 introspect,2004-10-01 12:00:00,7,0 plop,2004-10-01 12:00:00,7,0 self-aware,2004-10-01 12:00:00,7,0 deduction,2004-09-30 21:48:56,6,0 reimbursement,2004-09-30 21:51:09,6,0 relic,2004-09-30 21:51:13,6,0 unilateral,2004-09-30 21:53:23,6,0 loiter,2004-09-30 21:53:18,6,0 symptom,2004-09-30 18:39:04,5,0 conspire,2004-09-30 21:38:59,5,0 incubation,2004-09-30 21:41:03,5,0 ridiculous,2004-09-30 17:43:11,4,0 overextended,2004-09-30 20:13:37,4,0 summit,2004-09-30 20:15:39,4,1 endorse,2004-09-30 20:15:56,4,1
   
 
  |