001 /* PAVLOV -- Multiple Choice Study System 002 * Copyright (C) 2000 - 2004 T.J. Willis 003 * 004 * This program is free software; you can redistribute it and/or 005 * modify it under the terms of the GNU General Public License 006 * as published by the Free Software Foundation; either version 2 007 * of the License, or (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program; if not, write to the Free Software 016 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. 017 * 018 * $Header: /cvsroot/pavlov/net/sourceforge/pavlov/user/ChapterData.java,v 1.9 2004/07/01 09:03:44 tj_willis Exp $ 019 */ 020 package net.sourceforge.pavlov.user; 021 022 import java.util.*; 023 024 import net.sourceforge.pavlov.library.AbstractChapter; 025 import net.sourceforge.pavlov.library.Question; 026 import org.apache.log4j.*; 027 028 029 /** 030 * Encapsulates a users responses to all questions in a chapter. 031 * 032 * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a> 033 * @version $Revision: 1.9 $ 034 * @see net.sourceforge.pavlov.library.Chapter 035 * @see BookData 036 * @see QuestionData 037 */ 038 public final class ChapterData { 039 // Unit coverage in TestQuiz.java 040 /** Title of the corresponding Chapter */ 041 private String title; 042 /** Holds QuestionData. */ 043 private Hashtable<String,QuestionData> questions; 044 /** Strategy to use in choosing next question. */ 045 private AbstractStrategy strategy = null; 046 /** Detailed statistics about responses to this chapter. */ 047 private ChapterStatistics chapterStatistics; 048 /** Results of past quizzes. */ 049 private QuizCollection quizCollection; 050 /** QuestionData's we don't want to ask*/ 051 protected Vector<QuestionData> exclusion; 052 /** QuestionData's we don't want to ask*/ 053 protected int exclusionSize = 5; 054 055 /** 056 * Creates a ChapterData with the given title 057 * @param _title a <code>String</code> value 058 */ 059 public ChapterData(String title) { 060 this.title = title; 061 init(); 062 } 063 064 private void init() 065 { 066 questions = new Hashtable<String,QuestionData>(150); 067 quizCollection = new QuizCollection(); 068 exclusion = new Vector<QuestionData>(); 069 } 070 071 072 // FIXME: title should be "name" to agree with library.Chapter 073 074 /** Creates a ChapterData with title "Untitled" */ 075 public ChapterData() { 076 this("Untitled"); 077 } 078 079 /** 080 * Sets the title of the Chapter this ChapterData refers to. 081 * 082 * @param t a <code>String</code> value 083 */ 084 public void setTitle(String title) { 085 this.title = title; 086 } 087 088 089 /** 090 * Returns the title of the Chapter this ChapterData refers to. 091 * 092 * @return a <code>String</code> value 093 */ 094 public String getTitle() { 095 return title; 096 } 097 098 /** 099 * Returns the QuestionData for the question with the given ID 100 * or null if no such question exists. 101 * 102 * @param qid a <code>String</code> value 103 * @return a <code>QuestionData</code> value 104 */ 105 public QuestionData getQuestionData(final String qid) { 106 return questions.get(qid); 107 } 108 109 /** 110 * Adds a correct response to the named QuestionData at the 111 * current time. 112 * 113 * @param id a <code>String</code> value 114 */ 115 public void addRight(final String id) { 116 Date now = new Date(); 117 addRight(id, now); 118 } 119 120 /** 121 * Adds an incorrect response to the named QuestionData at the 122 * current time. 123 * 124 * @param id a <code>String</code> value 125 */ 126 public void addWrong(final String id) { 127 Date now = new Date(); 128 addWrong(id, now); 129 } 130 131 /** 132 * Returns the number of questions in this chapter. 133 * 134 * @return an <code>int</code> value 135 */ 136 public int getNumberOfQuestions() { 137 if (questions == null) return 0; 138 return questions.size(); 139 } 140 141 /** 142 * Adds a correct response to the named QuestionData at the 143 * given time. 144 * 145 * @param id a <code>String</code> value 146 * @param whe a <code>long</code> value 147 */ 148 public void addRight(final String id, final long when) { 149 Date now = new Date(when); 150 addRight(id, now); 151 } 152 153 /** 154 * Adds an incorrect response to the named QuestionData at the 155 * given time. 156 * 157 * @param id a <code>String</code> value 158 * @param whe a <code>long</code> value 159 */ 160 public void addWrong(final String id, final long whe) { 161 Date now = new Date(whe); 162 addWrong(id, now); 163 } 164 165 /** 166 * Adds a correct response to the named QuestionData at the 167 * given time. 168 * 169 * @param id a <code>String</code> value 170 * @param when a <code>Date</code> value 171 */ 172 public void addRight(final String id, final Date when) { 173 QuestionData q = (QuestionData) questions.get(id); 174 if (q == null) { 175 q = new QuestionData(id); 176 questions.put(id, q); 177 } 178 q.addRight(when); 179 } 180 181 /** 182 * Adds an incorrect response to the named QuestionData at the 183 * given time. 184 * 185 * @param id a <code>String</code> value 186 * @param when a <code>Date</code> value 187 */ 188 public void addWrong(final String id, final Date when) { 189 QuestionData q = (QuestionData) questions.get(id); 190 if (q == null) { 191 q = new QuestionData(id); 192 questions.put(id, q); 193 } 194 q.addWrong(when); 195 } 196 197 /** 198 * Loop through all the questions in the chapter, ensuring a 199 * QuestionData exists for each. If not, create a new QuestionData. 200 * 201 * @param cp an <code>AbstractChapter</code> value 202 */ 203 public void validate(AbstractChapter cp) { 204 // foreach question in chapter 205 // get QuestionData for question 206 // if NEXIST, make a new one 207 Collection<Question> v = cp.getQuestionsReadOnly(); 208 209 for(Question left: v) { 210 QuestionData da = (QuestionData) questions.get(left.getID()); 211 212 if (da == null) { 213 QuestionData x = new QuestionData(left.getID()); 214 215 questions.put(x.getID(), x); 216 } 217 // DON'T NEED x.validate(left) -- these are leaves 218 } 219 } 220 221 /** 222 * Returns the size of the exclusion vector. 223 * 224 * @return an <code>int</code> value 225 */ 226 public int getExclusionSize() 227 { 228 return exclusionSize; 229 } 230 231 /** 232 * Sets the size of the exclusion vector. 233 * 234 * @param i an <code>int</code> value 235 */ 236 public void setExclusionSize(final int newSize) 237 { 238 int x = newSize; 239 if(x<0){ 240 x = 0; 241 } 242 exclusionSize = x; 243 } 244 245 /** 246 * Returns a QuestionData using the current strategy. If no strategy 247 * is current, create a BasicStrategy and use it. 248 * 249 * @return a <code>QuestionData</code> value 250 */ 251 public QuestionData getQuestion() { 252 //assert strategy != null : "Null strategy in getQuestion"; 253 if(strategy==null){ 254 strategy = new BasicStrategy(questions.values()); 255 } 256 QuestionData da = strategy.getQuestion(exclusion); 257 //FIXME: add this: 258 //while exclusion.size()>= # avail questions exclusion.remove(0); 259 if(exclusion.size()>exclusionSize){ 260 exclusion.remove(0); 261 } 262 exclusion.add(da); 263 return da; 264 } 265 266 /** 267 * Returns the current question selection strategy. 268 * 269 * @return an <code>AbstractStrategy</code> value 270 */ 271 public final AbstractStrategy getStrategy() 272 { 273 return strategy; 274 } 275 276 277 /** 278 * Sets the question selection strategy and initializes it with 279 * the QuestionData for this chapter. 280 * 281 * @param strat an <code>AbstractStrategy</code> value 282 */ 283 public final void setStrategy(AbstractStrategy strat) { 284 if(strat==null) return; 285 strat.setQuestions(questions.values()); 286 strategy = strat; 287 } 288 289 /** 290 * Returns the <code>ChapterStatistics</code> for 291 * this ChapterData. 292 * 293 * @return a <code>ChapterStatistics</code> value 294 */ 295 public ChapterStatistics getChapterStatistics() { 296 if (chapterStatistics != null){ 297 return chapterStatistics; 298 } 299 Collection<QuestionData> v = questions.values(); 300 chapterStatistics = new ChapterStatistics(); 301 for(QuestionData q: v){ 302 chapterStatistics.addQuestionData(q); 303 } 304 305 return chapterStatistics; 306 } 307 308 /** 309 * Removes the QuestionData corresponding to the given ID. 310 * 311 * @param key a <code>String</code> value 312 */ 313 public void deleteQuestionID(final String key) { 314 questions.remove(key); 315 } 316 317 /** 318 * Converts this ChapterData to XML. 319 * 320 * @param writer a <code>java.io.Writer</code> value 321 * @exception java.io.IOException if an error occurs 322 */ 323 public void toXML(java.io.Writer writer) 324 throws java.io.IOException { 325 Collection<QuestionData> v = questions.values(); 326 327 writer.write("\t\t<CHAPTER TITLE=\"" + title + "\">\n"); // LAST=\"19/Feb/02\">\n"); 328 329 //x += "This chapter has " + v.size() + " questions w/ history\n"; 330 for( QuestionData q : v ) { 331 q.toXML(writer); 332 } 333 334 quizCollection.toXML(writer); 335 336 writer.write("\t\t</CHAPTER>\n"); 337 338 } 339 340 /** 341 * Adds the given QuizData object to this ChapterData's QuizCollection. 342 * 343 * @param quizData a <code>QuizData</code> value 344 */ 345 public void addQuizData(final QuizData quizData) { 346 quizCollection.addQuiz(quizData); 347 } 348 349 /** 350 * Describe <code>getStatistics</code> method here. 351 * 352 * @deprecated Misleading name. Use getQuizCollectionData() 353 * @return a <code>QuizCollectionData</code> value 354 */ 355 @Deprecated public QuizCollectionData getStatistics() { 356 return getQuizCollectionData(); 357 } 358 359 360 /** 361 * Returns data for all quizzes the user has taken on this chapter. 362 * 363 * @return a <code>QuizCollectionData</code> value 364 */ 365 public QuizCollectionData getQuizCollectionData() { 366 return quizCollection.getStatistics(); 367 } 368 369 /** 370 * Returns a vector of percentages for all the quizzes the user 371 * has taken on this chapter. 372 * 373 * @return a <code>Vector</code> value 374 */ 375 public Vector getQuizPercentages() { 376 return quizCollection.getPercentages(); 377 } 378 379 /** 380 * Describe <code>getQuizCollection</code> method here. 381 * 382 * @return a <code>QuizCollection</code> value 383 */ 384 public QuizCollection getQuizCollection() 385 { 386 return quizCollection; 387 } 388 389 390 // FIXME: make an unmodifiable list version of this method 391 /** 392 * Describe <code>getAllQuestionData</code> method here. 393 * 394 * @return a <code>Collection</code> value 395 */ 396 public Collection getAllQuestionData() 397 { 398 return questions.values(); 399 } 400 } 401 402 403 404 405 406 407 408