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/User.java,v 1.8 2004/07/01 09:03:44 tj_willis Exp $
019 */
020 package net.sourceforge.pavlov.user;
021
022 import java.io.*;
023 import java.net.URLEncoder;
024 import java.util.*;
025 import net.sourceforge.pavlov.library.AbstractBook;
026 import net.sourceforge.pavlov.library.AbstractChapter;
027 import net.sourceforge.pavlov.library.AbstractLibrary;
028 import net.sourceforge.pavlov.library.Question;
029 import net.sourceforge.pavlov.library.ChapterReference;
030 import org.apache.log4j.*;
031
032 /**
033 * Describe class <code>User</code> here.
034 *
035 * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
036 * @version 1.0
037 */
038 public final class User {
039 // FIXED: Books -> books
040 // FIXED: tiger-style this file
041 private Hashtable<String, BookData> books;
042 private String name;
043 private String password;
044 private UserFile userFile;
045
046
047 /**
048 * Creates a user with the given name and password.
049 * @param name a <code>String</code> value
050 * @param password a <code>String</code> value
051 */
052 public User(String name, String password) {
053 this.name = name;
054 this.password = password;
055 init();
056 }
057
058 /**
059 * Creates an empty user named "Unnamed" with password "empty."
060 */
061 public User() {
062 this("Unnamed","empty");
063 }
064
065 private void init(){
066 books = new Hashtable<String, BookData>();
067 }
068
069 /**
070 * Returns true if pass is the user's password.
071 * @param pass a <code>String</code> value
072 * @return a <code>boolean</code> value
073 */
074 public boolean checkPassword(final String pass) {
075 if (password == null){
076 return true;
077 }
078 return password.equals(pass);
079 }
080
081 /**
082 * Returns true if 1) both are null, or 2)
083 * name and password are the same.
084 *
085 * @param o an <code>Object</code> value
086 * @return a <code>boolean</code> value
087 */
088 public boolean equals(Object o){
089 if(o==null) {
090 if (this==null) return true;
091 return false;
092 }
093 if( ! (o instanceof User) )
094 return false;
095 User u = (User) o;
096 if( ! getName().equals(u.getName())) return false;
097 if( ! u.checkPassword(password)) return false;
098 return true;
099 }
100
101
102 /**
103 * Gets data describing the users global history with the named book.
104 *
105 * @param bkName a <code>String</code> value
106 * @return a <code>BookData</code> value
107 */
108 public BookData getBookData(final String bkName)
109 {
110 return books.get(bkName);
111 }
112
113 /**
114 * Ensures the user has QuestionData for all the questions in
115 * this book, creating QuestionData as necessary.
116 *
117 * @param l an <code>AbstractLibrary</code> value
118 */
119 public void validate(AbstractLibrary l) {
120 // foreach book in library
121 // get BookData for book
122 // BookData.validate(book)
123
124 assert l != null : "Library is null!";
125
126 Collection<AbstractBook> v = l.getBooksReadOnly();
127
128 for(AbstractBook left : v) {
129 BookData da = books.get(left.getName());
130
131 if (da == null) {
132 BookData x = new BookData(left.getName());
133
134 books.put(x.getTitle(), x);
135 da = x;
136 }
137 da.validate(left);
138 }
139 //System.out.println("End Validating at " + new Date());
140 }
141
142 /**
143 * Describe <code>getQuestionData</code> method here.
144 *
145 * @param bkName a <code>String</code> value
146 * @param cpName a <code>String</code> value
147 * @param qid a <code>String</code> value
148 * @return a <code>QuestionData</code> value
149 * @deprecated, should be able to get this from ChapterData
150 */
151 @Deprecated public QuestionData getQuestionData(String bkName, String cpName, String qid) {
152 BookData bd = (BookData) books.get(bkName);
153
154 if (bd == null) return null;
155 return bd.getQuestionData(cpName, qid);
156 }
157
158 /**
159 * Number of questions in this book.
160 *
161 * @param bkName a <code>String</code> value
162 * @param cpName a <code>String</code> value
163 * @return an <code>int</code> value
164 * @deprecated, should be able to get this from ChapterData
165 */
166 @Deprecated public int getNumberOfQuestions(final String bkName, final String cpName) {
167 ChapterData cd = getChapterData(bkName, cpName);
168
169 if (cd == null) return 0;
170 return cd.getNumberOfQuestions();
171 }
172
173 /**
174 * if this is needed by an outside class, make it through an
175 * intermediate method in this class
176 * @param bkName a <code>String</code> value
177 * @param cpName a <code>String</code> value
178 * @param qn a <code>String</code> value
179 * @deprecated implement reverse validation in this class, then privatize this method
180 */
181 @Deprecated public void deleteQuestionID(String bkName, String cpName, String qn) {
182 ChapterData cd = getChapterData(bkName, cpName);
183
184 if (cd == null) return;
185 cd.deleteQuestionID(qn);
186 }
187
188 /**
189 * Reverse-validation: if a user.QuestionData exists for a nonexistant
190 * library.question, unceremoniously remove the user.QuestionData.
191 * @param chapDat a <code>ChapterData</code> value
192 * @param chapt an <code>AbstractChapter</code> value
193 * @return a <code>Question</code> value
194 * @see net.sourceforge.pavlov.library.Chapter
195 * @see net.sourceforge.pavlov.user.ChapterData
196 */
197 public static Question getValidQuestion(ChapterData chapDat, AbstractChapter chapt )
198 {
199 if(chapDat==null) throw new IllegalArgumentException("Chapter Data is null");
200 if(chapt==null) throw new IllegalArgumentException ("Chapter is null");
201 QuestionData questionData = chapDat.getQuestion();
202 assert questionData!=null: "ChapterData.getQuestion returned null questiondata";
203 String qid = questionData.getID();
204 assert qid!=null: "questionData.getID returned null";
205 Question question = chapt.getQuestion(qid);
206 assert question!=null: "chapter.getQuestion(qid) returned null question";
207 //QuestionData questionData = chapDat.getQuestionData(qid);
208
209
210 while ( (question == null) &&
211 (chapDat.getNumberOfQuestions() > 0)
212 )
213 {
214 // if question is null, qid is invalid, delete it
215 chapDat.deleteQuestionID(qid);
216 // get a new candidate
217 questionData = chapDat.getQuestion();
218 qid = questionData.getID();
219 // if it exists, return it below.. if not, delete it & try again
220 question = chapt.getQuestion(qid);
221 }
222
223 assert question!=null : "No Questions to Ask!";
224 return question;
225
226 }
227 // public Question reverseValidate(ChapterData cd, String qn, Chapter c )
228 // {
229 // String qid=qn;
230 // Question x = c.getQuestion(qid);
231 // while ((x == null) && (cd.getNumberOfQuestions() > 0)) {
232 // cd.deleteQuestionID(qid);
233 // qid = cd.getQuestionID();
234 // x = c.getQuestion(qid);
235 // }
236 // return x;
237 // }
238
239 /**
240 * Returns a question from the given book & chapter using
241 * the current strategy.
242 * @param bkName a <code>String</code> value
243 * @param cpName a <code>String</code> value
244 * @return a <code>String</code> value
245 * @see ChapterData.getQuestion()
246 */
247 public String getQuestionId(final String bkName, final String cpName) {
248 ChapterData cd = getChapterData(bkName, cpName);
249
250 if (cd == null) return null;
251
252 QuestionData qd = cd.getQuestion();
253
254 if (qd == null) return null;
255 return qd.getID();
256 }
257
258 /**
259 * Sets this user's name.
260 * @param n a <code>String</code> value
261 */
262 public void setName(final String n) {
263 name = n;
264 }
265
266 /**
267 * Returns this user's name.
268 * @return a <code>String</code> value
269 */
270 public String getName() {
271 return name;
272 }
273
274 /**
275 * Sets this user's password. Notice that getPassword isn't implemented.
276 * @param n a <code>String</code> value
277 */
278 public void setPassword(String n) {
279 password = n;
280 }
281
282 /**
283 * Adds the bookdata to this user.
284 *
285 * @param b a <code>BookData</code> value
286 */
287 public void addBook(BookData b) {
288 books.put(b.getTitle(), b);
289 }
290
291
292
293 /**
294 * Sends a representation of this user in XML form to the given writer.
295 * @param writer a <code>java.io.Writer</code> value
296 * @exception java.io.IOException if an error occurs
297 */
298 public void toXML(java.io.Writer writer)
299 throws java.io.IOException {
300
301 writer.write("<USER SN=\"" + name + "\" PASSWORD=\"" + password + "\">\n");
302 Collection<BookData> v = books.values();
303
304 for(BookData bk : v) {
305 bk.toXML(writer);
306 }
307 writer.write("</USER>\n");
308 }
309
310 /**
311 * Gets the userfile to save this user to.
312 *
313 * @return an <code>UserFile</code> value
314 */
315 public UserFile getUserFile()
316 {
317 return userFile;
318 }
319
320 /**
321 * Sets the userfile for saving/loading this user.
322 *
323 * @param f an <code>UserFile</code> value
324 */
325 public void setUserFile(UserFile f)
326 {
327 userFile = f;
328 }
329
330 /**
331 * Saves this file to file userFile.
332 *
333 * @exception FileNotFoundException if an error occurs
334 * @exception IOException if an error occurs
335 */
336 public void save()
337 throws FileNotFoundException, IOException
338 {
339 userFile.save(this);
340
341 }
342
343 /**
344 * Saves this file to the given directory.
345 *
346 * @param directory a <code>File</code> value
347 * @exception IOException if an error occurs
348 */
349 public void save(File directory)
350 throws IOException
351 {
352 File outputFile = null;
353 outputFile = new File(directory,getBaseFileName()+".xml");
354 FileWriter out = new FileWriter(outputFile);
355 toXML(out);
356 out.flush();
357 out.close();
358 }
359
360
361 /**
362 * Describe <code>getBaseFileName</code> method here.
363 *
364 * @return a <code>String</code> value
365 */
366 protected String getBaseFileName()
367 {
368 String n = getName().toLowerCase();
369 try {
370 n = URLEncoder.encode(n,"UTF-8");
371 } catch (Exception e) {
372 //FIXME: don't ignore exception!
373 }
374 return n;
375 }
376
377 /**
378 * Describe <code>getChapterData</code> method here.
379 *
380 * @param bkName a <code>String</code> value
381 * @param cpName a <code>String</code> value
382 * @return a <code>ChapterData</code> value
383 * @deprecated use getChapterData(ChapterReference) instead.
384 */
385 @Deprecated public ChapterData getChapterData(final String bkName, final String cpName) {
386 BookData bd = books.get(bkName);
387 //ChapterData cd = null;
388
389 if (bd == null) return null;
390 return bd.getChapter(cpName);
391 }
392
393 /**
394 * Describe <code>getChapterData</code> method here.
395 *
396 * @param ref a <code>ChapterReference</code> value
397 * @return a <code>ChapterData</code> value
398 */
399 public ChapterData getChapterData(final ChapterReference ref){
400 return getChapterData(ref.getBookName(),ref.getChapterName());
401
402 }
403
404 /**
405 * Adds data for a quiz to this user.
406 * @param who a <code>ChapterData</code> value
407 * @param qdd a <code>QuizData</code> value
408 */
409 public static void addQuizData(final ChapterData who, QuizData qdd) {
410 who.addQuizData(qdd);
411 }
412
413 /**
414 * Returns descriptive statistics about this user's past quizzes.
415 * @param who a <code>ChapterData</code> value
416 * @return a <code>QuizCollectionData</code> value
417 */
418 public static QuizCollectionData getStatistics(final ChapterData who) {
419 return who.getQuizCollectionData();
420 }
421
422 }
423
424
425
426
427