1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: DistinctPassage.java 2221 2012-01-25 21:32:57Z dmsmith $
21   */
22  package org.crosswire.jsword.passage;
23  
24  import java.io.IOException;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.util.Collections;
28  import java.util.Iterator;
29  import java.util.SortedSet;
30  import java.util.TreeSet;
31  
32  import org.crosswire.jsword.versification.Versification;
33  
34  /**
35   * A Passage that is implemented using a TreeSet of Verses. The attributes of
36   * the style are:
37   * <ul>
38   * <li>Fairly fast manipulation
39   * <li>Slow getName()
40   * <li>Bloated for storing large numbers of Verses
41   * </ul>
42   * 
43   * @see gnu.lgpl.License for license details.<br>
44   *      The copyright to this program is held by it's authors.
45   * @author Joe Walker [joe at eireneh dot com]
46   */
47  public class DistinctPassage extends AbstractPassage {
48      /**
49       * Create an empty DistinctPassage. There are no ctors from either Verse or
50       * VerseRange so you need to do new <code>DistinctPassage().add(...);</code>
51       * 
52       * @param v11n
53       *            The Versification to which this Passage belongs.
54       */
55      protected DistinctPassage(Versification v11n) {
56          super(v11n);
57      }
58  
59      /**
60       * Create a Verse from a human readable string. The opposite of toString(),
61       * Given any DistinctPassage v1, and the following
62       * <code>DistinctPassage v2 = new DistinctPassage(v1.toString());</code>
63       * Then <code>v1.equals(v2);</code> Theoretically, since there are many ways
64       * of representing a DistinctPassage as text string comparison along the
65       * lines of: <code>v1.toString().equals(v2.toString())</code> could be
66       * false. Practically since toString() is standardized this will be true
67       * however. We don't need to worry about thread safety in a ctor since we
68       * don't exist yet.
69       * 
70       * @param v11n
71       *            The Versification to which this Passage belongs.
72       * @param refs
73       *            A String containing the text of the DistinctPassage
74       * @throws NoSuchVerseException
75       *             If the string is not valid
76       */
77      protected DistinctPassage(Versification v11n, String refs) throws NoSuchVerseException {
78          super(v11n, refs);
79  
80          store = Collections.synchronizedSortedSet(new TreeSet<Key>());
81          addVerses(refs);
82      }
83  
84      /**
85       * Get a copy of ourselves. Points to note:
86       * <ul>
87       * <li>Call clone() not new() on member Objects, and on us.
88       * <li>Do not use Copy Constructors! - they do not inherit well.
89       * <li>Think about this needing to be synchronized
90       * <li>If this is not cloneable then writing cloneable children is harder
91       * </ul>
92       * 
93       * @return A complete copy of ourselves
94       */
95      @Override
96      public DistinctPassage clone() {
97          // This gets us a shallow copy
98          DistinctPassage copy = (DistinctPassage) super.clone();
99  
100         // I want to just do the following
101         // copy.store = (SortedSet) store.clone();
102         // However SortedSet is not Cloneable so I can't
103         // Watch out for this, I'm not sure if it breaks anything.
104         copy.store = new TreeSet<Key>();
105         copy.store.addAll(store);
106 
107         return copy;
108     }
109 
110     /* (non-Javadoc)
111      * @see java.lang.Iterable#iterator()
112      */
113     public Iterator<Key> iterator() {
114         return store.iterator();
115     }
116 
117     @Override
118     public boolean isEmpty() {
119         return store.isEmpty();
120     }
121 
122     @Override
123     public int countVerses() {
124         return store.size();
125     }
126 
127     @Override
128     public boolean contains(Key obj) {
129         for (Key aKey : obj) {
130             if (!store.contains(aKey)) {
131                 return false;
132             }
133         }
134 
135         return true;
136     }
137 
138     /* (non-Javadoc)
139      * @see org.crosswire.jsword.passage.Passage#add(org.crosswire.jsword.passage.Key)
140      */
141     public void add(Key obj) {
142         optimizeWrites();
143 
144         Verse firstVerse = null;
145         Verse lastVerse = null;
146         for (Key aKey : obj) {
147             lastVerse = (Verse) aKey;
148             if (firstVerse == null) {
149                 firstVerse = lastVerse;
150             }
151             store.add(lastVerse);
152         }
153 
154         // we do an extra check here because the cost of calculating the
155         // params is non-zero an may be wasted
156         if (suppressEvents == 0) {
157             fireIntervalAdded(this, firstVerse, lastVerse);
158         }
159     }
160 
161     /* (non-Javadoc)
162      * @see org.crosswire.jsword.passage.Passage#remove(org.crosswire.jsword.passage.Key)
163      */
164     public void remove(Key obj) {
165         optimizeWrites();
166 
167         Verse firstVerse = null;
168         Verse lastVerse = null;
169         for (Key aKey : obj) {
170             lastVerse = (Verse) aKey;
171             if (firstVerse == null) {
172                 firstVerse = lastVerse;
173             }
174             store.remove(lastVerse);
175         }
176 
177         // we do an extra check here because the cost of calculating the
178         // params is non-zero an may be wasted
179         if (suppressEvents == 0) {
180             fireIntervalAdded(this, firstVerse, lastVerse);
181         }
182     }
183 
184     @Override
185     public void clear() {
186         optimizeWrites();
187 
188         store.clear();
189         fireIntervalRemoved(this, null, null);
190     }
191 
192     /**
193      * Call the support mechanism in AbstractPassage
194      * 
195      * @param out
196      *            The stream to write our state to
197      * @throws IOException
198      *             if the read fails
199      * @serialData Write the ordinal number of this verse
200      * @see AbstractPassage#writeObjectSupport(ObjectOutputStream)
201      */
202     private void writeObject(ObjectOutputStream out) throws IOException {
203         out.defaultWriteObject();
204 
205         writeObjectSupport(out);
206     }
207 
208     /**
209      * Call the support mechanism in AbstractPassage
210      * 
211      * @param in
212      *            The stream to read our state from
213      * @throws IOException
214      *             if the read fails
215      * @throws ClassNotFoundException
216      *             If the read data is incorrect
217      * @serialData Write the ordinal number of this verse
218      * @see AbstractPassage#readObjectSupport(ObjectInputStream)
219      */
220     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
221         optimizeWrites();
222 
223         store = new TreeSet<Key>();
224 
225         in.defaultReadObject();
226 
227         readObjectSupport(in);
228     }
229 
230     /**
231      * To make serialization work across new versions
232      */
233     static final long serialVersionUID = 817374460730441662L;
234 
235     /**
236      * The place the real data is stored
237      */
238     private transient SortedSet<Key> store = new TreeSet<Key>();
239 }
240