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/ChapterData.java,v 1.9 2004/07/01 09:03:44 tj_willis Exp $
019     */ 
020    package net.sourceforge.pavlov.user;
021    
022    import java.util.*;
023    
024    import net.sourceforge.pavlov.library.AbstractChapter;
025    import net.sourceforge.pavlov.library.Question;
026    import org.apache.log4j.*;
027    
028    
029    /**
030     * Encapsulates a users responses to all questions in a chapter.
031     *
032     * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
033     * @version $Revision: 1.9 $
034     * @see net.sourceforge.pavlov.library.Chapter
035     * @see BookData
036     * @see QuestionData
037     */
038    public final class ChapterData {
039            // Unit coverage in TestQuiz.java
040            /** Title of the corresponding Chapter */
041            private String title;
042            /** Holds QuestionData. */
043            private  Hashtable<String,QuestionData> questions;
044            /** Strategy to use in choosing next question. */
045            private AbstractStrategy strategy = null;
046            /** Detailed statistics about responses to this chapter. */
047            private ChapterStatistics chapterStatistics;
048            /** Results of past quizzes. */
049            private  QuizCollection quizCollection;
050            /** QuestionData's we don't want to ask*/
051            protected Vector<QuestionData> exclusion;
052            /** QuestionData's we don't want to ask*/
053            protected int exclusionSize = 5;
054    
055            /**
056             * Creates a ChapterData with the given title
057             * @param _title a <code>String</code> value
058             */
059            public ChapterData(String title) {
060                    this.title = title;
061                    init();
062            }
063    
064            private void init()
065            {
066                    questions = new Hashtable<String,QuestionData>(150);
067                    quizCollection = new QuizCollection();
068                    exclusion = new Vector<QuestionData>();
069            }
070    
071            
072            // FIXME: title should be "name" to agree with library.Chapter
073            
074            /** Creates a ChapterData with title "Untitled" */
075            public ChapterData() {
076                    this("Untitled");
077            }
078    
079            /**
080             * Sets the title of the Chapter this ChapterData refers to.
081             *
082             * @param t a <code>String</code> value
083             */
084            public void setTitle(String title) {
085                    this.title = title;
086            }
087    
088    
089            /**
090             * Returns the title of the Chapter this ChapterData refers to.
091             *
092             * @return a <code>String</code> value
093             */
094            public String getTitle() {
095                    return title;
096            }
097    
098            /**
099             * Returns the QuestionData for the question with the given ID
100             * or null if no such question exists.
101             *
102             * @param qid a <code>String</code> value
103             * @return a <code>QuestionData</code> value
104             */
105            public QuestionData getQuestionData(final String qid) {
106                    return questions.get(qid);
107            }
108    
109            /**
110             * Adds a correct response to the named QuestionData at the
111             * current time. 
112             *
113             * @param id a <code>String</code> value
114             */
115            public void addRight(final String id) {
116                    Date now = new Date();
117                    addRight(id, now);
118            }
119    
120            /**
121             * Adds an incorrect response to the named QuestionData at the
122             * current time. 
123             *
124             * @param id a <code>String</code> value
125             */
126            public void addWrong(final String id) {
127                    Date now = new Date();
128                    addWrong(id, now);
129            }
130    
131            /**
132             * Returns the number of questions in this chapter.
133             *
134             * @return an <code>int</code> value
135             */
136            public int getNumberOfQuestions() {
137                    if (questions == null) return 0;
138                    return questions.size();
139            }
140    
141            /**
142             * Adds a correct response to the named QuestionData at the
143             * given time.
144             *
145             * @param id a <code>String</code> value
146             * @param whe a <code>long</code> value
147             */
148            public void addRight(final String id, final long when) {
149                    Date now = new Date(when);
150                    addRight(id, now);
151            }
152    
153            /**
154             * Adds an incorrect response to the named QuestionData at the
155             * given time.
156             *
157             * @param id a <code>String</code> value
158             * @param whe a <code>long</code> value
159             */
160            public void addWrong(final String id, final long whe) {
161                    Date now = new Date(whe);
162                    addWrong(id, now);
163            }
164    
165            /**
166             * Adds a correct response to the named QuestionData at the
167             * given time.
168             *
169             * @param id a <code>String</code> value
170             * @param when a <code>Date</code> value
171             */
172            public void addRight(final String id, final Date when) {
173                    QuestionData q = (QuestionData) questions.get(id);
174                    if (q == null) {
175                            q = new QuestionData(id);
176                            questions.put(id, q);
177                    }
178                    q.addRight(when);
179            }
180    
181            /**
182             * Adds an incorrect response to the named QuestionData at the
183             * given time. 
184             *
185             * @param id a <code>String</code> value
186             * @param when a <code>Date</code> value
187             */
188            public void addWrong(final String id, final Date when) {
189                    QuestionData q = (QuestionData) questions.get(id);
190                    if (q == null) {
191                            q = new QuestionData(id);
192                            questions.put(id, q);
193                    }
194                    q.addWrong(when);
195            }
196    
197            /**
198             * Loop through all the questions in the chapter, ensuring a
199             * QuestionData exists for each.  If not, create a new QuestionData.
200             *
201             * @param cp an <code>AbstractChapter</code> value
202             */
203            public void validate(AbstractChapter cp) {
204                    // foreach question in chapter
205                    // get QuestionData for question
206                    // if NEXIST, make a new one
207                    Collection<Question> v = cp.getQuestionsReadOnly();
208    
209            for(Question left: v) {
210                            QuestionData da = (QuestionData) questions.get(left.getID());
211    
212                            if (da == null) {
213                                    QuestionData x = new QuestionData(left.getID());
214    
215                                    questions.put(x.getID(), x);
216                            }
217                            // DON'T NEED x.validate(left) -- these are leaves
218                    }
219            }
220    
221            /**
222             * Returns the size of the exclusion vector.
223             *
224             * @return an <code>int</code> value
225             */
226            public int getExclusionSize()
227            {
228                    return exclusionSize;
229            }
230    
231            /**
232             * Sets the size of the exclusion vector.
233             *
234             * @param i an <code>int</code> value
235             */
236            public void setExclusionSize(final int newSize)
237            {
238                    int x = newSize;
239                    if(x<0){ 
240                        x = 0;
241                    }
242                    exclusionSize = x;
243            }
244    
245            /**
246             * Returns a QuestionData using the current strategy.  If no strategy
247             * is current, create a BasicStrategy and use it.
248             *
249             * @return a <code>QuestionData</code> value
250             */
251            public QuestionData getQuestion() {
252                    //assert strategy != null : "Null strategy in getQuestion";
253                    if(strategy==null){
254                            strategy = new BasicStrategy(questions.values());
255                    }
256                    QuestionData da =  strategy.getQuestion(exclusion);
257                    //FIXME: add this:
258                    //while exclusion.size()>= # avail questions exclusion.remove(0);
259                    if(exclusion.size()>exclusionSize){
260                            exclusion.remove(0);
261                    }
262                    exclusion.add(da);
263                    return da;
264            }
265    
266            /**
267             * Returns the current question selection strategy.
268             *
269             * @return an <code>AbstractStrategy</code> value
270             */
271            public final AbstractStrategy getStrategy()
272            {
273                    return strategy;
274            }
275    
276    
277            /**
278             * Sets the question selection strategy and initializes it with
279             * the QuestionData for this chapter.
280             *
281             * @param strat an <code>AbstractStrategy</code> value
282             */
283            public final void setStrategy(AbstractStrategy strat) {
284                    if(strat==null) return;
285                    strat.setQuestions(questions.values());
286                    strategy = strat;
287            }
288    
289            /**
290             * Returns the <code>ChapterStatistics</code> for
291             * this ChapterData.
292             *
293             * @return a <code>ChapterStatistics</code> value
294             */
295            public ChapterStatistics getChapterStatistics() {
296                    if (chapterStatistics != null){
297                            return chapterStatistics;
298                    }
299                    Collection<QuestionData> v = questions.values();
300                    chapterStatistics = new ChapterStatistics();
301            for(QuestionData q: v){
302                            chapterStatistics.addQuestionData(q);
303                    }
304    
305                    return chapterStatistics;
306            }
307    
308            /**
309             * Removes the QuestionData corresponding to the given ID.
310             *
311             * @param key a <code>String</code> value
312             */
313            public void deleteQuestionID(final String key) {
314                    questions.remove(key);
315            }
316    
317            /**
318             * Converts this ChapterData to XML.
319             *
320             * @param writer a <code>java.io.Writer</code> value
321             * @exception java.io.IOException if an error occurs
322             */
323            public void toXML(java.io.Writer writer)
324            throws java.io.IOException {
325                    Collection<QuestionData> v = questions.values();
326    
327                    writer.write("\t\t<CHAPTER TITLE=\"" + title + "\">\n"); // LAST=\"19/Feb/02\">\n");
328    
329                    //x += "This chapter has " + v.size() + " questions w/ history\n";
330                    for( QuestionData q : v ) {
331                            q.toXML(writer);
332                    }
333    
334                    quizCollection.toXML(writer);
335    
336                    writer.write("\t\t</CHAPTER>\n");
337    
338            }
339    
340            /**
341             * Adds the given QuizData object to this ChapterData's QuizCollection.
342             *
343             * @param quizData a <code>QuizData</code> value
344             */
345            public void addQuizData(final QuizData quizData) {
346                    quizCollection.addQuiz(quizData);
347            }
348    
349            /**
350             * Describe <code>getStatistics</code> method here.
351             *
352             * @deprecated Misleading name.  Use getQuizCollectionData()
353             * @return a <code>QuizCollectionData</code> value
354             */
355            @Deprecated public QuizCollectionData getStatistics() {
356                    return getQuizCollectionData();
357            }
358    
359    
360            /**
361             * Returns data for all quizzes the user has taken on this chapter.
362             *
363             * @return a <code>QuizCollectionData</code> value
364             */
365            public QuizCollectionData getQuizCollectionData() {
366                    return quizCollection.getStatistics();
367            }
368    
369            /**
370             * Returns a vector of percentages for all the quizzes the user
371             * has taken on this chapter.
372             *
373             * @return a <code>Vector</code> value
374             */
375            public Vector getQuizPercentages() {
376                    return quizCollection.getPercentages();
377            }
378    
379            /**
380             * Describe <code>getQuizCollection</code> method here.
381             *
382             * @return a <code>QuizCollection</code> value
383             */
384            public QuizCollection getQuizCollection()
385            {
386                    return quizCollection;
387            }
388    
389    
390            // FIXME: make an unmodifiable list version of this method
391            /**
392             * Describe <code>getAllQuestionData</code> method here.
393             *
394             * @return a <code>Collection</code> value
395             */
396            public Collection getAllQuestionData()
397            {
398                    return questions.values();
399            }
400    }
401    
402    
403    
404    
405    
406    
407    
408