View Javadoc
1   package org.apache.maven.plugin.docck;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.model.ReportPlugin;
23  import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
24  import org.apache.maven.plugin.descriptor.MojoDescriptor;
25  import org.apache.maven.plugin.descriptor.Parameter;
26  import org.apache.maven.plugin.descriptor.PluginDescriptor;
27  import org.apache.maven.plugin.docck.reports.DocumentationReporter;
28  import org.apache.maven.plugins.annotations.Component;
29  import org.apache.maven.plugins.annotations.LifecyclePhase;
30  import org.apache.maven.plugins.annotations.Mojo;
31  import org.apache.maven.project.MavenProject;
32  import org.apache.maven.tools.plugin.extractor.ExtractionException;
33  import org.apache.maven.tools.plugin.scanner.MojoScanner;
34  import org.codehaus.plexus.util.IOUtil;
35  import org.codehaus.plexus.util.ReaderFactory;
36  
37  import java.io.File;
38  import java.io.IOException;
39  import java.io.Reader;
40  import java.util.ArrayList;
41  import java.util.List;
42  
43  /**
44   * Checks a plugin's documentation for the standard minimums.
45   *
46   * @author jdcasey
47   */
48  @Mojo( name = "check", aggregator = true, defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
49  public class CheckPluginDocumentationMojo
50      extends AbstractCheckDocumentationMojo
51  {
52  
53      /**
54       * Plexus component that searches for Mojos.
55       */
56      @Component
57      protected MojoScanner mojoScanner;
58  
59      // TODO: really a description of length 1 isn't all that helpful...
60      private static final int MIN_DESCRIPTION_LENGTH = 1;
61  
62      @Override
63      protected void checkPackagingSpecificDocumentation( MavenProject project, DocumentationReporter reporter )
64      {
65          PluginDescriptor descriptor = new PluginDescriptor();
66  
67          try
68          {
69              mojoScanner.populatePluginDescriptor( project, descriptor );
70          }
71          catch ( InvalidPluginDescriptorException e )
72          {
73              reporter.error( "Failed to parse mojo descriptors.\nError: " + e.getMessage() );
74              descriptor = null;
75          }
76          catch ( ExtractionException e )
77          {
78              reporter.error( "Failed to parse mojo descriptors.\nError: " + e.getMessage() );
79              descriptor = null;
80          }
81  
82          if ( descriptor != null )
83          {
84              @SuppressWarnings( "unchecked" )
85              List<MojoDescriptor> mojos = descriptor.getMojos();
86  
87              // ensure that all mojo classes are documented
88              if ( mojos != null && !mojos.isEmpty() )
89              {
90                  for ( MojoDescriptor mojo : mojos )
91                  {
92                      String mojoDescription = mojo.getDescription();
93  
94                      if ( mojoDescription == null || mojoDescription.trim().length() < MIN_DESCRIPTION_LENGTH )
95                      {
96                          reporter.error( "Mojo: \'" + mojo.getGoal() + "\' is missing a description." );
97                      }
98  
99                      @SuppressWarnings( "unchecked" )
100                     List<Parameter> params = mojo.getParameters();
101 
102                     // ensure that all parameters are documented
103                     if ( params != null && !params.isEmpty() )
104                     {
105                         for ( Parameter param : params )
106                         {
107                             if ( param.getRequirement() == null && param.isEditable() )
108                             {
109                                 String paramDescription = param.getDescription();
110 
111                                 if ( paramDescription == null
112                                     || paramDescription.trim().length() < MIN_DESCRIPTION_LENGTH )
113                                 {
114                                     reporter.error( "Parameter: \'" + param.getName() + "\' in mojo: \'"
115                                         + mojo.getGoal() + "\' is missing a description." );
116                                 }
117                             }
118                         }
119                     }
120                 }
121             }
122         }
123 
124         checkConfiguredReportPlugins( project, reporter );
125 
126         checkProjectSite( project, reporter );
127     }
128 
129     @Override
130     protected boolean approveProjectPackaging( String packaging )
131     {
132         return "maven-plugin".equals( packaging );
133     }
134 
135     private void checkProjectSite( MavenProject project, DocumentationReporter reporter )
136     {
137         File projectSiteDirectory = new File( project.getBasedir(), siteDirectory );
138 
139         // check for site.xml
140         File siteXml = new File( projectSiteDirectory, "site.xml" );
141 
142         if ( !siteXml.exists() )
143         {
144             reporter.error( "site.xml is missing." );
145         }
146         else
147         {
148             
149             try ( Reader streamReader = ReaderFactory.newXmlReader( siteXml ) )
150             {
151                 String siteHtml = IOUtil.toString( streamReader );
152 
153                 if ( !siteHtml.contains( "href=\"index.html\"" ) )
154                 {
155                     reporter.error( "site.xml is missing the link to: index.html \"Introduction\"." );
156                 }
157 
158                 if ( !siteHtml.contains( "href=\"usage.html\"" ) )
159                 {
160                     reporter.error( "site.xml is missing the link to: usage.html \"Usage\"." );
161                 }
162 
163                 if ( !siteHtml.contains( "href=\"plugin-info.html\"" ) )
164                 {
165                     reporter.error( "site.xml is missing the link to: plugin-info.html \"Goals\"." );
166                 }
167 
168                 if ( !siteHtml.contains( "href=\"faq.html\"" ) )
169                 {
170                     reporter.error( "site.xml is missing the link to: faq.html \"FAQ\"." );
171                 }
172             }
173             catch ( IOException e )
174             {
175                 reporter.error( "Unable to read site.xml file: \'" + siteXml.getAbsolutePath() + "\'.\nError: "
176                     + e.getMessage() );
177             }
178         }
179 
180         // check for index.(apt|html|xml)[.vm]
181         if ( !findFiles( projectSiteDirectory, "index" ) )
182         {
183             reporter.error( "There is no \'index\' file in your site directory (in apt|html|xml[.vm] format)." );
184         }
185 
186         // check for usage.(apt|html|xml)[.vm]
187         if ( !findFiles( projectSiteDirectory, "usage" ) )
188         {
189             reporter.error( "There is no \'usage\' file in your site directory (in apt|html|xml[.vm] format)." );
190         }
191 
192         // check for **/examples/**.(apt|html|xml)[.vm] or **/example*.(apt|html|xml)[.vm]
193         if ( !findFiles( projectSiteDirectory, "**/examples/*" ) && !findFiles( projectSiteDirectory, "**/example*" ) )
194         {
195             reporter.error( "There are no example files in your site directory (in apt|html|xml[.vm] format)."
196                 + " They should either be called \'example*.(apt|html|xml)[.vm]\'"
197                 + " or they should be located in the \'examples\' directory." );
198         }
199 
200         if ( !findFiles( projectSiteDirectory, "faq" ) )
201         {
202             reporter.error( "There is no \'faq\' file in your site directory (in apt|fml|html|xml[.vm] format)." );
203         }
204     }
205 
206     /**
207      * Checks the project configured plugins if the required report plugins are present.
208      *
209      * @param project MavenProject to check
210      * @param reporter listener
211      * @todo maybe this should be checked default for all project?
212      */
213     private void checkConfiguredReportPlugins( MavenProject project, DocumentationReporter reporter )
214     {
215         List<String> expectedPlugins = getRequiredPlugins();
216 
217         @SuppressWarnings( "unchecked" )
218         List<ReportPlugin> reportPlugins = project.getReportPlugins();
219         if ( reportPlugins != null && reportPlugins.size() > 0 )
220         {
221             for ( ReportPlugin plugin : reportPlugins )
222             {
223                 expectedPlugins.remove( plugin.getArtifactId() );
224             }
225         }
226         else
227         {
228             reporter.error( "pom.xml has no report plugins configured." );
229         }
230 
231         for ( String expectedPlugin : expectedPlugins )
232         {
233             reporter.error( "pom.xml is missing the report plugin: " + expectedPlugin + "." );
234         }
235     }
236 
237     /**
238      * Returns a List of Strings of required report plugins.
239      *
240      * @return List of report plugin artifactIds
241      */
242     private List<String> getRequiredPlugins()
243     {
244         List<String> list = new ArrayList<>();
245 
246         list.add( "maven-javadoc-plugin" );
247         list.add( "maven-jxr-plugin" );
248 
249         return list;
250     }
251 }