View Javadoc
1   /*
2    * Copyright 2016-2017 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.github.matinh.vdldoc.maven.plugins;
18  
19  import org.apache.maven.doxia.sink.Sink;
20  import org.apache.maven.plugin.AbstractMojo;
21  import org.apache.maven.plugin.MojoExecutionException;
22  import org.apache.maven.plugins.annotations.Execute;
23  import org.apache.maven.plugins.annotations.LifecyclePhase;
24  import org.apache.maven.plugins.annotations.Mojo;
25  import org.apache.maven.plugins.annotations.Parameter;
26  import org.apache.maven.reporting.MavenReport;
27  import org.apache.maven.reporting.MavenReportException;
28  import org.codehaus.plexus.util.StringUtils;
29  import org.omnifaces.vdldoc.VdldocGenerator;
30  
31  import java.io.File;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.List;
35  import java.util.Locale;
36  import java.util.ResourceBundle;
37  
38  /**
39   * Generate documentation for JSF tag libraries via OmniFaces Vdldoc.
40   *
41   * @author martin
42   * @since 1.0-alpha-1
43   */
44  @Mojo(name = "vdldoc", defaultPhase = LifecyclePhase.SITE)
45  @Execute(phase = LifecyclePhase.GENERATE_SOURCES)
46  public class Vdldoc
47      extends AbstractMojo
48      implements MavenReport
49  {
50      /**
51       * Browser window title.
52       */
53      @Parameter(defaultValue = "${project.name} VDL Documentation")
54      private String browserTitle;
55  
56      /**
57       * Documentation title.
58       */
59      @Parameter(defaultValue = "${project.name} VDL Documentation")
60      private String documentTitle;
61  
62      /**
63       * If {@code false}, build will continue when generation of documentation
64       * fails.
65       */
66      // we initialize to default value here for unit testing.
67      @Parameter(defaultValue = "true", property = "maven.vdldoc.failOnError")
68      private boolean failOnError = true;
69  
70      /**
71       * Skip the generation of VDL documentation.
72       */
73      @Parameter(defaultValue = "false", property = "maven.vdldoc.skip")
74      private boolean skip;
75  
76      /**
77       * Patterns to include when searching for taglib descriptor files.
78       */
79      @Parameter(defaultValue = "**/*.taglib.xml",
80          property = "maven.vdldoc.includes")
81      private List<String> includes;
82  
83      /**
84       * Patterns to exclude when searching for taglib descriptor files.
85       */
86      @Parameter(defaultValue = "target/**", property = "maven.vdldoc.excludes")
87      private List<String> excludes;
88  
89      /**
90       * Location of the output directory for the generated report.
91       * This configuration is usually only useful if you call the mojo directly
92       * from the command-line.
93       */
94      @Parameter(defaultValue = "${project.reporting.outputDirectory}",
95          property = "maven.vdldoc.outputDirectory")
96      private File reportOutputDirectory;
97  
98      /**
99       * Name of the directory (inside the reporting-output-directory) into which
100      * Vdldoc will place the generated documentation.
101      */
102     // we initialize to default value here for unit testing.
103     @Parameter(defaultValue = "vdldoc", property = "maven.vdldoc.destDir")
104     private String destDir = "vdldoc";
105 
106     /**
107      * URI of the CSS file. This defaults to Vdldocs internal CSS.
108      * @since 1.0
109      */
110     @Parameter(property = "maven.vdldoc.css")
111     private String css;
112 
113     /**
114      * Path to the faces-config.xml file.
115      * @since 1.0
116      */
117     @Parameter(property = "maven.vdldoc.facesConfig")
118     private String facesConfig;
119 
120     /**
121      * Path to properties file containing descriptions for implied attributes
122      * of composite components, such as 'id', 'rendered', etc.
123      * @since 1.0
124      */
125     @Parameter(property = "maven.vdldoc.attributesFile")
126     private String attributesFile;
127 
128     /**
129      * Hide the "output generated by" footer.
130      * @since 1.0
131      */
132     @Parameter(defaultValue = "false",
133         property = "maven.vdldoc.hideGeneratedBy")
134     private boolean hideGeneratedBy;
135 
136 
137     // ============ end of writable mojo parameters ===========
138 
139     /**
140      * The directory to scan for taglib files.
141      */
142     @Parameter(defaultValue = "${project.basedir}", readonly = true)
143     private File srcDirectory;
144 
145     /**
146      * The documentation generator to use.
147      */
148     private VdldocGenerator generator = new VdldocGenerator();
149 
150 
151     /**
152      * Set the generator to use for documentation generation.
153      * <p>
154      * This method is meant for unit tests only.
155      * </p>
156      * @param gen The generator to use. Must not be {@code null}.
157      */
158     void setGenerator(final VdldocGenerator gen)
159     {
160         this.generator = gen;
161     }
162 
163     @Override
164     public void execute() throws MojoExecutionException
165     {
166         if (skip) {
167             getLog().info("Skipping generation of Vdldoc.");
168             return;
169         }
170 
171         try {
172             generateDocumentation();
173         } catch (Exception e) {
174             if (failOnError) {
175                 throw new MojoExecutionException("Error generating Vdldoc!", e);
176             }
177 
178             // log the failure and continue
179             final String reason = e.getLocalizedMessage() == null
180                 ? e.toString() : e.getLocalizedMessage();
181             getLog().warn("Failed to generate documentation: "
182                 + reason);
183             getLog().debug(e);
184         }
185     }
186 
187     @Deprecated
188     @Override
189     public void generate(final org.codehaus.doxia.sink.Sink sink, final Locale locale)
190         throws MavenReportException
191     {
192         generate((Sink) sink, locale);
193     }
194 
195     /**
196      * Generate the actual report.
197      * <p>
198      * This method delegates to {@link #execute()} and generates the actual
199      * report. It is needed by the (new) maven reporting API.
200      * </p>
201      * @param sink currently ignored.
202      * @param locale currently ignored.
203      * @throws MavenReportException on any problem during report generation.
204      * @see #generate(org.codehaus.doxia.sink.Sink, Locale)
205      */
206     @SuppressWarnings({"unused", "deprecation"})
207     public void generate(final Sink sink, final Locale locale)
208         throws MavenReportException
209     {
210         try {
211             execute();
212         } catch (MojoExecutionException e) {
213             throw new MavenReportException(e.getMessage(),
214                 (Exception) e.getCause());
215         }
216     }
217 
218     @Override
219     public String getOutputName()
220     {
221         return destDir + File.separator + "index";
222     }
223 
224     @Override
225     public String getCategoryName()
226     {
227         return CATEGORY_PROJECT_REPORTS;
228     }
229 
230     @Override
231     public String getName(final Locale locale)
232     {
233         return getBundle(locale).getString("report.vdldoc.name");
234     }
235 
236     @Override
237     public String getDescription(final Locale locale)
238     {
239         return getBundle(locale).getString("report.vdldoc.description");
240     }
241 
242     @Override
243     public void setReportOutputDirectory(final File file)
244     {
245         reportOutputDirectory = file;
246     }
247 
248     @Override
249     public File getReportOutputDirectory()
250     {
251         return reportOutputDirectory;
252     }
253 
254     @Override
255     public boolean isExternalReport()
256     {
257         return true;
258     }
259 
260     @Override
261     public boolean canGenerateReport()
262     {
263         return true;
264     }
265 
266     /**
267      * Gets the resource bundle for the specified locale.
268      *
269      * @param locale The locale of the currently generated report.
270      * @return The resource bundle for the requested locale.
271      */
272     private ResourceBundle getBundle(final Locale locale)
273     {
274         return ResourceBundle.getBundle(
275             "vdldoc-report", locale, getClass().getClassLoader());
276     }
277 
278     private void generateDocumentation()
279     {
280         generator.setWindowTitle(browserTitle);
281         generator.setDocTitle(documentTitle);
282         generator.setOutputDirectory(new File(reportOutputDirectory, destDir));
283 
284         if (!StringUtils.isEmpty(css)) {
285             getLog().debug("Using CSS file " + css);
286             generator.setCssLocation(css);
287         }
288 
289         if (!StringUtils.isEmpty(facesConfig)) {
290             getLog().debug("Using faces-config file "
291                 + facesConfig);
292             generator.setFacesConfig(new File(facesConfig));
293         }
294         if (!StringUtils.isEmpty(attributesFile)) {
295             getLog().debug("Using attributes-file "
296                 + attributesFile);
297             generator.setAttributes(new File(attributesFile));
298         }
299         if (hideGeneratedBy) {
300             getLog().debug("Hiding 'generated-by' message.");
301             generator.setHideGeneratedBy(true);
302         }
303 
304         // TODO add support for quiet-flag
305 
306         final List<String> taglibs = scanForTaglibs(
307             srcDirectory, includes, excludes);
308         getLog().debug("Found taglibs: " + taglibs);
309         for (String taglib : taglibs) {
310             generator.addTaglib(new File(srcDirectory, taglib));
311         }
312 
313         generator.generate();
314     }
315 
316     private List<String> scanForTaglibs(final File basedir,
317         final List<String> includeList, final List<String> excludeList)
318     {
319         List<String> result = new ArrayList<>();
320         if (basedir != null && basedir.exists()) {
321             org.codehaus.plexus.util.DirectoryScanner scanner =
322                 new org.codehaus.plexus.util.DirectoryScanner();
323 
324             scanner.setBasedir(basedir);
325 
326             if (includeList != null) {
327                 scanner.setIncludes(processIncludesExcludes(includeList));
328             }
329 
330             if (excludeList != null) {
331                 scanner.setExcludes(processIncludesExcludes(excludeList));
332             }
333 
334             scanner.scan();
335             Collections.addAll(result, scanner.getIncludedFiles());
336         }
337         return result;
338     }
339 
340     // based on maven-surefire-plugin's DirectoryScanner
341     private static String[] processIncludesExcludes(final List<String> list)
342     {
343         List<String> newList = new ArrayList<>();
344         for (String aList : list) {
345             String[] includes = aList.split(",");
346             Collections.addAll(newList, includes);
347         }
348 
349         String[] incs = new String[newList.size()];
350         for (int i = 0; i < incs.length; i++) {
351             incs[i] = newList.get(i);
352         }
353 
354         return incs;
355     }
356 }