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