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