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.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     // 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     /**
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()); // this does an assertCanWrite
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     // this isn't as efficient as it could be but doesn't happen often
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    * Completely overrides Unit.encache
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    * @see org.melati.util.Treeable#getChildren()
514    */
515   public Treeable[] children = null;
516    
517   public Treeable[] getChildren() {
518     return children;
519   }
520   /* 
521    * @see org.melati.util.Treeable#getName()
522    */
523   public String getName() {
524     return getDisplayName();
525   }
526 
527 }