View Javadoc

1   package org.apache.maven.doxia.book.services.renderer;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.IOException;
25  import java.io.Reader;
26  import java.io.Writer;
27  import java.util.Iterator;
28  import java.util.Locale;
29  
30  import org.apache.maven.doxia.Doxia;
31  import org.apache.maven.doxia.book.BookDoxiaException;
32  import org.apache.maven.doxia.book.context.BookContext;
33  import org.apache.maven.doxia.book.model.BookModel;
34  import org.apache.maven.doxia.book.model.Chapter;
35  import org.apache.maven.doxia.book.model.Section;
36  import org.apache.maven.doxia.book.services.renderer.xdoc.ChapterXdocBookSink;
37  import org.apache.maven.doxia.book.services.renderer.xdoc.IndexXdocBookSink;
38  import org.apache.maven.doxia.book.services.renderer.xdoc.SectionXdocBookSink;
39  import org.apache.maven.doxia.index.IndexEntry;
40  import org.apache.maven.doxia.module.xdoc.XdocSink;
41  import org.apache.maven.doxia.parser.ParseException;
42  import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
43  import org.apache.maven.doxia.util.HtmlTools;
44  import org.codehaus.plexus.i18n.I18N;
45  import org.codehaus.plexus.logging.AbstractLogEnabled;
46  import org.codehaus.plexus.util.IOUtil;
47  import org.codehaus.plexus.util.ReaderFactory;
48  import org.codehaus.plexus.util.StringUtils;
49  import org.codehaus.plexus.util.WriterFactory;
50  
51  /**
52   * An implementation of <code>BookRenderer</code> for Xdoc
53   *
54   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
55   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
56   * @version $Id: XdocBookRenderer.java 1090706 2011-04-09 23:15:28Z hboutemy $
57   * @plexus.component role-hint="xdoc"
58   */
59  public class XdocBookRenderer
60      extends AbstractLogEnabled
61      implements BookRenderer
62  {
63      /**
64       * @plexus.requirement
65       */
66      private Doxia doxia;
67  
68      /**
69       * @plexus.requirement
70       */
71      private I18N i18n;
72  
73      // ----------------------------------------------------------------------
74      // BookRenderer Implementation
75      // ----------------------------------------------------------------------
76  
77      /** {@inheritDoc} */
78      public void renderBook( BookContext context )
79          throws BookDoxiaException
80      {
81          BookModel book = context.getBook();
82  
83          if ( !context.getOutputDirectory().exists() )
84          {
85              if ( !context.getOutputDirectory().mkdirs() )
86              {
87                  throw new BookDoxiaException( "Could not make directory: "
88                      + context.getOutputDirectory().getAbsolutePath() + "." );
89              }
90          }
91  
92          renderBook( book, context );
93      }
94  
95      // -----------------------------------------------------------------------
96      // Protected
97      // -----------------------------------------------------------------------
98  
99      /**
100      * Gets a trimmed String for the given key from the resource bundle defined by Plexus.
101      *
102      * @param locale the locale used
103      * @param key the key for the desired string
104      * @return the string for the given key and the given locale
105      */
106     protected String getString( Locale locale, String key )
107     {
108         if ( StringUtils.isEmpty( key ) )
109         {
110             throw new IllegalArgumentException( "The key cannot be empty" );
111         }
112 
113         return i18n.getString( "book-renderer", locale, key ).trim();
114     }
115 
116     // -----------------------------------------------------------------------
117     // Private
118     // -----------------------------------------------------------------------
119 
120     /**
121      * Render the book, ie the book index and all chapter index and pages
122      *
123      * @param book the BookModel.
124      * @param context the BookContext.
125      * @throws BookDoxiaException if any
126      */
127     private void renderBook( BookModel book, BookContext context )
128         throws BookDoxiaException
129     {
130         // -----------------------------------------------------------------------
131         // Render the book index.xml page
132         // -----------------------------------------------------------------------
133 
134         File index = new File( context.getOutputDirectory(), "index.xml" );
135 
136         try
137         {
138             writeBookIndex( index, book, context );
139         }
140         catch ( IOException e )
141         {
142             throw new BookDoxiaException( "Error while rendering index page to: '"
143                         + index.getAbsolutePath() + "'.", e );
144         }
145 
146         // -----------------------------------------------------------------------
147         // Render all the chapter pages
148         // -----------------------------------------------------------------------
149 
150         Iterator<IndexEntry> ii = context.getIndex().getChildEntries().iterator();
151 
152         for ( Chapter chapter : book.getChapters() )
153         {
154             renderChapter( chapter, context, ii.next() );
155         }
156     }
157 
158     /**
159      * Write the book index, ie a TOC.
160      *
161      * @param index the File.
162      * @param book the BookModel.
163      * @param context the BookContext.
164      * @throws IOException if any
165      */
166     private void writeBookIndex( File index, BookModel book, BookContext context )
167         throws IOException
168     {
169         Writer writer = WriterFactory.newXmlWriter( index );
170 
171         XdocSink sink = new IndexXdocBookSink( writer, context.getIndex().getFirstEntry(), i18n, context.getLocale() );
172 
173         try
174         {
175             // -----------------------------------------------------------------------
176             // Head
177             // -----------------------------------------------------------------------
178 
179             sink.head();
180 
181             sink.title();
182             sink.text( book.getTitle() + " - " + getString( context.getLocale(), "toc" ) );
183             sink.title_();
184 
185             sink.head_();
186 
187             // -----------------------------------------------------------------------
188             // Body
189             // -----------------------------------------------------------------------
190 
191             sink.body();
192 
193             sink.section1();
194             sink.sectionTitle1();
195             sink.text( book.getTitle() + " - " + getString( context.getLocale(), "toc" ) );
196             sink.sectionTitle1_();
197 
198             sink.list();
199             for ( IndexEntry entry : context.getIndex().getChildEntries() )
200             {
201                 writeChapterIndexForBookIndex( sink, entry );
202             }
203             sink.list_();
204 
205             sink.section1_();
206 
207             sink.body_();
208         }
209         finally
210         {
211             sink.flush();
212 
213             sink.close();
214 
215             IOUtil.close( writer );
216         }
217     }
218 
219     /**
220      * Write the chapter index for the book index.
221      *
222      * @param sink the XdocSink.
223      * @param chapterEntry the chapter IndexEntry.
224      */
225     private void writeChapterIndexForBookIndex( XdocSink sink, IndexEntry chapterEntry )
226     {
227         sink.listItem();
228         sink.link( chapterEntry.getId() + ".html" );
229         sink.text( chapterEntry.getTitle() );
230         sink.link_();
231 
232         sink.list();
233         for ( IndexEntry sectionIndex : chapterEntry.getChildEntries() )
234         {
235             writeSectionIndexForBookIndex( sink, sectionIndex );
236         }
237         sink.list_();
238 
239         sink.listItem_();
240     }
241 
242     /**
243      * Write the section index for the book index.
244      *
245      * @param sink the XdocSink.
246      * @param sectionIndex the section IndexEntry.
247      */
248     private void writeSectionIndexForBookIndex( XdocSink sink, IndexEntry sectionIndex )
249     {
250         sink.listItem();
251         sink.link( sectionIndex.getId() + ".html" );
252         sink.text( sectionIndex.getTitle() );
253         sink.link_();
254 
255         sink.list();
256         for ( IndexEntry subsectionIndex : sectionIndex.getChildEntries() )
257         {
258             writeSubsectionIndexForBookIndex( sink, sectionIndex, subsectionIndex );
259         }
260         sink.list_();
261 
262         sink.listItem_();
263     }
264 
265     /**
266      * Write subsection index for the book index.
267      *
268      * @param sink the XdocSink.
269      * @param sectionIndex the section IndexEntry.
270      * @param subsectionIndex the subsection IndexEntry.
271      */
272     private void writeSubsectionIndexForBookIndex( XdocSink sink, IndexEntry sectionIndex, IndexEntry subsectionIndex )
273     {
274         sink.listItem();
275         sink.link( sectionIndex.getId() + ".html#" + HtmlTools.encodeId( subsectionIndex.getId() ) );
276         sink.text( subsectionIndex.getTitle() );
277         sink.link_();
278         sink.listItem_();
279     }
280 
281     // -----------------------------------------------------------------------
282     // Rendering
283     // -----------------------------------------------------------------------
284 
285     /**
286      * Render the chapter index and all section pages.
287      *
288      * @param chapter the Chapter.
289      * @param context the BookContext.
290      * @param chapterIndex the IndexEntry.
291      * @throws BookDoxiaException if any
292      */
293     private void renderChapter( Chapter chapter, BookContext context, IndexEntry chapterIndex )
294         throws BookDoxiaException
295     {
296         // -----------------------------------------------------------------------
297         // Render the chapter index page
298         // -----------------------------------------------------------------------
299 
300         File index = new File( context.getOutputDirectory(), chapter.getId() + ".xml" );
301 
302         try
303         {
304             writeChapterIndex( index, chapter, chapterIndex, context );
305         }
306         catch ( IOException e )
307         {
308             throw new BookDoxiaException( "Error while rendering index page to: '"
309                         + index.getAbsolutePath() + "'.", e );
310         }
311 
312         // -----------------------------------------------------------------------
313         // Render all section pages
314         // -----------------------------------------------------------------------
315 
316         Iterator<IndexEntry> ii = chapterIndex.getChildEntries().iterator();
317 
318         for ( Section section : chapter.getSections() )
319         {
320             renderSection( context, section, ii.next() );
321         }
322     }
323 
324     /**
325      * Write a chapter index
326      *
327      * @param index the File.
328      * @param context the context.
329      * @param chapter the Chapter.
330      * @param chapterIndex the IndexEntry.
331      * @throws IOException if any.
332      */
333     private void writeChapterIndex( File index, Chapter chapter, IndexEntry chapterIndex, BookContext context )
334         throws IOException
335     {
336         Writer writer = WriterFactory.newXmlWriter( index );
337 
338         ChapterXdocBookSink sink = new ChapterXdocBookSink( writer, chapterIndex, i18n, context.getLocale() );
339 
340         try
341         {
342             // -----------------------------------------------------------------------
343             // Head
344             // -----------------------------------------------------------------------
345 
346             sink.head();
347 
348             sink.title();
349             sink.text( chapter.getTitle() );
350             sink.title_();
351 
352             sink.head_();
353 
354             // -----------------------------------------------------------------------
355             // Body
356             // -----------------------------------------------------------------------
357 
358             sink.body();
359 
360             sink.section1();
361             sink.sectionTitle1();
362             sink.text( chapter.getTitle() );
363             sink.sectionTitle1_();
364 
365             sink.list();
366             for ( IndexEntry sectionIndex : chapterIndex.getChildEntries() )
367             {
368                 writeSectionIndexForBookIndex( sink, sectionIndex );
369             }
370             sink.list_();
371 
372             sink.section1_();
373 
374             sink.body_();
375         }
376         finally
377         {
378             sink.flush();
379 
380             sink.close();
381 
382             IOUtil.close( writer );
383         }
384     }
385 
386     /**
387      * Render all section pages.
388      *
389      * @param context the BookContext.
390      * @param section the Section.
391      * @param sectionIndex the IndexEntry.
392      * @throws BookDoxiaException if any.
393      */
394     private void renderSection( BookContext context, Section section, IndexEntry sectionIndex )
395         throws BookDoxiaException
396     {
397         try
398         {
399             Writer writer = WriterFactory.newXmlWriter( new File( context.getOutputDirectory()
400                     + "/" + section.getId() + ".xml" ) );
401 
402             SectionXdocBookSink sink = new SectionXdocBookSink( writer, sectionIndex, i18n, context.getLocale() );
403 
404             BookContext.BookFile bookFile = (BookContext.BookFile) context.getFiles().get( section.getId() );
405 
406             if ( bookFile == null )
407             {
408                 throw new BookDoxiaException( "No document that matches section with id=" + section.getId() + "." );
409             }
410 
411             Reader reader = null;
412             try
413             {
414                 reader = ReaderFactory.newReader( bookFile.getFile(), context.getInputEncoding() );
415                 doxia.parse( reader, bookFile.getParserId(), sink );
416             }
417             catch ( ParserNotFoundException e )
418             {
419                 throw new BookDoxiaException( "Parser not found: " + bookFile.getParserId() + ".", e );
420             }
421             catch ( ParseException e )
422             {
423                 throw new BookDoxiaException( "Error while parsing document: " + bookFile.getFile().getAbsolutePath()
424                     + ".", e );
425             }
426             catch ( FileNotFoundException e )
427             {
428                 throw new BookDoxiaException( "Could not find document: " + bookFile.getFile().getAbsolutePath() + ".",
429                                               e );
430             }
431             finally
432             {
433                 sink.flush();
434                 sink.close();
435 
436                 IOUtil.close( reader );
437                 IOUtil.close( writer );
438             }
439         }
440         catch ( IOException e )
441         {
442             throw new BookDoxiaException( "Error while rendering book.", e );
443         }
444     }
445 }