| BookData.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: BookData.java 1607 2007-08-04 17:13:24Z dmsmith $
21 */
22 package org.crosswire.jsword.book;
23
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.crosswire.common.diff.Diff;
28 import org.crosswire.common.diff.DiffCleanup;
29 import org.crosswire.common.util.Language;
30 import org.crosswire.common.xml.JDOMSAXEventProvider;
31 import org.crosswire.common.xml.SAXEventProvider;
32 import org.crosswire.jsword.passage.Key;
33 import org.jdom.Content;
34 import org.jdom.Document;
35 import org.jdom.Element;
36 import org.jdom.Namespace;
37 import org.jdom.Text;
38
39 /**
40 * BookData is the assembler of the OSIS that is returned by the filters.
41 * As such it puts that into an OSIS document. When several books are
42 * supplied, it gets the data from each and puts it into a parallel or
43 * interlinear view.
44 * Note: it is critical that all the books are able to understand the same key.
45 * That does not mean that each has to have content for each key. Missing keys
46 * are represented by empty cells.
47 *
48 * @see gnu.lgpl.License for license details.
49 * The copyright to this program is held by it's authors.
50 * @author Joe Walker [joe at eireneh dot com]
51 * @author DM Smith [dmsmith555 at yahoo dot com]
52 */
53 public class BookData implements BookProvider
54 {
55 /**
56 * Ctor
57 */
58 public BookData(Book book, Key key)
59 {
60 assert book != null;
61 assert key != null;
62
63 this.key = key;
64
65 books = new Book[1];
66 books[0] = book;
67 }
68
69 /**
70 * Create BookData for multiple books.
71 */
72 public BookData(Book[] books, Key key, boolean compare)
73 {
74 assert books != null && books.length > 0;
75 assert key != null;
76
77 this.books = (Book[]) books.clone();
78 this.key = key;
79 this.comparingBooks = compare;
80 }
81
82 /**
83 * Accessor for the root OSIS element
84 */
85 public Element getOsis() throws BookException
86 {
87 if (osis == null)
88 {
89 // TODO(DMS): Determine the proper representation of the OSISWork name for multiple books.
90 osis = OSISUtil.createOsisFramework(getFirstBook().getBookMetaData());
91 Element text = osis.getChild(OSISUtil.OSIS_ELEMENT_OSISTEXT);
92 Element div = getOsisFragment();
93 text.addContent(div);
94 }
95
96 return osis;
97 }
98
99 /**
100 * Accessor for the root OSIS element
101 */
102 public Element getOsisFragment() throws BookException
103 {
104 if (fragment == null)
105 {
106 fragment = getOsisContent();
107 }
108
109 return fragment;
110 }
111
112 /**
113 * Output the current data as a SAX stream.
114 * @return A way of posting SAX events
115 */
116 public SAXEventProvider getSAXEventProvider() throws BookException
117 {
118 // If the fragment is already in a document, then use that.
119 Element frag = getOsisFragment();
120 Document doc = frag.getDocument();
121 if (doc == null)
122 {
123 doc = new Document(frag);
124 }
125 return new JDOMSAXEventProvider(doc);
126 }
127
128 /**
129 * Who created this data.
130 * @return Returns the book.
131 */
132 public Book[] getBooks()
133 {
134 return books == null ? null : (Book[]) books.clone();
135 }
136
137 /**
138 * Get the first book.
139 */
140 public Book getFirstBook()
141 {
142 return books != null && books.length > 0 ? books[0] : null;
143 }
144
145 /**
146 * The key used to obtain data from one or more books.
147 * @return Returns the key.
148 */
149 public Key getKey()
150 {
151 return key;
152 }
153
154 /**
155 * @return whether the books should be compared.
156 */
157 public boolean isComparingBooks()
158 {
159 return comparingBooks;
160 }
161
162 private Element getOsisContent() throws BookException
163 {
164 Element div = OSISUtil.factory().createDiv();
165
166 if (books.length == 1)
167 {
168 Iterator iter = books[0].getOsisIterator(key, false);
169 while (iter.hasNext())
170 {
171 Content content = (Content) iter.next();
172 div.addContent(content);
173 }
174 }
175 else
176 {
177 Element table = OSISUtil.factory().createTable();
178 Element row = OSISUtil.factory().createRow();
179 Element cell = null;
180
181 table.addContent(row);
182
183 Iterator[] iters = new Iterator[books.length];
184 boolean[] showDiffs = new boolean[books.length - 1];
185 boolean doDiffs = false;
186
187 for (int i = 0; i < books.length; i++)
188 {
189 Book book = books[i];
190
191 cell = OSISUtil.factory().createHeaderCell();
192
193 if (i > 0)
194 {
195 Book prevBook = books[i - 1];
196 BookCategory category = book.getBookCategory();
197
198 BookCategory prevCategory = prevBook.getBookCategory();
199 String prevName = prevBook.getInitials();
200 showDiffs[i - 1] = comparingBooks
201 && BookCategory.BIBLE.equals(category)
202 && category.equals(prevCategory)
203 && book.getLanguage().equals(prevBook.getLanguage())
204 && !book.getInitials().equals(prevName);
205
206 if (showDiffs[i - 1])
207 {
208 doDiffs = true;
209 StringBuffer buf = new StringBuffer(prevBook.getInitials());
210 buf.append(" ==> "); //$NON-NLS-1$
211 buf.append(book.getInitials());
212
213 cell.addContent(OSISUtil.factory().createText(buf.toString()));
214 row.addContent(cell);
215 cell = OSISUtil.factory().createHeaderCell();
216 }
217 }
218
219 cell.addContent(OSISUtil.factory().createText(book.getInitials()));
220 row.addContent(cell);
221
222 iters[i] = book.getOsisIterator(key, true);
223 }
224
225 Content content = null;
226
227 int cellCount = 0;
228 int rowCount = 0;
229 while (true)
230 {
231 cellCount = 0;
232
233 row = OSISUtil.factory().createRow();
234
235 String lastText = ""; //$NON-NLS-1$
236
237 for (int i = 0; i < iters.length; i++)
238 {
239 Book book = books[i];
240 cell = OSISUtil.factory().createCell();
241 Language lang = (Language) book.getProperty(BookMetaData.KEY_XML_LANG);
242 cell.setAttribute(OSISUtil.OSIS_ATTR_LANG, lang.getCode(), Namespace.XML_NAMESPACE);
243 row.addContent(cell);
244 if (iters[i].hasNext())
245 {
246 content = (Content) iters[i].next();
247
248 if (doDiffs)
249 {
250 String thisText = ""; //$NON-NLS-1$
251 if (content instanceof Element)
252 {
253 thisText = OSISUtil.getCanonicalText((Element) content);
254 }
255 else if (content instanceof Text)
256 {
257 thisText = ((Text) content).getText();
258 }
259
260 if (i > 0 && showDiffs[i - 1])
261 {
262 List diffs = new Diff(lastText, thisText, false).compare();
263 DiffCleanup.cleanupSemantic(diffs);
264 cell.addContent(OSISUtil.diffToOsis(diffs));
265
266 // Since we used that cell create another
267 cell = OSISUtil.factory().createCell();
268 lang = (Language) book.getProperty(BookMetaData.KEY_XML_LANG);
269 cell.setAttribute(OSISUtil.OSIS_ATTR_LANG, lang.getCode(), Namespace.XML_NAMESPACE);
270 row.addContent(cell);
271 }
272 lastText = thisText;
273 }
274 cell.addContent(content);
275 cellCount++;
276 }
277 }
278
279 if (cellCount == 0)
280 {
281 break;
282 }
283
284 table.addContent(row);
285 rowCount++;
286 }
287 if (rowCount > 0)
288 {
289 div.addContent(table);
290 }
291 }
292
293 return div;
294 }
295
296 /**
297 * What key was used to create this data
298 */
299 private Key key;
300
301 /**
302 * The books to which the key should be applied.
303 */
304 private Book[] books;
305
306 /**
307 * Whether the Books should be compared.
308 */
309 private boolean comparingBooks;
310
311 /**
312 * The complete OSIS container for the element
313 */
314 private Element osis;
315
316 /**
317 * Just the element
318 */
319 private Element fragment;
320 }
321