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.Map;
17
18 import org.melati.Melati;
19 import org.melati.admin.AdminSpecialised;
20 import org.melati.poem.AccessPoemException;
21 import org.melati.poem.CachedSelection;
22 import org.melati.poem.Capability;
23 import org.melati.poem.PoemThread;
24 import org.melati.poem.Table;
25 import org.melati.template.MarkupLanguage;
26 import org.melati.poem.util.EmptyEnumeration;
27 import org.melati.poem.util.IntegerEnumeration;
28 import org.melati.util.IoUtils;
29 import org.melati.poem.Treeable;
30 import org.melati.util.UnexpectedExceptionException;
31 import org.paneris.bibliomania.fti.FTIException;
32 import org.paneris.bibliomania.fti.Text;
33 import org.paneris.bibliomania.generated.ChapterBase;
34 import org.paneris.bibliomania.pagination.Pagination;
35 import org.webmacro.WebMacroException;
36
37 import com.sleepycat.db.DbException;
38
39 public class Chapter extends ChapterBase implements Text, AdminSpecialised {
40 public Chapter() {
41 }
42
43 private int page;
44
45 public int getPageNumber() {
46 return page;
47 }
48 public int getPreviousPageNumber() {
49 return page - 1;
50 }
51 public int getNextPageNumber() {
52 return page + 1;
53 }
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
240
241
242 private static final long serialVersionUID = 1L;
243
244 public TooManyChaptersException(int seq, int limit) {
245 super(seq, limit);
246 }
247
248 public Table getTable() {
249 return getChapterTable();
250 }
251
252 public String getParentTable() {
253 return "book";
254 }
255 }
256
257 public void setSequence(Integer sequence) throws AccessPoemException {
258 if (sequence != null && sequence.intValue() >= ftiTextID_chapter_limit)
259 throw new TooManyChaptersException(
260 sequence.intValue(),
261 ftiTextID_chapter_limit);
262 super.setSequence(sequence);
263 notifyChangedTextID();
264 }
265
266 public static class PageBody {
267 public Object textStringOrReader;
268 public Integer nextPage;
269
270 public PageBody(Reader text, Integer nextPage) {
271 textStringOrReader = text;
272 this.nextPage = nextPage;
273 }
274
275 public PageBody(InputStream text, Integer nextPage) {
276 this(new InputStreamReader(text), nextPage);
277 }
278
279 public PageBody(String text, Integer nextPage) {
280 textStringOrReader = text;
281 this.nextPage = nextPage;
282 }
283 }
284
285 public PageBody bodyTextOfPage(Pagination.PageSpan span) throws IOException {
286 InputStream whole = body();
287 whole.skip(span.startOffset);
288 byte[] stuff = new byte[span.endOffset - span.startOffset];
289
290 return new PageBody(
291 new String(stuff, 0, whole.read(stuff, 0, stuff.length)),
292 span.number + 2 > span.totalPages ? null : new Integer(span.number + 2));
293 }
294
295 public Pagination.PageSpan spanOfPage(int pageP) {
296 try {
297 return getBibliomaniaDatabase()
298 .pagination()
299 .new PageFinder()
300 .pageOfNumber(
301 ftiTextID(),
302 pageP - 1);
303 } catch (com.sleepycat.db.DbException e) {
304 throw new UnexpectedExceptionException(e);
305 }
306 }
307
308 public Pagination.PageSpan spanOfPage(Integer pageP) {
309 return spanOfPage(pageP.intValue());
310 }
311
312 public PageBody bodyTextOfPage(Integer pageP) throws IOException {
313 if (pageP == null || Boolean.FALSE.equals(getPaginated()))
314 return new PageBody(body(), null);
315 else {
316 Pagination.PageSpan span = spanOfPage(pageP);
317 return span == null ? new PageBody(body(), null) : bodyTextOfPage(span);
318 }
319 }
320
321 public int totalPages() {
322 try {
323 return getBibliomaniaDatabase().pagination().new PageFinder().totalPages(
324 ftiTextID());
325 } catch (Exception e) {
326 throw new UnexpectedExceptionException(e);
327 }
328 }
329
330 public Enumeration pageNumbers() {
331 return new IntegerEnumeration(1, totalPages());
332 }
333
334 public Enumeration getMembersSlowly() {
335 return EmptyEnumeration.it;
336 }
337
338 public String getDisplayName() {
339 return getTitle();
340 }
341
342 public void index() throws IOException {
343 try {
344 setTextid(ftiTextID());
345 getBibliomaniaDatabase().fti().index(this);
346 } catch (IOException e) {
347 throw e;
348 } catch (Exception e) {
349 throw new FTIException(e);
350 }
351 }
352
353 public void unIndex() {
354 Long tid = getTextid();
355 if (tid != null)
356 try {
357 getBibliomaniaDatabase().fti().unIndex(tid.longValue());
358 } catch (Exception e) {
359 throw new UnexpectedExceptionException(e);
360 }
361 }
362
363 public void delete_unsafe_butCleanFTI() {
364 unIndex();
365 super.delete_unsafe();
366 }
367
368 public void delete(Map integrityFixOfColumn) {
369 unIndex();
370 super.delete(integrityFixOfColumn);
371 }
372
373 public void splitOutPages()
374 throws WebMacroException, IOException {
375
376
377
378 for (int p = 1; p <= totalPages(); ++p) {
379 page = p;
380 File file = new File(getCachePath(p));
381 file.getParentFile().mkdirs();
382 BufferedOutputStream w =
383 new BufferedOutputStream(new FileOutputStream(file));
384 try {
385 getBibliomaniaDatabase().writeContentHeader(
386 w,
387 this,
388 p == 1 ? "page1Header.wm" : "pageHeader.wm");
389 w.write(
390 bodyTextOfPage(spanOfPage(p))
391 .textStringOrReader
392 .toString()
393 .getBytes());
394 getBibliomaniaDatabase().writeContentFooter(w, this, "pageFooter.wm");
395 } finally {
396 try {
397 w.close();
398 } catch (Exception e) {
399 }
400 }
401
402 BibliomaniaDatabase.notifyNewContentFile(file);
403 }
404 }
405
406
407
408
409
410 public void paginate() throws Exception {
411 assertCanWrite();
412
413 BibliomaniaDatabase db = getBibliomaniaDatabase();
414
415 File allPaged = allPagedFile();
416 allPaged.getParentFile().mkdirs();
417
418 final File temp = File.createTempFile("Chapter.encache", null, null);
419
420 try {
421 db.macroexpand(originalBodyFile(), temp, this);
422
423 final long textID = ftiTextID();
424
425 Text unpaged = new Text() {
426 public InputStream body() throws IOException {
427 FileInputStream it = new FileInputStream(temp);
428 PoemThread.toTidy().add(it);
429 return it;
430 }
431
432 public InputStream bodyForFragment() throws IOException {
433 return body();
434 }
435
436 public long ftiTextID() {
437 return textID;
438 }
439 };
440
441 Writer allPagedW = new BufferedWriter(new FileWriter(allPaged));
442 try {
443 db.pagination().paginate(
444 unpaged,
445 db.getPaginationTexHeader(),
446 allPagedW,
447 db.getFootnoteTemplate(),
448 db.getWebMacro(),
449 db.getContentEncoding());
450 } finally {
451 try {
452 allPagedW.close();
453 } catch (Exception e) {
454 }
455 }
456 } finally {
457 temp.delete();
458 }
459 }
460
461 public void encache() throws Exception {
462 assertCanWrite();
463 splitOutPages();
464 setLastencached(new Timestamp(System.currentTimeMillis()));
465 }
466
467 public String getMetatag_description() {
468 return getBook().getMetatag_description();
469 }
470
471 public String getMetatag_keywords() {
472 return getBook().getMetatag_keywords();
473 }
474 public String getLongTitle() {
475 String titleCandidate = getLongtitle();
476 return titleCandidate == null ? getTitle() : titleCandidate;
477 }
478
479 public Integer pageOfAnchor(String anchor) {
480 try {
481 BibliomaniaDatabase db = getBibliomaniaDatabase();
482
483 String blockmarker =
484 db.fti().blockmarkerBeforeFirstOccurrence(ftiTextID(), '#' + anchor);
485
486 return blockmarker == null ? null : db.pageFromAnchor(blockmarker);
487 } catch (DbException e) {
488 throw new UnexpectedExceptionException(e);
489 }
490 }
491
492 public String adminHandle(Melati melati, MarkupLanguage markupLanguage)
493 throws Exception {
494 return null;
495 }
496
497 public String adminSpecialFacilities(
498 Melati melatiContext,
499 MarkupLanguage markupLanguage)
500 throws Exception {
501 return getWMTemplet("Chapter-specials");
502 }
503
504 public SectionGroup getReadArea() {
505 return getBook().getReadArea();
506 }
507
508 public Enumeration getProductAssociations() {
509 return EmptyEnumeration.it;
510 }
511
512
513
514
515 public Treeable[] children = null;
516
517 public Treeable[] getChildren() {
518 return children;
519 }
520
521
522
523 public String getName() {
524 return getDisplayName();
525 }
526
527 }