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/library/Chapter.java,v 1.7 2004/07/01 05:50:20 tj_willis Exp $
019     */ 
020    package net.sourceforge.pavlov.library;
021    import java.util.*;
022    import java.io.*;
023    import org.apache.log4j.*;
024    
025    /**
026     * Describes a chapter, which contains questions.
027     *
028     * @author <a href="mailto:tj_willis@users.sourceforge.net"></a>
029     * @version 1.0
030     * @see Question  
031     * @see Book
032     * @see Library
033     * @has 1 Has 1..n net.sourceforge.pavlov.library.Question
034     */
035    public final class Chapter extends LibraryDocument 
036        implements AbstractChapter, Serializable, Comparable {
037        /** Filename for a descriptive image. */ 
038        private String coverFile;
039        /** Holds the questions, keyed off of Question.id */
040        private TreeMap<String,Question> questions; 
041        
042        /** Creates an untitled chapter. */
043        public Chapter() {
044            this("Untitled");
045        }
046    
047        private void init(){
048            questions = new TreeMap<String, Question>();
049        }
050    
051        /**
052         * Creates a new <code>Chapter</code> instance.
053         *
054         * @param newName a <code>String</code> value
055         */
056        public Chapter(String newName){
057            setName(newName);
058            init();
059        }
060    
061        /**
062         * Gets the question with the specified ID or null if it doesn't exist.
063         * @param id a <code>String</code> value
064         * @return a <code>Question</code> value
065         */
066        public Question getQuestion(final String id) {
067            return questions.get(id);
068        }
069    
070        public int compareTo(Object obj){
071                Chapter c = (Chapter)obj;
072                return getName().compareTo(c.getName());    
073        }
074    
075        /**
076         * Describe <code>setQuestions</code> method here.
077         *
078         */
079        public void setQuestions(Collection<Question> list) {
080            questions.clear();
081            for(Question da : list) {
082                if(da==null) continue;
083                addQuestion(da);
084            }
085        }
086    
087        /**
088         * Number of questions this chapter contains.
089         * @return an <code>int</code> value
090         */
091        public int getNumberOfQuestions() {
092            return questions.size();
093        }
094    
095        /**
096         * Returns the title of this chapter.
097         * @return a <code>String</code> value
098         * @deprecated Use getName()
099         */
100        @Deprecated public String getTitle() {
101            return getName();
102        }
103    
104        /**
105         * Sets the title of this chapter.
106         * @param n a <code>String</code> value
107         * @deprecated Use setTitle()
108         */
109        @Deprecated public void setTitle(String n) {
110            setName(n);
111        }
112    
113        /**
114         * Returns the filename for a descriptive image.
115         * @return a <code>String</code> value
116         */
117        public String getCover() {
118            return coverFile;
119        }
120    
121        /**
122         * Sets the filename for a descriptive image.
123         * @param n a <code>String</code> value
124         */
125        public void setCover(String n) {
126            coverFile = n;
127        }
128    
129        /**
130         * Adds a question using it's ID as the key
131         * @param b a <code>Question</code> value
132         */
133        public void addQuestion(Question b) {
134            questions.put(b.getID(), b);
135        }
136    
137        /**
138         * Dumps the chapter in XML format to the
139         * given writer.  Dumps all questions too.
140         * @param writer a <code>java.io.Writer</code> value
141         * @exception java.io.IOException if an error occurs
142         */
143        public void toXML(java.io.Writer writer) 
144            throws java.io.IOException {    
145            Collection<Question> v = getQuestionsCollection();
146            
147            writer.write("\t\t<CHAPTER NAME=\"" + getName() + "\" AUTHOR=\"" + author + "\" COVER=\"" + coverFile + "\">\n");
148            writer.write("\t\t<TOPICS>" + description + "</TOPICS>\n");
149    
150            for(Question da : v) {
151                da.toXML(writer);
152            }
153            writer.write("\t\t</CHAPTER>\n");
154        }
155    
156        /**
157         * Returns all questions as a Collection 
158         * @return a <code>Collection</code> value
159         * @deprecated
160         */
161        @Deprecated public Collection<Question> getQuestionsCollection() {
162            return questions.values();
163        }
164    
165        /**
166         * Returns the chapter's questions as a read-only collection. 
167         * @return a <code>Collection</code> value
168         */
169        public Collection<Question> getQuestionsReadOnly(){
170            return Collections.unmodifiableCollection(getQuestionsCollection());
171        }
172    
173        /**
174         * Describe <code>getQuestions</code> method here.
175         *
176         * @return a <code>Hashtable</code> value
177         */
178        public TreeMap<String, Question> getQuestions(){
179            return questions;
180        }
181    
182        /**
183         * Returns the chapter's title
184         * @return a <code>String</code> value
185         */
186        public String toString() {
187            return getName();
188        }
189    
190        /**
191         * Describe <code>makeBlankChapter</code> method here.
192         *
193         * @return a <code>Chapter</code> value
194         */
195        public static Chapter makeBlankChapter()
196        {
197            Chapter n = new Chapter();
198            n.setAuthor("Unknown");
199            n.setTitle("Unknown");
200            n.setCover("Unknown");
201            n.setCover("No description");
202            Question q = Question.makeBlankQuestion();
203            n.addQuestion(q.deepCopy());
204            return n;   
205        }
206    
207        /**
208         * Returns true if the given object is a chapter and all its elements
209         * are equal to mine.
210         *
211         * @param obj an <code>Object</code> value
212         * @return a <code>boolean</code> value
213         */
214        public boolean equals(Object obj)
215        {
216            if(obj==null && this!=null) return false;
217            if(obj!=null && this==null) return false;
218            if(obj==null && this==null) return true;
219            
220            if( !(obj instanceof Chapter) ) return false;
221            Chapter q = (Chapter) obj;
222            
223            if(getAuthor() == null && q.getAuthor() !=null) return false;
224            if(getName() == null && q.getName() !=null) return false;
225            if(getCover()== null && q.getCover() !=null) return false;
226            if(getDescription() == null && q.getDescription() !=null) return false;
227            if(getQuestions() == null && q.getQuestions() !=null) return false;
228            
229            // FIXED: these should be .equals()
230            // whee... my first bug caught by JUnit! :)
231            if(getAuthor()!=null)
232                if( !getAuthor().equals(q.getAuthor())) return false;
233            if(getName()!=null)
234                if( !getName().equals(q.getName())) return false;
235            if(getCover()!=null)
236                if( !getCover().equals(q.getCover())) return false;
237            if(getDescription()!=null)
238                if( !getDescription().equals(q.getDescription())) return false;
239            if(getQuestions()!=null)
240                if( !getQuestions().equals(q.getQuestions())) return false;
241            
242            return true;
243        }
244    
245    
246        /**
247         * Uses a nifty trick from "Design Patterns in Java" to make a 
248         * completely independent copy of this chapter.  Useful in chapter
249         * editors.
250         *
251         * @return a <code>Chapter</code> value
252         */
253        public Chapter deepCopy()
254        {
255            try {
256                ByteArrayOutputStream b = new ByteArrayOutputStream();
257                ObjectOutputStream out = new ObjectOutputStream(b);
258                out.writeObject(this);
259                ByteArrayInputStream bIn = new ByteArrayInputStream(b.toByteArray());
260                ObjectInputStream oi = new ObjectInputStream(bIn);
261                return ( (Chapter)oi.readObject());
262            } catch (IOException e) {
263                return null;
264            } catch (ClassNotFoundException e1){
265                return null;
266            }
267        }
268    
269    
270    }
271