View Javadoc

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     // section author book seq
190     //    8      11    11   10
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; //FIXME Sticking plaster, I have no idea what is going on here, just stop the bleeding 
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()); // this does an assertCanWrite
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     // this isn't as efficient as it could be but doesn't happen often
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    * @see org.melati.util.Treeable#getChildren()
506    */
507   public Treeable[] children = null;
508    
509   public Treeable[] getChildren() {
510     return children;
511   }
512   /* 
513    * @see org.melati.util.Treeable#getName()
514    */
515   public String getName() {
516     return getDisplayName();
517   }
518 
519 }