View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.site.render;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.TreeMap;
30  
31  import org.apache.maven.doxia.siterenderer.DocumentRenderer;
32  import org.apache.maven.doxia.siterenderer.DoxiaDocumentRenderer;
33  import org.apache.maven.doxia.siterenderer.RendererException;
34  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
35  import org.apache.maven.doxia.tools.SiteTool;
36  import org.apache.maven.execution.MavenSession;
37  import org.apache.maven.plugin.MojoExecutionException;
38  import org.apache.maven.plugin.MojoFailureException;
39  import org.apache.maven.plugins.annotations.Mojo;
40  import org.apache.maven.plugins.annotations.Parameter;
41  import org.apache.maven.plugins.annotations.ResolutionScope;
42  import org.apache.maven.project.MavenProject;
43  import org.apache.maven.reporting.MavenReportException;
44  import org.apache.maven.reporting.exec.MavenReportExecution;
45  import org.apache.maven.shared.utils.logging.MessageBuilder;
46  
47  import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
48  
49  /**
50   * Generates the site for a single project.
51   * <p>
52   * Note that links between module sites in a multi module build will <b>not</b> work, since local build directory
53   * structure doesn't match deployed site.
54   * </p>
55   *
56   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
57   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
58   *
59   */
60  @Mojo(name = "site", requiresDependencyResolution = ResolutionScope.TEST, requiresReports = true, threadSafe = true)
61  public class SiteMojo extends AbstractSiteRenderingMojo {
62      /**
63       * Directory where the project sites and report distributions will be generated (as html/css/...).
64       */
65      @Parameter(property = "siteOutputDirectory", defaultValue = "${project.reporting.outputDirectory}")
66      protected File outputDirectory;
67  
68      /**
69       * Convenience parameter that allows you to disable report generation.
70       */
71      @Parameter(property = "generateReports", defaultValue = "true")
72      private boolean generateReports;
73  
74      /**
75       * Whether to validate xml input documents. If set to true, <strong>all</strong> input documents in xml format (in
76       * particular xdoc and fml) will be validated and any error will lead to a build failure.
77       *
78       * @since 2.1.1
79       */
80      @Parameter(property = "validate", defaultValue = "false")
81      private boolean validate;
82  
83      /**
84       * {@inheritDoc}
85       */
86      @Override
87      public void execute() throws MojoExecutionException, MojoFailureException {
88          if (skip) {
89              getLog().info("maven.site.skip = true: Skipping site generation");
90              return;
91          }
92  
93          if (getLog().isDebugEnabled()) {
94              getLog().debug("executing Site Mojo");
95          }
96  
97          checkInputEncoding();
98  
99          try {
100             List<Locale> localesList = getLocales();
101 
102             for (Locale locale : localesList) {
103                 getLog().info("Rendering site for "
104                         + buffer().strong(
105                                         (!locale.equals(SiteTool.DEFAULT_LOCALE)
106                                                 ? "locale '" + locale + "'"
107                                                 : "default locale"))
108                                 .build());
109                 File outputDirectory = getOutputDirectory(locale);
110                 List<MavenReportExecution> reports =
111                         generateReports ? getReports(outputDirectory) : Collections.emptyList();
112                 renderLocale(locale, reports, localesList, outputDirectory);
113             }
114         } catch (RendererException e) {
115             if (e.getCause() instanceof MavenReportException) {
116                 // issue caused by report, not really by Doxia Site Renderer
117                 throw new MojoExecutionException(e.getMessage(), e.getCause());
118             }
119             throw new MojoExecutionException("Failed to render site", e);
120         } catch (IOException e) {
121             throw new MojoExecutionException("Error during site generation", e);
122         }
123     }
124 
125     private void renderLocale(
126             Locale locale, List<MavenReportExecution> reports, List<Locale> supportedLocales, File outputDirectory)
127             throws IOException, RendererException, MojoFailureException, MojoExecutionException {
128         SiteRenderingContext context = createSiteRenderingContext(locale);
129         context.addSiteLocales(supportedLocales);
130         context.setInputEncoding(getInputEncoding());
131         context.setOutputEncoding(getOutputEncoding());
132         context.setValidate(validate);
133         if (validate) {
134             getLog().info("Validation is switched on, xml input documents will be validated!");
135         }
136 
137         // locate all Doxia documents first
138         Map<String, DocumentRenderer> documents = locateDocuments(context, reports, locale);
139 
140         // copy resources
141         siteRenderer.copyResources(context, outputDirectory);
142 
143         // and finally render Doxia documents
144         List<DocumentRenderer> nonDoxiaDocuments = renderDoxiaDocuments(documents.values(), context, outputDirectory);
145 
146         // then non-Doxia documents (e.g. reports)
147         renderNonDoxiaDocuments(nonDoxiaDocuments, context, outputDirectory);
148     }
149 
150     /**
151      * Render Doxia documents from the list given, but not reports.
152      *
153      * @param documents a collection of documents containing both Doxia source files and reports
154      * @return the sublist of documents that are not Doxia source files
155      */
156     private List<DocumentRenderer> renderDoxiaDocuments(
157             Collection<DocumentRenderer> documents, SiteRenderingContext context, File outputDirectory)
158             throws RendererException, IOException {
159         List<DocumentRenderer> doxiaDocuments = new ArrayList<>();
160         List<DocumentRenderer> generatedDoxiaDocuments = new ArrayList<>();
161         List<DocumentRenderer> nonDoxiaDocuments = new ArrayList<>();
162 
163         Map<String, Integer> counts = new TreeMap<>();
164         Map<String, Integer> generatedCounts = new TreeMap<>();
165 
166         for (DocumentRenderer doc : documents) {
167             if (doc instanceof DoxiaDocumentRenderer) {
168                 DoxiaDocumentRenderer doxia = (DoxiaDocumentRenderer) doc;
169                 boolean editable = doxia.getRenderingContext().isEditable();
170 
171                 if (editable) {
172                     doxiaDocuments.add(doc);
173                 } else {
174                     generatedDoxiaDocuments.add(doc);
175                 }
176 
177                 // count documents per parserId
178                 String parserId = doxia.getRenderingContext().getParserId();
179                 Map<String, Integer> actualCounts;
180                 if (editable) {
181                     actualCounts = counts;
182                 } else {
183                     actualCounts = generatedCounts;
184                 }
185                 Integer count = actualCounts.get(parserId);
186                 if (count == null) {
187                     count = 1;
188                 } else {
189                     count++;
190                 }
191                 actualCounts.put(parserId, count);
192             } else {
193                 nonDoxiaDocuments.add(doc);
194             }
195         }
196 
197         if (doxiaDocuments.size() > 0) {
198             MessageBuilder mb = buffer();
199             mb.a("Rendering ");
200             mb.strong(doxiaDocuments.size() + " Doxia document" + (doxiaDocuments.size() > 1 ? "s" : ""));
201             mb.a(": ");
202 
203             boolean first = true;
204             for (Map.Entry<String, Integer> entry : counts.entrySet()) {
205                 if (first) {
206                     first = false;
207                 } else {
208                     mb.a(", ");
209                 }
210                 mb.strong(entry.getValue() + " " + entry.getKey());
211             }
212 
213             getLog().info(mb.build());
214 
215             siteRenderer.render(doxiaDocuments, context, outputDirectory);
216         }
217 
218         if (generatedDoxiaDocuments.size() > 0) {
219             MessageBuilder mb = buffer();
220             mb.a("Rendering ");
221             mb.strong(generatedDoxiaDocuments.size() + " generated Doxia document"
222                     + (generatedDoxiaDocuments.size() > 1 ? "s" : ""));
223             mb.a(": ");
224 
225             boolean first = true;
226             for (Map.Entry<String, Integer> entry : generatedCounts.entrySet()) {
227                 if (first) {
228                     first = false;
229                 } else {
230                     mb.a(", ");
231                 }
232                 mb.strong(entry.getValue() + " " + entry.getKey());
233             }
234 
235             getLog().info(mb.build());
236 
237             siteRenderer.render(generatedDoxiaDocuments, context, outputDirectory);
238         }
239 
240         return nonDoxiaDocuments;
241     }
242 
243     /**
244      * Render non-Doxia documents (e.g., reports) from the list given
245      *
246      * @param documents a collection of documents containing non-Doxia source files
247      */
248     private void renderNonDoxiaDocuments(
249             Collection<DocumentRenderer> documents, SiteRenderingContext context, File outputDirectory)
250             throws RendererException, IOException {
251         Map<String, Integer> counts = new TreeMap<>();
252 
253         for (DocumentRenderer doc : documents) {
254             String type;
255             if (doc instanceof ReportDocumentRenderer || doc instanceof SitePluginReportDocumentRenderer) {
256                 type = "report";
257             } else {
258                 type = "other";
259             }
260 
261             Integer count = counts.get(type);
262             if (count == null) {
263                 count = 1;
264             } else {
265                 count++;
266             }
267             counts.put(type, count);
268         }
269 
270         if (documents.size() > 0) {
271             for (Map.Entry<String, Integer> entry : counts.entrySet()) {
272                 String type = entry.getKey();
273                 Integer count = entry.getValue();
274 
275                 MessageBuilder mb = buffer();
276                 mb.a("Rendering ");
277                 mb.strong(count + " " + type + " document" + (count > 1 ? "s" : ""));
278 
279                 getLog().info(mb.build());
280             }
281 
282             siteRenderer.render(documents, context, outputDirectory);
283         }
284     }
285 
286     private File getOutputDirectory(Locale locale) {
287         File file;
288         if (!locale.equals(SiteTool.DEFAULT_LOCALE)) {
289             file = new File(outputDirectory, locale.toString());
290         } else {
291             file = outputDirectory;
292         }
293 
294         // Safety
295         if (!file.exists()) {
296             file.mkdirs();
297         }
298 
299         return file;
300     }
301 
302     public MavenProject getProject() {
303         return project;
304     }
305 
306     public MavenSession getSession() {
307         return mavenSession;
308     }
309 }