1 package org.paneris.bibliomania;
2
3 import java.io.BufferedOutputStream;
4 import java.io.BufferedWriter;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.FileWriter;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.InputStreamReader;
12 import java.io.Reader;
13 import java.io.Writer;
14 import java.sql.Timestamp;
15 import java.util.Enumeration;
16 import java.util.Hashtable;
17 import java.util.Map;
18
19 import org.melati.Melati;
20 import org.melati.admin.AdminSpecialised;
21 import org.melati.poem.AccessPoemException;
22 import org.melati.poem.CachedSelection;
23 import org.melati.poem.Capability;
24 import org.melati.poem.PoemThread;
25 import org.melati.poem.Table;
26 import org.melati.template.MarkupLanguage;
27 import org.melati.poem.util.EmptyEnumeration;
28 import org.melati.poem.util.IntegerEnumeration;
29 import org.melati.util.IoUtils;
30 import org.melati.poem.Treeable;
31 import org.melati.util.UnexpectedExceptionException;
32 import org.paneris.bibliomania.fti.FTIException;
33 import org.paneris.bibliomania.fti.Text;
34 import org.paneris.bibliomania.generated.ChapterBase;
35 import org.paneris.bibliomania.pagination.Pagination;
36 import org.webmacro.WebMacroException;
37
38 import com.sleepycat.db.DatabaseException;
39
40 public class Chapter extends ChapterBase implements Text, AdminSpecialised {
41 public Chapter() {
42 }
43
44 private int pageNumber;
45
46 public int getPageNumber() {
47 return pageNumber;
48 }
49 public int getPreviousPageNumber() {
50 return pageNumber - 1;
51 }
52 public int getNextPageNumber() {
53 return pageNumber + 1;
54 }
55 private CachedSelection previous = null;
56
57 public Chapter getPrevious() {
58 if (previous == null) {
59 ChapterTable t = getChapterTable();
60
61 String whereClause =
62 t.getBookColumn().eqClause(getBook().troid())
63 + " AND "
64 + t.getSequenceColumn().eqClause(
65 new Integer(getSequence().intValue() - 1));
66
67 previous = new CachedSelection(t, whereClause, null);
68 }
69
70 return (Chapter)previous.firstObject();
71 }
72
73 private CachedSelection next = null;
74
75 public Chapter getNext() {
76 if (next == null) {
77 ChapterTable t = getChapterTable();
78
79 String whereClause =
80 t.getBookColumn().eqClause(getBook().troid())
81 + " AND "
82 + t.getSequenceColumn().eqClause(
83 new Integer(getSequence().intValue() + 1));
84
85 next = new CachedSelection(t, whereClause, null);
86 }
87
88 return (Chapter)next.firstObject();
89 }
90
91 public Advert getAdvert() {
92 return getBook().getAdvert();
93 }
94 public Layout getLayout() {
95 return getBook().getLayout();
96 }
97
98 public Boolean getPaginated() {
99 return getBook().getPaginated();
100 }
101
102 public String getFilepath() {
103 return getPath() + getFilename();
104 }
105
106 public void setFilepath(String path, String filename) {
107 setPath(path.charAt(path.length() - 1) == '/' ? path : path + '/');
108 setFilename(filename);
109 }
110
111 public File originalBodyFile() {
112 return new File(
113 getBibliomaniaDatabase().getContentRootDir(),
114 getFilepath());
115 }
116
117 public InputStream originalBody() throws IOException {
118 InputStream it = new FileInputStream(originalBodyFile());
119 PoemThread.toTidy().add(it);
120 return it;
121 }
122
123 public void appendCacheSubpath(StringBuffer buffer) {
124 getBook().appendCacheSubpath(buffer);
125 buffer.append('/');
126 buffer.append(troid());
127 }
128
129 public String getCachePath(int pageP) {
130 StringBuffer buffer = new StringBuffer();
131 appendCachePath(buffer);
132 buffer.append('/');
133 buffer.append(pageP);
134 buffer.append(".html");
135 return buffer.toString();
136 }
137
138 public File allPagedFile() {
139 return new File(getCachePath(), "all.html");
140 }
141
142 public InputStream bodyUnsafe() throws IOException {
143 InputStream it = new FileInputStream(allPagedFile());
144 PoemThread.toTidy().add(it);
145 return it;
146 }
147
148 public InputStream body() throws IOException {
149 Capability canReadContent = getBook().getCanReadContent();
150 if (canReadContent != null
151 && !PoemThread.accessToken().givesCapability(canReadContent))
152 throw new ContentAccessPoemException(
153 getBook(),
154 PoemThread.accessToken(),
155 canReadContent);
156
157 return bodyUnsafe();
158 }
159
160 public InputStream bodyForFragment() throws IOException {
161 return bodyUnsafe();
162 }
163
164 public String bodyText() throws IOException {
165 return new String(IoUtils.slurp(body(), 4096));
166 }
167
168 public static final int ftiTextID_section_shift = 32,
169 ftiTextID_section_limit = 256,
170 ftiTextID_author_shift = 21,
171 ftiTextID_author_limit = 2048,
172 ftiTextID_book_shift = 10,
173 ftiTextID_book_limit = 2048,
174 ftiTextID_sequence_shift = 0,
175 ftiTextID_chapter_limit = 1024;
176
177 public static final long ftiTextID_section_mask =
178 ~((1l << ftiTextID_section_shift) - 1),
179 ftiTextID_author_mask =
180 ((1l << ftiTextID_section_shift) - 1)
181 & ~((1l << ftiTextID_author_shift) - 1),
182 ftiTextID_book_mask =
183 ((1l << ftiTextID_author_shift) - 1)
184 & ~((1l << ftiTextID_book_shift) - 1),
185 ftiTextID_sequence_mask = (1l << ftiTextID_book_shift) - 1;
186
187 public static long ftiTextID(int section, int author, int book, int seq) {
188
189
190
191
192 return (((long)section) << ftiTextID_section_shift)
193 | (((long)author) << ftiTextID_author_shift)
194 | (((long)book) << ftiTextID_book_shift)
195 | (((long)seq) << ftiTextID_sequence_shift);
196 }
197
198 public long ftiTextID() {
199 return ftiTextID(
200 getBook().getSectionTroid().intValue(),
201 getBook().getAuthorTroid().intValue(),
202 getBook().getAuthorsequence().intValue(),
203 sequence.intValue());
204 }
205
206 public long ftiTextID_start() {
207 return ftiTextID();
208 }
209
210 public long ftiTextID_limit() {
211 return ftiTextID() + (1L << ftiTextID_sequence_shift);
212 }
213
214 public Unit getParentUnit() {
215 return getBook();
216 }
217
218 void notifyChangedTextID() {
219 Long textidL = this.textid;
220 if (book != null && sequence != null && textidL != null) {
221 if (textidL.longValue() != ftiTextID()) {
222 try {
223 getBibliomaniaDatabase().fti().unIndex(textidL.longValue());
224 } catch (Exception e) {
225 throw new FTIException(e);
226 }
227
228 setTextid(null);
229 }
230 }
231 }
232
233 public void setBook(Book book) throws AccessPoemException {
234 super.setBook(book);
235 notifyChangedTextID();
236 }
237
238 public class TooManyChaptersException extends SequenceFTILimitsException {
239 private static final long serialVersionUID = 1L;
240
241 public TooManyChaptersException(int seq, int limit) {
242 super(seq, limit);
243 }
244
245 public Table getTable() {
246 return getChapterTable();
247 }
248
249 public String getParentTable() {
250 return "book";
251 }
252 }
253
254 public void setSequence(Integer sequence) throws AccessPoemException {
255 if (sequence != null && sequence.intValue() >= ftiTextID_chapter_limit)
256 throw new TooManyChaptersException(
257 sequence.intValue(),
258 ftiTextID_chapter_limit);
259 super.setSequence(sequence);
260 notifyChangedTextID();
261 }
262
263 public static class PageBody {
264 public Object textStringOrReader;
265 public Integer nextPage;
266
267 public PageBody(Reader text, Integer nextPage) {
268 textStringOrReader = text;
269 this.nextPage = nextPage;
270 }
271
272 public PageBody(InputStream text, Integer nextPage) {
273 this(new InputStreamReader(text), nextPage);
274 }
275
276 public PageBody(String text, Integer nextPage) {
277 textStringOrReader = text;
278 this.nextPage = nextPage;
279 }
280 }
281
282 public PageBody bodyTextOfPage(Pagination.PageSpan span) throws IOException {
283 InputStream whole = body();
284 whole.skip(span.startOffset);
285 byte[] stuff = new byte[span.endOffset - span.startOffset];
286
287 return new PageBody(
288 new String(stuff, 0, whole.read(stuff, 0, stuff.length)),
289 span.number + 2 > span.totalPages ? null : new Integer(span.number + 2));
290 }
291
292 public Pagination.PageSpan spanOfPage(int pageP) {
293 try {
294 return getBibliomaniaDatabase()
295 .pagination()
296 .new PageFinder()
297 .pageOfNumber(
298 ftiTextID(),
299 pageP - 1);
300 } catch (DatabaseException e) {
301 throw new UnexpectedExceptionException(e);
302 }
303 }
304
305 public Pagination.PageSpan spanOfPage(Integer pageP) {
306 return spanOfPage(pageP.intValue());
307 }
308
309 public PageBody bodyTextOfPage(Integer pageP) throws IOException {
310 if (pageP == null || Boolean.FALSE.equals(getPaginated()))
311 return new PageBody(body(), null);
312 else {
313 Pagination.PageSpan span = spanOfPage(pageP);
314 return span == null ? new PageBody(body(), null) : bodyTextOfPage(span);
315 }
316 }
317
318 public int totalPages() {
319 try {
320 return getBibliomaniaDatabase().pagination().new PageFinder().totalPages(ftiTextID());
321 } catch (NullPointerException e) {
322 return 1;
323 } catch (Exception e) {
324 throw new UnexpectedExceptionException(e);
325 }
326 }
327
328 public Enumeration pageNumbers() {
329 return new IntegerEnumeration(1, totalPages());
330 }
331
332 public Enumeration getMembersSlowly() {
333 return new EmptyEnumeration();
334 }
335
336 public String getDisplayName() {
337 return getTitle();
338 }
339
340 public void index() throws IOException {
341 try {
342 setTextid(ftiTextID());
343 getBibliomaniaDatabase().fti().index(this);
344 } catch (IOException e) {
345 throw e;
346 } catch (Exception e) {
347 throw new FTIException(e);
348 }
349 }
350
351 public void unIndex() {
352 Long tid = getTextid();
353 if (tid != null)
354 try {
355 getBibliomaniaDatabase().fti().unIndex(tid.longValue());
356 } catch (Exception e) {
357 throw new UnexpectedExceptionException(e);
358 }
359 }
360
361 public void delete_unsafe_butCleanFTI() {
362 unIndex();
363 super.delete_unsafe();
364 }
365
366 public void delete(Map integrityFixOfColumn) {
367 unIndex();
368 super.delete(integrityFixOfColumn);
369 }
370
371 public void splitOutPages()
372 throws WebMacroException, IOException {
373
374
375
376 for (int p = 1; p <= totalPages(); ++p) {
377 pageNumber = p;
378 File file = new File(getCachePath(p));
379 file.getParentFile().mkdirs();
380 BufferedOutputStream w =
381 new BufferedOutputStream(new FileOutputStream(file));
382 try {
383 Hashtable<String, Object> extras = new Hashtable<String, Object>();
384 extras.put("pageNumber", new Integer(p));
385 extras.put("content", bodyTextOfPage(spanOfPage(p)).textStringOrReader);
386 getBibliomaniaDatabase().interpolateTemplateToFile("page.wm", file, this, extras);
387 } finally {
388 try {
389 w.close();
390 } catch (Exception e) {
391 throw new RuntimeException(e);
392 }
393 }
394
395 BibliomaniaDatabase.notifyNewContentFile(file);
396 }
397 }
398
399 public void paginate() throws Exception {
400 System.err.println("Paginating chapter");
401 assertCanWrite();
402
403 BibliomaniaDatabase db = getBibliomaniaDatabase();
404
405 File allPaged = allPagedFile();
406 allPaged.getParentFile().mkdirs();
407
408 final File temp = File.createTempFile("Chapter.encache", null, null);
409
410 try {
411 db.interpolateAsTemplate(originalBodyFile(), temp, this);
412
413 final long textID = ftiTextID();
414
415 Text unpaged = new Text() {
416 public InputStream body() throws IOException {
417 FileInputStream it = new FileInputStream(temp);
418 PoemThread.toTidy().add(it);
419 return it;
420 }
421
422 public InputStream bodyForFragment() throws IOException {
423 return body();
424 }
425
426 public long ftiTextID() {
427 return textID;
428 }
429 };
430
431 Writer allPagedW = null;
432 try {
433 allPagedW = new BufferedWriter(new FileWriter(allPaged));
434 db.pagination().paginate(
435 unpaged,
436 db.getPaginationTexHeader(),
437 allPagedW,
438 db.getFootnoteTemplate(),
439 db.getWebMacro(),
440 db.getContentEncoding());
441 } finally {
442 try {
443 allPagedW.close();
444 } catch (Exception e) {
445 throw new RuntimeException(e);
446 }
447 }
448 } finally {
449 temp.delete();
450 }
451 }
452
453 public void encache() throws Exception {
454 assertCanWrite();
455 splitOutPages();
456 setLastencached(new Timestamp(System.currentTimeMillis()));
457 }
458
459 public String getMetatag_description() {
460 return getBook().getMetatag_description();
461 }
462
463 public String getMetatag_keywords() {
464 return getBook().getMetatag_keywords();
465 }
466 public String getLongTitle() {
467 String titleCandidate = getLongtitle();
468 return titleCandidate == null ? getTitle() : titleCandidate;
469 }
470
471 public Integer pageOfAnchor(String anchor) {
472 try {
473 BibliomaniaDatabase db = getBibliomaniaDatabase();
474
475 String blockmarker =
476 db.fti().blockmarkerBeforeFirstOccurrence(ftiTextID(), '#' + anchor);
477
478 return blockmarker == null ? null : db.pageFromAnchor(blockmarker);
479 } catch (DatabaseException e) {
480 throw new UnexpectedExceptionException(e);
481 }
482 }
483
484 public String adminHandle(Melati melati, MarkupLanguage markupLanguage)
485 throws Exception {
486 return null;
487 }
488
489 public String adminSpecialFacilities(
490 Melati melatiContext,
491 MarkupLanguage markupLanguage)
492 throws Exception {
493 return getBibliomaniaDatabase().getWMTemplet("Chapter-specials");
494 }
495
496 public SectionGroup getReadArea() {
497 return getBook().getReadArea();
498 }
499
500 public Enumeration<ProductAssociation> getProductAssociations() {
501 return new EmptyEnumeration<ProductAssociation>();
502 }
503
504
505
506
507 public Treeable[] children = null;
508
509 public Treeable[] getChildren() {
510 return children;
511 }
512
513
514
515 public String getName() {
516 return getDisplayName();
517 }
518
519 }