001 /* 002 * PAVLOV -- Multiple Choice Study System 003 * Copyright (C) 2000 - 2004 T.J. Willis 004 * 005 * This program is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU General Public License 007 * as published by the Free Software Foundation; either version 2 008 * of the License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. 018 * 019 * $Header: /cvsroot/pavlov/net/sourceforge/pavlov/controllers/QuizController.java,v 1.7 2004/06/27 08:16:25 tj_willis Exp $ 020 */ 021 package net.sourceforge.pavlov.controllers; 022 023 import java.awt.*; 024 import java.io.*; 025 import java.net.URL; 026 import java.net.URLDecoder; 027 import java.net.URLEncoder; 028 import java.util.*; 029 import javax.swing.*; 030 import javax.swing.border.*; 031 import javax.swing.event.*; 032 import java.lang.StringBuffer; 033 034 import net.sourceforge.pavlov.event.*; 035 import net.sourceforge.pavlov.library.*; 036 import net.sourceforge.pavlov.main.Quiz; 037 import net.sourceforge.pavlov.main.standalone.ResourceBroker; 038 import net.sourceforge.pavlov.user.*; 039 import net.sourceforge.sillyview.*; 040 import org.apache.log4j.*; 041 042 /** 043 * <code>QuizController</code> is a controller that presents the user with 044 * questions and gives various sorts of feedback. 045 * 046 *@author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a> 047 *@created Feb 10, 2004 048 *@since 1.1 049 *@version $Revision: 1.7 $ 050 */ 051 public final class QuizController extends Widget 052 implements QuestionChangedListener 053 { 054 055 /** 056 * Token used to communicate with velocity model. 057 */ 058 public final static String ANSWER_EVENT = "__ANSWER_EVENT"; 059 /** 060 * Token used to communicate with velocity model. 061 */ 062 public final static String ANSWER_MODE = "__ANSWER_MODE"; 063 /** 064 * Token used to communicate with velocity model. 065 */ 066 public final static String RIGHT = "__RIGHT"; 067 /** 068 * Token used to communicate with velocity model. 069 */ 070 public final static String WRONG = "__WRONG"; 071 072 /** 073 * Token used to communicate with velocity model. 074 */ 075 public final static String BOOK_NAME = "BOOK_NAME"; 076 /** 077 * Token used to communicate with velocity model. 078 */ 079 public final static String CHAPTER_NAME = "CHAPTER_NAME"; 080 081 /** 082 * Token used to communicate with velocity model. 083 */ 084 public final static String QUESTION_TEXT = "QUESTION_TEXT"; 085 /** 086 * Token used to communicate with velocity model. 087 */ 088 public final static String QUESTION_IMAGE = "QUESTION_IMAGE"; 089 /** 090 * Token used to communicate with velocity model. 091 */ 092 public final static String ANSWER_A = "A_ANSWER"; 093 /** 094 * Token used to communicate with velocity model. 095 */ 096 public final static String ANSWER_B = "B_ANSWER"; 097 /** 098 * Token used to communicate with velocity model. 099 */ 100 public final static String ANSWER_C = "C_ANSWER"; 101 /** 102 * Token used to communicate with velocity model. 103 */ 104 public final static String ANSWER_D = "D_ANSWER"; 105 /** 106 * Token used to communicate with velocity model. 107 */ 108 public final static String HREF_A = "A_BUTTON"; 109 /** 110 * Token used to communicate with velocity model. 111 */ 112 public final static String HREF_B = "B_BUTTON"; 113 /** 114 * Token used to communicate with velocity model. 115 */ 116 public final static String HREF_C = "C_BUTTON"; 117 /** 118 * Token used to communicate with velocity model. 119 */ 120 public final static String HREF_D = "D_BUTTON"; 121 /** 122 * Token used to communicate with velocity model. 123 */ 124 public final static String URL_PREFIX = "URL_PREFIX"; 125 126 private ResourceBroker rb; 127 /** 128 * Description of the Field 129 */ 130 public final static int LOCAL_FILES = 0; 131 /** 132 * Description of the Field 133 */ 134 public final static int REMOTE_FILES = 1; 135 136 /** 137 * Description of the Field 138 */ 139 protected int fileLocations = LOCAL_FILES; 140 /** 141 * Description of the Field 142 */ 143 protected QuestionHistory s1; 144 /** 145 * Description of the Field 146 */ 147 protected QuizStatus s2; 148 /** 149 * Description of the Field 150 */ 151 protected ChapterCoverage s3; 152 153 private static Category cat 154 = Category.getInstance(QuizController.class.getName()); 155 156 /** 157 * Describe variable <code>quiz</code> here. 158 */ 159 protected Quiz quiz; 160 161 162 /** 163 * Creates a new <code>QuizFrame</code> instance. 164 * 165 *@param q a <code>Quiz</code> value 166 *@param v a <code>WidgetView</code> value 167 */ 168 public QuizController(Quiz q, WidgetView v) 169 { 170 super(v); 171 cat.setLevel(Level.WARN); 172 rb = ResourceBroker.getInstance(); 173 if(q==null) return; 174 175 176 ChapterReference ref = null; 177 setQuiz(q); 178 ref = q.getChapterReference(); 179 180 // Set up the title 181 StringBuffer bf = new StringBuffer(); 182 bf.append(rb.getString(rb.PAVLOV_QUIZ)); 183 bf.append(": "); 184 bf.append(ref.getBookName()); 185 bf.append(" : "); 186 bf.append(ref.getChapterName()); 187 188 view.setToken(JPanelView.TITLE,bf.toString()); 189 190 // Set up BaseURL 191 //FIXME: try to get rid of this block 192 try { 193 File f = new File("resources/views/"); 194 view.setToken("BASE_URL", f.toURL()); 195 } 196 catch (Exception e) { 197 cat.error("Setting base_url", e); 198 } 199 } 200 201 202 /** 203 * Set the Quiz value. 204 * 205 *@param newQuiz The new Quiz value. 206 */ 207 public void setQuiz(Quiz newQuiz) 208 { 209 this.quiz = newQuiz; 210 if(quiz==null) return; 211 212 s1 = new QuestionHistory(view); 213 s2 = new QuizStatus(view); 214 s3 = new ChapterCoverage(view, quiz.getChapterData()); 215 216 quiz.addQuestionChangedListener(this); 217 quiz.addAnswerListener(s3); 218 quiz.addQuestionChangedListener(s1); 219 quiz.addAnswerListener(s2); 220 } 221 222 223 /** 224 * Get the Quiz value. 225 * 226 *@return the Quiz value. 227 */ 228 public Quiz getQuiz() 229 { 230 return quiz; 231 } 232 233 234 /** 235 * Description of the Method 236 * 237 *@param q Description of the Parameter 238 *@param qd Description of the Parameter 239 */ 240 public void questionChanged(Question q, QuestionData qd) 241 { 242 _presentQuestion(q); 243 } 244 245 246 /** 247 * Sets the template attribute of the QuizController object 248 * 249 *@param tmp The new template value 250 *@param baseDir The new template value 251 */ 252 public void setTemplate(String tmp, File baseDir) 253 { 254 VelocityModel m = (VelocityModel) view.getModel(); 255 m.setRawModel(tmp); 256 try { 257 view.setToken("BASE_URL", baseDir.toURL()); 258 } 259 catch (java.net.MalformedURLException e) { 260 System.out.println(e); 261 } 262 } 263 264 265 /** 266 * Describe <code>presentQuestion</code> method here. 267 * 268 *@param q a <code>Question</code> value 269 */ 270 private void _presentQuestion(Question q) 271 { 272 273 HashMap < Object, Object > props = 274 new HashMap < Object, Object > (); 275 276 props.put(JPanelView.TITLE, "Pavlov " + rb.getString(rb.PAVLOV_QUIZ));//^ 277 //ChapterReference ref = quiz.getChapterReference(); 278 279 // Set the question text 280 props.put(QUESTION_TEXT, q.getText()); 281 // Set the answers 282 283 java.util.Vector < String > vec = q.getWrongAnswers (); 284 // FIXME: trim vec to 3 items, and shuffle 285 Vector < AnswerAtom > answers = new Vector < AnswerAtom > (); 286 for (String x:vec) { 287 AnswerAtom at = new AnswerAtom (false, x); 288 answers.add (at); 289 } 290 291 answers.add(new AnswerAtom(true, q.getRightAnswer())); 292 Collections.shuffle(answers); 293 294 AnswerAtom a1 = answers.elementAt(0); 295 AnswerAtom a2 = answers.elementAt(1); 296 AnswerAtom a3 = answers.elementAt(2); 297 AnswerAtom a4 = answers.elementAt(3); 298 299 props.put(ANSWER_A, a1.getContent().toString()); 300 props.put(HREF_A, getCorrectString(q, a1)); 301 props.put(ANSWER_B, a2.getContent().toString()); 302 props.put(HREF_B, getCorrectString(q, a2)); 303 props.put(ANSWER_C, a3.getContent().toString()); 304 props.put(HREF_C, getCorrectString(q, a3)); 305 props.put(ANSWER_D, a4.getContent().toString()); 306 props.put(HREF_D, getCorrectString(q, a4)); 307 308 ChapterReference cr = quiz.getChapterReference(); 309 props.put(BOOK_NAME, cr.getBookName()); 310 props.put(CHAPTER_NAME, cr.getChapterName()); 311 props.put(JPanelView.TITLE, 312 rb.getString(rb.PAVLOV_QUIZ) + cr.getBookName() + " : " + 313 cr.getChapterName()); 314 props.put(URL_PREFIX, getURLPrefix()); 315 316 if (q.hasImage()) { 317 try { 318 319 //URL u = new URL( getURLPrefix()+"diagrams/"+q.getImageFile()); 320 File f = new File("diagrams/" + q.getImageFile());//^ 321 URL u = getFileURL(f); 322 props.put(QUESTION_IMAGE, u.toString()); 323 } 324 catch (Exception e) { 325 cat.warn("broken image reference", e); 326 props.put(QUESTION_IMAGE, "Broken"); 327 } 328 } 329 else { 330 //NullObject no = new NullObject (); 331 props.put(QUESTION_IMAGE, ""); 332 } 333 334 view.addTokens(props); 335 } 336 337 338 /** 339 * Gets the correctString attribute of the QuizController object 340 * 341 *@param ques Description of the Parameter 342 *@param at Description of the Parameter 343 *@return The correctString value 344 */ 345 protected String getCorrectString(Question ques, AnswerAtom at) 346 { 347 ChapterReference cr = quiz.getChapterReference(); 348 String bookName = null; 349 String chapterName = null; 350 351 String userName = null; 352 try { 353 bookName = encode(cr.getBookName()); 354 chapterName = encode(cr.getChapterName()); 355 userName = encode(quiz.getUserName()); 356 } 357 catch (java.io.UnsupportedEncodingException ex) { 358 cat.warn("Converting to UTF-8" + ex); 359 } 360 String x = 361 ANSWER_EVENT + "?user=" + userName + "&bookName=" + bookName + 362 "&chapterName=" + chapterName + "&questionID=" + ques.getID() + 363 "&" + ANSWER_MODE + "="; 364 if (at == null) { 365 return x + WRONG; 366 } 367 if (at.isCorrect()) { 368 return x + RIGHT; 369 } 370 return x + WRONG; 371 } 372 373 private String encode(String in) 374 throws java.io.UnsupportedEncodingException 375 { 376 return URLEncoder.encode(in,"UTF-8"); 377 } 378 379 /** 380 * Sets the fileLocations attribute of the QuizController object 381 * 382 *@param i The new fileLocations value 383 */ 384 public void setFileLocations(int i) 385 { 386 fileLocations = i; 387 } 388 389 390 /** 391 * Gets the uRLPrefix attribute of the QuizController object 392 * 393 *@return The uRLPrefix value 394 */ 395 public String getURLPrefix() 396 { 397 if (fileLocations == LOCAL_FILES) { 398 return "file:"; 399 } 400 else { 401 return "http://"; 402 } 403 } 404 405 406 /** 407 * Gets the fileURL attribute of the QuizController object 408 * 409 *@param f Description of the Parameter 410 *@return The fileURL value 411 */ 412 public URL getFileURL(File f) 413 { 414 if (fileLocations == LOCAL_FILES) { 415 try { 416 return f.toURI().toURL(); 417 } 418 catch (java.net.MalformedURLException ex) { 419 cat.warn("getting file URL", ex); 420 return null; 421 } 422 } 423 else { 424 try { 425 return new URL(f.toString()); 426 // FIXME: how to implement this 427 } 428 catch (java.net.MalformedURLException ex) { 429 cat.warn("getting file URL", ex); 430 return null; 431 } 432 } 433 } 434 435 } 436