| DistinctPassage.java |
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