/* * ----------------------------------------------------------------------- * File: $HeadURL: http://keith-laptop/svn/krs/LanguageTest/trunk/org.thanlwinsoft.languagetest/src/org/thanlwinsoft/languagetest/language/test/Test.java $ * Revision $LastChangedRevision: 1388 $ * Last Modified: $LastChangedDate: 2009-01-31 19:32:00 +0700 (Sat, 31 Jan 2009) $ * Last Change by: $LastChangedBy: keith $ * ----------------------------------------------------------------------- * Copyright (C) 2003 Keith Stribley <devel@thanlwinsoft.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * ----------------------------------------------------------------------- */ package org.thanlwinsoft.languagetest.language.test; import java.util.List; import java.util.LinkedList; import java.util.Iterator; import java.util.Random; /** Class to hold all the items for a test. It holds all the properties * applied to the test. * @author keith */ public class Test { private boolean repeatTillLearnt; private List<TestItem> testList; private List<TestItem> remainingTests; private Random random; private TestItem currentTest; private int passCount = 0; private int testCount = 0; private int retestCount = 0; private int firstTimePasses = 0; private int untested = 0; private TestType type = null; private boolean audioDisabled = false; private boolean retest = false; private int maxRepeats = 100; /** Creates a new instance of Test from a list of modules. * @param modules Modules to add items from. * @param type Test Type * @param repeatTillLearnt Flag whether items that are marked wrong should be repeatedly * tested until they are correct. * @param useHistory Flag whether the history should be checked before adding items * from the list of modules. If this is false then all items in the * module for the current language configuration will be added. */ // public Test(SortedSet modules, TestType type, boolean repeatTillLearnt, // boolean useHistory) // { // this.type = type; // this.repeatTillLearnt = repeatTillLearnt; // this.testList = new LinkedList(); // TestHistory history = UserConfig.getCurrent().getTestHistory(); // Iterator i = modules.iterator(); // boolean useH = useHistory; // if (type == TestType.FLIP_CARD) useH = false; // while (i.hasNext()) // { // TestModule module = (TestModule)i.next(); // if (module.isSelected()) // { // Set moduleList = module.getTestList(); // Iterator t = moduleList.iterator(); // while (t.hasNext()) // { // TestItem item = (TestItem)t.next(); // addItemIfAppropriate(item, history, useH); // } // } // } // finishInit(); // } /** * Method to check all the different parameters that deterine whether an * item should really be included in a test. * @param item Test Item to add * @param history History Object to check for past performance * @param useHistory boolean as to whether history should be checked */ // protected void addItemIfAppropriate(TestItem item, TestHistory history, // boolean useHistory) // { // // check that the item has data for the current language // if ((item.getNativeLanguages() // .contains(LanguageConfig.getCurrent() // .getNativeLanguage())) && // (item.getForeignLanguages() // .contains(LanguageConfig.getCurrent() // .getForeignLanguage()))) // { // if (type == TestType.LISTENING_FOREIGN_NATIVE && // item.getSoundFile() == null) // { // // can't do listening test if no audio file! // } // else // { // // now check test history for the item // ItemHistory hItem = null; // if (useHistory) // { // try // { // hItem = history.getHistoryItem(item, type); // } // catch (TestHistoryStorageException thse) // { // System.out.println(thse.getMessage()); // hItem = null; // } // } // if (hItem == null || (hItem.isTestDue(type) && // !hItem.isDisabled())) // { // testList.add(item); // item.reset(); // reset pass flags // } // } // } // } /** Sets disabled flag to disable playing of audio data even if its exists. * @param disabled Boolean flag. */ public void setAudioDisabled(boolean disabled) { this.audioDisabled = disabled; } /** Tests whether audio disabled flag is set. * @return True if audio disabled. */ public boolean isAudioDisabled() { return this.audioDisabled; } /** Revision Test constructor. The list for the test is prepared by an outside * method and passed directly into the class. * @param items List of test items for test * @param type Test Type * @param repeatTillLearnt Flag whether items that are marked wrong should be repeatedly * tested until they are correct. */ public Test(LinkedList<TestItem> items, TestType type, boolean repeatTillLearnt) { this.type = type; this.repeatTillLearnt = repeatTillLearnt; this.testList =items; finishInit(); } /** helper function to do initialisation tasks common to all test types */ protected void finishInit() { this.random = new Random(); // take copy of list remainingTests = new LinkedList<TestItem>(testList); //System.out.println("Number test items: " + testList.size()); untested = testList.size(); } /** Repeat the test for all items that were not passed first time*/ public void retestUnknown() { retest = true; List<TestItem> oldList = testList; passCount = 0; testCount = 0; retestCount = 0; firstTimePasses = 0; testList = new LinkedList<TestItem>(); Iterator<TestItem> i = oldList.iterator(); while (i.hasNext()) { TestItem item = (TestItem)i.next(); if (item.isPassed() == false || item.getTestCount() > 1) { item.reset(); testList.add(item); } } remainingTests = new LinkedList<TestItem>(testList); untested = testList.size(); } /** Tests whether retest flag is set. * @return True if the test has already been run once. */ public boolean isRetest() { return retest; } /** * Randomly removes tests until the requested number of tests equals maxTests. * @param maxTests */ public void pruneTestToLimit(int maxTests) { while (remainingTests.size() > maxTests) { int number = random.nextInt(remainingTests.size()); remainingTests.remove(number); } untested = remainingTests.size(); testList = new LinkedList<TestItem>(remainingTests); } /** Gets the next item in the test. The actual item returned will be random * though it will never be the same as the previous item unless there is only * one item left. * @return The next test item to test the user with. */ public TestItem getNextItem() { if (remainingTests.size() == 0) return null; TestItem previousTest = currentTest; // prevent the same test being repeated immediately do { // nextInt seems to favour repeating tests around the same are in // the test list so use double instead - will be slower, but this // is hardly a time critical loop //int number = random.nextInt(remainingTests.size()); int number = (int)((double)remainingTests.size() * Math.random()); currentTest = (TestItem)remainingTests.get(number); // if only one test is left, have to repeat it immediately if (remainingTests.size()<2) break; } while (currentTest == previousTest); if (currentTest.getTestCount()>0) retestCount++; return currentTest; } /** Called from the GUI to set the users test result. * @param passed True if user got answer correct. */ public void setPassStatus(boolean passed) { testCount++; if (passed) { if (currentTest.getTestCount()==0) { firstTimePasses++; untested--; } currentTest.setPassed(true); remainingTests.remove(currentTest); passCount++; } else { if (currentTest.getTestCount()==0) { untested--; } currentTest.setPassed(false); if (!repeatTillLearnt || currentTest.getTestCount()>maxRepeats) { remainingTests.remove(currentTest); } } } /** Removes the current item from the test if the user has chosen to ignore it. */ public void removeCurrentItem() { // remove item from both test lists remainingTests.remove(currentTest); testList.remove(currentTest); } /** Getter for test type. * @return The type of test this is. */ public TestType getType() { return type; } /** Total number of passes. * @return Number of test items passed. */ public int getPassCount() { return passCount; } /** Getter for total number of tests including retests. * @return Number of tests performed by calling getNextItem */ public int getTestCount() { return testCount; } /** Number of items in this test. * @return Number of items in this test. */ public int getNumTests() { return testList.size(); } /** Number of retests performed. * @return Number of retests performed. */ public int getNumRetests() { return retestCount; } /** Number of retests performed. * @return Number of retests performed. */ public int getUntested() { return untested; } /** Number of tests passed first time. * @return Number of tests passed first time. */ public int getNumFirstTimePasses() { return firstTimePasses; } /** Gives access to the the Test Item currently being shown to user. * @return Current test item that was returned by last call to getNextItem. */ public TestItem getCurrentItem() { return currentTest; } /** Provides an iterator over all the items in the test. This is designed for * setting flags on items, not for performing the test itself. * @return Iterator for internal linked list. */ public Iterator<TestItem> getItemIterator() { return testList.iterator(); } /** * Set max number of redisplays in flip card mode */ public void setMaxRepeats(int max) { maxRepeats = max; } }