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