View Javadoc
1   /*
2    * #%L
3    * JRst :: Site util
4    * %%
5    * Copyright (C) 2012 CodeLutin
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  package org.nuiton.jrst;
23  
24  import java.io.File;
25  import java.io.FileNotFoundException;
26  import java.io.IOException;
27  import java.io.StringWriter;
28  import java.io.UnsupportedEncodingException;
29  import java.io.Writer;
30  import java.lang.reflect.Field;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.LinkedList;
34  import java.util.List;
35  import java.util.Locale;
36  import java.util.Map;
37  
38  import org.apache.commons.collections.CollectionUtils;
39  import org.apache.commons.io.FileUtils;
40  import org.apache.commons.lang3.StringUtils;
41  import org.apache.maven.doxia.parser.Parser;
42  import org.apache.maven.doxia.site.decoration.Menu;
43  import org.apache.maven.doxia.site.decoration.MenuItem;
44  import org.apache.maven.doxia.siterenderer.DefaultSiteRenderer;
45  import org.apache.maven.doxia.siterenderer.DocumentRenderer;
46  import org.apache.maven.doxia.siterenderer.Renderer;
47  import org.apache.maven.doxia.siterenderer.RendererException;
48  import org.apache.maven.doxia.siterenderer.RenderingContext;
49  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
50  import org.apache.maven.project.MavenProject;
51  import org.apache.velocity.VelocityContext;
52  import org.apache.velocity.app.VelocityEngine;
53  import org.apache.velocity.context.Context;
54  import org.codehaus.plexus.component.annotations.Component;
55  import org.codehaus.plexus.component.annotations.Requirement;
56  import org.codehaus.plexus.i18n.I18N;
57  import org.codehaus.plexus.velocity.VelocityComponent;
58  import org.dom4j.Document;
59  
60  import com.google.common.collect.Maps;
61  
62  /**
63   * Override of the DefaultSiteRenderer to add pdf generation of the site.
64   *
65   * @author jpages <pages@codelutin.com>
66   * @author tchemit <chemit@codelutin.com>
67   * @since 2.0
68   */
69  @Component(role = Renderer.class, hint = "default")
70  public class JrstSiteRenderer extends DefaultSiteRenderer {
71  
72      public static final String DEFAULT_PDF_FILENAME = "site";
73  
74      /** All locales used to render sites. */
75      protected List<Locale> locales;
76  
77      /** Current locale used to render. */
78      protected Locale locale;
79  
80      /** Current maven project to render. */
81      protected MavenProject mavenProject;
82  
83      protected boolean pdfGenerationEnabled;
84  
85      protected boolean verbose;
86  
87      @Requirement(role = Parser.class, hint = "jrst", optional = true)
88      protected AbstractJrstParser jrstParser;
89  
90      @Override
91      public void render(Collection<DocumentRenderer> documents,
92                         SiteRenderingContext siteRenderingContext,
93                         File outputDirectory)
94              throws RendererException, IOException {
95  
96          // prepare all common values for a given module
97  
98          locale = siteRenderingContext.getLocale();
99  
100         locales = Collections.unmodifiableList(siteRenderingContext.getSiteLocales());
101 
102         Map<String, ?> templateProperties = siteRenderingContext.getTemplateProperties();
103 
104         mavenProject = (MavenProject) templateProperties.get("project");
105 
106         verbose = getBooleanProperty("site.verbose") ||
107                   getLogger().isDebugEnabled();
108 
109         pdfGenerationEnabled = getBooleanProperty("pdfGenerationEnabled");
110 
111         super.render(documents, siteRenderingContext, outputDirectory);
112 
113         if (pdfGenerationEnabled) {
114 
115             // Get relative paths of documents from menu definition (from site.xml model)
116             List<String> relativPathsFromMenu =
117                     getDocumentRelativePathsFromMenu(siteRenderingContext);
118 
119             // Get relatifs map of all documents
120             Map<String, String> relatifPaths = buildDocMap(documents);
121 
122             // merge to have documents refs (to be used in pdf template)
123             List<String> documentRefs = getDocumentRefs(relatifPaths,
124                                                         relativPathsFromMenu);
125 
126             if (CollectionUtils.isEmpty(documentRefs)) {
127                 // no docuemnts to render, skip pdf generation
128 
129                 if (getLogger().isInfoEnabled()) {
130                     getLogger().info("No document to put in pdf, skip pdf generation.");
131                 }
132             } else {
133                 String pdfFilename = getPdfFileName();
134 
135                 try {
136                     Field i18nField = ReflectUtil.getField(DefaultSiteRenderer.class, "i18n");
137                     I18N i18n = (I18N) i18nField.get(this);
138 
139                     renderPdf(i18n, documentRefs,
140                               siteRenderingContext,
141                               outputDirectory,
142                               pdfFilename);
143                 } catch (Exception e) {
144                     getLogger().error("Can't find rst resource to generate pdf documentation", e);
145                 }
146             }
147 
148         }
149     }
150 
151     @Override
152     public void renderDocument(Writer writer,
153                                RenderingContext renderingContext,
154                                SiteRenderingContext context)
155             throws RendererException, FileNotFoundException, UnsupportedEncodingException {
156 
157         if (AbstractJrstParser.JRST_PARSER_ID.equals(renderingContext.getParserId())) {
158 
159             // let's keep the current rendering context into the JrstParser
160             // to be able to have the document name to parse
161             jrstParser.setRenderingContext(renderingContext);
162             jrstParser.setMavenProject(mavenProject);
163             jrstParser.setVerbose(verbose);
164 
165         }
166         super.renderDocument(writer, renderingContext, context);
167     }
168 
169     protected String getPdfFileName() {
170         // get pdfFilename
171         String pdfFilename = getStringProperty("pdfFilename");
172         if (StringUtils.isBlank(pdfFilename)) {
173             pdfFilename = DEFAULT_PDF_FILENAME;
174         }
175 
176         if (!locales.isEmpty() && !locale.equals(locales.get(0))) {
177             // not default locale, prefix with locale country.
178             pdfFilename += "_" + locale.getCountry();
179         }
180         pdfFilename += ".pdf";
181         return pdfFilename;
182     }
183 
184     protected void renderPdf(I18N i18n,
185                              List<String> documentRefs,
186                              SiteRenderingContext siteRenderingContext,
187                              File outputDirectory,
188                              String pdfFilename)
189             throws RendererException, FileNotFoundException, UnsupportedEncodingException {
190 
191         if (getLogger().isDebugEnabled()) {
192             getLogger().debug("Generate Site Pdf: " + pdfFilename);
193         }
194 
195         Context vc = preparePdfVelocityContext(i18n, documentRefs);
196 
197         // Define the output file
198         File fileOut = new File(outputDirectory, pdfFilename);
199         if (verbose) {
200             getLogger().info("Will generate the site pdf: " + fileOut);
201         }
202 
203         try {
204             StringWriter sw = new StringWriter();
205             Field velocityField =
206                     ReflectUtil.getField(DefaultSiteRenderer.class, "velocity");
207 
208             VelocityComponent velocity = (VelocityComponent) velocityField.get(this);
209             VelocityEngine engine = velocity.getEngine();
210 
211             String inputEncoding = siteRenderingContext.getInputEncoding();
212 
213             // Aggregate all rst file info one file
214             //TODO tchemit-2012-06-29 : use a property
215             engine.mergeTemplate("/META-INF/maven/RstAggregation.vm", inputEncoding, vc, sw);
216             String fileContent = sw.toString();
217             File pdfDoc = new File(outputDirectory, "pdfDoc.rst");
218             FileUtils.writeStringToFile(pdfDoc, fileContent, inputEncoding);
219 
220 
221             Document doc = jrstParser.getStrategy().generateRstToXml(
222                     fileOut, inputEncoding);
223 
224             // Generate the pdf file
225             JRST.generatePdf(pdfDoc, fileOut, JRST.Overwrite.ALLTIME, doc);
226 
227         } catch (Exception e) {
228             getLogger().error("Can't generate pdf documentation of the project at " + fileOut, e);
229         }
230     }
231 
232     protected Context preparePdfVelocityContext(I18N i18n,
233                                                 List<String> documentRefs) {
234 
235         Context vc = new VelocityContext();
236         vc.put("locale", locale);
237         vc.put("supportedLocales", locales);
238         String tableOfContent = i18n.getString("jrst-site-renderer", locale, "content");
239         String docName = mavenProject.getName();
240         String titleDecoration = StringUtils.rightPad("", docName.length(), '=');
241         vc.put("titleDecoration", titleDecoration);
242         vc.put("docName", docName);
243         vc.put("tableContentName", tableOfContent);
244 
245         // Build the map of html documents from .rst or .rst.vm files
246 
247         vc.put("basedir", mavenProject.getBasedir());
248         vc.put("separator", File.separatorChar);
249         vc.put("documentRefs", documentRefs);
250         return vc;
251     }
252 
253     protected List<String> getDocumentRelativePathsFromMenu(SiteRenderingContext siteRenderingContext) {
254         List<String> paths = new LinkedList<String>();
255         for (Menu menu : siteRenderingContext.getDecoration().getMenus()) {
256             for (MenuItem item : menu.getItems()) {
257                 buildListPathsFromMenuItem(item, paths);
258             }
259         }
260         return paths;
261     }
262 
263     protected List<String> getDocumentRefs(Map<String, String> relatifPaths,
264                                            List<String> relativPathsFromMenu) {
265 
266         // Merge the map and the list to obtain a list of rst files we can add in the pdf
267         List<String> documentRefs = new LinkedList<String>();
268         for (String path : relativPathsFromMenu) {
269             // Seek in the hashmap if the rst file exists to add it in the doc list
270             String rstFilename = relatifPaths.get(path);
271             if (rstFilename != null) {
272                 if (verbose) {
273                     getLogger().info("Add document to render in pdf: " +
274                                      rstFilename);
275                 }
276                 documentRefs.add(rstFilename);
277             }
278         }
279         return documentRefs;
280     }
281 
282     protected Map<String, String> buildDocMap(Collection<DocumentRenderer> documents) {
283 
284         Map<String, String> map = Maps.newTreeMap();
285 
286         for (DocumentRenderer doc : documents) {
287             RenderingContext renderingContext = doc.getRenderingContext();
288             String inputName = renderingContext.getInputName();
289 //            String relativeDir = renderingContext.getRelativePath();
290             if (inputName.endsWith("rst") || inputName.endsWith("rst.vm")) {
291                 // Change the extension to html
292                 String[] splitName = inputName.split("\\.");
293                 String htmlName = splitName[0] + "." + "html";
294                 String relativePath = "." + File.separator + htmlName;
295                 if (getLogger().isDebugEnabled()) {
296                     getLogger().debug("relativePath: " + relativePath);
297                 }
298                 // Add in map
299                 map.put(relativePath, inputName);
300 
301                 //TODO Why ?
302 //                if (relativeDir.equals(".") && inputName.startsWith("index")) {
303 //                    basedir = renderingContext.getBasedir();
304 //                }
305             }
306         }
307         return map;
308     }
309 
310     protected Collection<String> buildListPathsFromMenuItem(MenuItem item,
311                                                             Collection<String> paths) {
312         // add hrefs to paths if they don't start with "http" or ".."
313         String href = item.getHref();
314         if (!href.startsWith("http") && !href.startsWith("..")) {
315             boolean addHref = true;
316             for (Locale loc : locales) {
317                 if (href.startsWith(loc.toString())) {
318                     addHref = false;
319                 }
320             }
321             if (addHref) {
322                 if (!href.startsWith("./")) {
323                     href = "./" + href;
324                 }
325                 if (getLogger().isDebugEnabled()) {
326                     getLogger().debug("document to render in pdf: " + href);
327                 }
328                 paths.add(href);
329             }
330         }
331         // If the item has sub-items, we use recursion to add them to paths
332         List<MenuItem> subItems = item.getItems();
333         if (!subItems.isEmpty()) {
334             for (MenuItem subItem : subItems) {
335                 paths = buildListPathsFromMenuItem(subItem, paths);
336             }
337         }
338         return paths;
339     }
340 
341     protected String getStringProperty(String propertyName) {
342         Object value = mavenProject.getProperties().get(propertyName);
343         String result = value == null ? null : String.valueOf(value);
344         return result;
345     }
346 
347     protected boolean getBooleanProperty(String propertyName) {
348         boolean result = "true".equals(getStringProperty(propertyName));
349         return result;
350     }
351 
352 }