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