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.javadoc;
20  
21  import java.io.File;
22  import java.nio.file.Path;
23  import java.util.Collection;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.ResourceBundle;
27  import java.util.stream.Collectors;
28  
29  import org.apache.maven.doxia.sink.Sink;
30  import org.apache.maven.doxia.sink.SinkFactory;
31  import org.apache.maven.doxia.siterenderer.DocumentRenderingContext;
32  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
33  import org.apache.maven.doxia.tools.SiteTool;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.MojoFailureException;
36  import org.apache.maven.plugins.annotations.Execute;
37  import org.apache.maven.plugins.annotations.LifecyclePhase;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.reporting.MavenMultiPageReport;
42  import org.apache.maven.reporting.MavenReportException;
43  
44  /**
45   * Generates documentation for the <code>Java code</code> in a <b>NON aggregator</b> project using the standard
46   * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/man/javadoc.html">Javadoc Tool</a>.
47   *
48   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
49   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
50   * @since 2.0
51   * @see <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/man/javadoc.html">Javadoc Tool</a>
52   */
53  @Mojo(name = "javadoc", requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
54  @Execute(phase = LifecyclePhase.GENERATE_SOURCES)
55  public class JavadocReport extends AbstractJavadocMojo implements MavenMultiPageReport {
56      // ----------------------------------------------------------------------
57      // Report Mojo Parameters
58      // ----------------------------------------------------------------------
59  
60      /** The current shared report output directory to use */
61      private File reportOutputDirectory;
62  
63      /**
64       * The name of the Javadoc report to be displayed in the Maven Generated Reports page
65       * (i.e. <code>project-reports.html</code>).
66       *
67       * @since 2.1
68       */
69      @Parameter(property = "name")
70      private String name;
71  
72      /**
73       * The description of the Javadoc report to be displayed in the Maven Generated Reports page
74       * (i.e. <code>project-reports.html</code>).
75       *
76       * @since 2.1
77       */
78      @Parameter(property = "description")
79      private String description;
80  
81      // ----------------------------------------------------------------------
82      // Report public methods
83      // ----------------------------------------------------------------------
84  
85      /** {@inheritDoc} */
86      @Override
87      public String getName(Locale locale) {
88          if (name == null || name.isEmpty()) {
89              return getBundle(locale).getString("report.javadoc.name");
90          }
91  
92          return name;
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public String getDescription(Locale locale) {
98          if (description == null || description.isEmpty()) {
99              return getBundle(locale).getString("report.javadoc.description");
100         }
101 
102         return description;
103     }
104 
105     /** {@inheritDoc} */
106     public void generate(Sink sink, Locale locale) throws MavenReportException {
107         generate(sink, null, locale);
108     }
109 
110     /** {@inheritDoc} */
111     @Override
112     public void generate(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException {
113         try {
114             executeReport(locale);
115         } catch (MavenReportException | RuntimeException e) {
116             if (failOnError) {
117                 throw e;
118             }
119             getLog().error("Error while creating javadoc report: " + e.getMessage(), e);
120         }
121     }
122 
123     /** {@inheritDoc} */
124     @Override
125     public String getOutputName() {
126         return (isTest() ? "test" : "") + "apidocs" + "/index";
127     }
128 
129     /** {@inheritDoc} */
130     @Override
131     public boolean isExternalReport() {
132         return true;
133     }
134 
135     /**
136      * {@inheritDoc}
137      *
138      * <br>
139      * The logic is the following:
140      * <table><caption>Can-generate-report Matrix</caption>
141      *   <tbody>
142      *     <tr>
143      *       <th> isAggregator </th>
144      *       <th> hasSourceFiles </th>
145      *       <th> isRootProject </th>
146      *       <th> Generate Report </th>
147      *     </tr>
148      *     <tr>
149      *       <td>True</td>
150      *       <td>True</td>
151      *       <td>True</td>
152      *       <td>True</td>
153      *     </tr>
154      *     <tr>
155      *       <td>True</td>
156      *       <td>True</td>
157      *       <td>False</td>
158      *       <td>False</td>
159      *     </tr>
160      *     <tr>
161      *       <td>True</td>
162      *       <td>False</td>
163      *       <td>True</td>
164      *       <td>False</td>
165      *     </tr>
166      *     <tr>
167      *       <td>True</td>
168      *       <td>False</td>
169      *       <td>False</td>
170      *       <td>False</td>
171      *     </tr>
172      *     <tr>
173      *       <td>False</td>
174      *       <td>True</td>
175      *       <td>True</td>
176      *       <td>True</td>
177      *     </tr>
178      *     <tr>
179      *       <td>False</td>
180      *       <td>True</td>
181      *       <td>False</td>
182      *       <td>True</td>
183      *     </tr>
184      *     <tr>
185      *        <td>False</td>
186      *        <td>False</td>
187      *        <td>True</td>
188      *        <td>False</td>
189      *      </tr>
190      *      <tr>
191      *        <td>False</td>
192      *        <td>False</td>
193      *        <td>False</td>
194      *        <td>False</td>
195      *      </tr>
196      *    </tbody>
197      *  </table>
198      */
199     @Override
200     public boolean canGenerateReport() throws MavenReportException {
201         if (skip) {
202             return false;
203         }
204 
205         Collection<JavadocModule> sourcePaths = getSourcePaths();
206 
207         Collection<Path> collectedSourcePaths =
208                 sourcePaths.stream().flatMap(e -> e.getSourcePaths().stream()).collect(Collectors.toList());
209 
210         Map<Path, Collection<String>> files = getFiles(collectedSourcePaths);
211 
212         if (!canGenerateReport(files)) {
213             return false;
214         }
215 
216         return true;
217     }
218 
219     /** {@inheritDoc} */
220     @Override
221     public String getCategoryName() {
222         return CATEGORY_PROJECT_REPORTS;
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public File getReportOutputDirectory() {
228         if (reportOutputDirectory == null) {
229             reportOutputDirectory = new File(getOutputDirectory());
230         }
231 
232         return reportOutputDirectory;
233     }
234 
235     /** {@inheritDoc} */
236     @Override
237     public void setReportOutputDirectory(File reportOutputDirectory) {
238         this.reportOutputDirectory = reportOutputDirectory;
239         this.outputDirectory = reportOutputDirectory;
240     }
241 
242     /** {@inheritDoc} */
243     @Override
244     protected String getPluginReportOutputDirectory() {
245         return getReportOutputDirectory().getAbsolutePath() + "/" + (isTest() ? "test" : "") + "apidocs";
246     }
247 
248     /** {@inheritDoc} */
249     @Override
250     protected void doExecute() throws MojoExecutionException, MojoFailureException {
251         try {
252             if (!canGenerateReport()) {
253                 String reportMojoInfo = mojoExecution.getPlugin().getId() + ":" + mojoExecution.getGoal();
254                 getLog().info("Skipping " + reportMojoInfo + " report goal");
255                 return;
256             }
257         } catch (MavenReportException e) {
258             throw new MojoExecutionException("Failed to determine whether report can be generated", e);
259         }
260 
261         File outputDirectory = new File(getOutputDirectory());
262 
263         String filename = getOutputName() + ".html";
264 
265         Locale locale = SiteTool.DEFAULT_LOCALE;
266 
267         try {
268             String reportMojoInfo = mojoExecution.getPlugin().getId() + ":" + mojoExecution.getGoal();
269             DocumentRenderingContext docRenderingContext =
270                     new DocumentRenderingContext(outputDirectory, filename, reportMojoInfo);
271 
272             SiteRendererSink sink = new SiteRendererSink(docRenderingContext);
273 
274             generate(sink, null, locale);
275         } catch (MavenReportException | RuntimeException e) {
276             failOnError("An error has occurred in " + getName(Locale.ENGLISH) + " report generation", e);
277         }
278     }
279 
280     /**
281      * Gets the resource bundle for the specified locale.
282      *
283      * @param locale the locale of the currently generated report
284      * @return the resource bundle for the requested locale
285      */
286     private ResourceBundle getBundle(Locale locale) {
287         return ResourceBundle.getBundle("javadoc-report", locale, getClass().getClassLoader());
288     }
289 }