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