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