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