View Javadoc

1   package org.apache.maven.plugins.site;
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 java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.io.Writer;
26  import java.io.File;
27  
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  
31  import java.util.ArrayList;
32  import java.util.Locale;
33  import java.util.List;
34  
35  import org.apache.maven.doxia.sink.Sink;
36  import org.apache.maven.doxia.sink.SinkFactory;
37  import org.apache.maven.doxia.sink.render.RenderingContext;
38  import org.apache.maven.doxia.siterenderer.DocumentRenderer;
39  import org.apache.maven.doxia.siterenderer.Renderer;
40  import org.apache.maven.doxia.siterenderer.RendererException;
41  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
42  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
43  import org.apache.maven.doxia.tools.MojoLogWrapper;
44  import org.apache.maven.plugin.logging.Log;
45  import org.apache.maven.reporting.MavenReport;
46  import org.apache.maven.reporting.MavenReportException;
47  
48  import org.codehaus.plexus.util.IOUtil;
49  import org.codehaus.plexus.util.WriterFactory;
50  
51  /**
52   * Renders a Maven report in a doxia site.
53   *
54   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
55   */
56  public class ReportDocumentRenderer
57      implements DocumentRenderer
58  {
59      private final MavenReport report;
60  
61      private final RenderingContext renderingContext;
62  
63      private final String pluginInfo;
64  
65      private final Log log;
66  
67      public ReportDocumentRenderer( MavenReport report, RenderingContext renderingContext, Log log )
68      {
69          this.report = report;
70  
71          pluginInfo = getPluginInfo( report );
72  
73          this.renderingContext = renderingContext;
74  
75          this.log = log;
76      }
77  
78      /**
79       * Get plugin information from report's Manifest.
80       * 
81       * @param report the Maven report
82       * @return plugin information as Specification Title followed by Specification Version if set in Manifest and
83       *         supported by JVM
84       */
85      private String getPluginInfo( MavenReport report )
86      {
87          Package pkg = report.getClass().getPackage();
88  
89          if ( pkg != null )
90          {
91              String title = pkg.getSpecificationTitle();
92              String version = pkg.getSpecificationVersion();
93              
94              if ( title == null )
95              {
96                  return version;
97              }
98              else if ( version == null )
99              {
100                 return title;
101             }
102             else
103             {
104                 return title + ' ' + version;
105             }
106         }
107 
108         return null;
109     }
110 
111     private static class MySink
112         extends SiteRendererSink
113     {
114         private File outputDir;
115 
116         private String outputName;
117 
118         public MySink( File outputDir, String outputName, RenderingContext ctx )
119         {
120             super( ctx );
121             this.outputName = outputName;
122             this.outputDir = outputDir;
123         }
124 
125         public String getOutputName()
126         {
127             return outputName;
128         }
129 
130         public File getOutputDir()
131         {
132             return outputDir;
133         }
134 
135     }
136 
137     private static class MySinkFactory
138         implements SinkFactory
139     {
140         private RenderingContext context;
141 
142         private List<MySink> sinks = new ArrayList<MySink>();
143 
144         public MySinkFactory( RenderingContext ctx )
145         {
146             this.context = ctx;
147         }
148 
149         public Sink createSink( File outputDir, String outputName )
150         {
151             MySink sink = new MySink( outputDir, outputName, context );
152             sinks.add( sink );
153             return sink;
154         }
155 
156         public Sink createSink( File arg0, String arg1, String arg2 )
157             throws IOException
158         {
159             // Not used
160             return null;
161         }
162 
163         public Sink createSink( OutputStream arg0 )
164             throws IOException
165         {
166             // Not used
167             return null;
168         }
169 
170         public Sink createSink( OutputStream arg0, String arg1 )
171             throws IOException
172         {
173             // Not used
174             return null;
175         }
176 
177         public List<MySink> sinks()
178         {
179             return sinks;
180         }
181     }
182 
183     public void renderDocument( Writer writer, Renderer renderer, SiteRenderingContext siteRenderingContext )
184         throws RendererException, FileNotFoundException
185     {
186         Locale locale = siteRenderingContext.getLocale();
187         String localReportName = report.getName( locale );
188 
189         log.info( "Generating \"" + localReportName + "\" report"
190                   + ( pluginInfo == null ? "." : ( "    --- " + pluginInfo ) ) );
191 
192         MySinkFactory sf = new MySinkFactory( renderingContext );
193 
194         SiteRendererSink sink = new SiteRendererSink( renderingContext );
195         sink.enableLogging( new MojoLogWrapper( log ) );
196 
197         try
198         {
199             // try extended multi-page API
200             if ( !generateMultiPage( locale, sf, sink ) )
201             {
202                 // fallback to old single-page-only API
203                 try
204                 {
205                     report.generate( sink, locale );
206                 }
207                 catch ( NoSuchMethodError e )
208                 {
209                     throw new RendererException( "No method on " + report.getClass(), e );
210                 }
211             }
212         }
213         catch ( MavenReportException e )
214         {
215             throw new RendererException( "Error rendering Maven report: " + e.getMessage(), e );
216         }
217         finally
218         {
219             sink.close();
220         }
221 
222         if ( !report.isExternalReport() )
223         {
224             try
225             {
226                 List<MySink> sinks = sf.sinks();
227 
228                 log.debug( "Multipage report: " + sinks.size() + " subreports" );
229 
230                 for ( MySink mySink : sinks )
231                 {
232                     mySink.enableLogging( new MojoLogWrapper( log ) );
233 
234                     log.debug( "  Rendering " + mySink.getOutputName() );
235 
236                     File outputFile = new File( mySink.getOutputDir(), mySink.getOutputName() );
237 
238                     Writer out = null;
239                     try
240                     {
241                         out = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() );
242                         renderer.generateDocument( out, mySink, siteRenderingContext );
243                     }
244                     finally
245                     {
246                         mySink.close();
247                         IOUtil.close( out );
248                     }
249                 }
250             }
251             catch ( IOException e )
252             {
253                 throw new RendererException( "Cannot create writer", e );
254             }
255 
256             renderer.generateDocument( writer, sink, siteRenderingContext );
257         }
258     }
259 
260     /**
261      * Try to generate report with extended multi-page API.
262      * 
263      * @return <code>true</code> if the report was compatible with the extended API
264      */
265     private boolean generateMultiPage( Locale locale, SinkFactory sf, Sink sink )
266         throws MavenReportException
267     {
268         try
269         {
270             // MavenMultiPageReport is not in Maven Core, then the class is different in site plugin and in each report
271             // plugin: only reflection can let us invoke its method
272             Method generate =
273                 report.getClass().getMethod( "generate", Sink.class, SinkFactory.class, Locale.class );
274 
275             generate.invoke( report, sink, sf, locale );
276 
277             return true;
278         }
279         catch ( SecurityException se )
280         {
281             return false;
282         }
283         catch ( NoSuchMethodException nsme )
284         {
285             return false;
286         }
287         catch ( IllegalArgumentException iae )
288         {
289             throw new MavenReportException( "error while invoking generate", iae );
290         }
291         catch ( IllegalAccessException iae )
292         {
293             throw new MavenReportException( "error while invoking generate", iae );
294         }
295         catch ( InvocationTargetException ite )
296         {
297             throw new MavenReportException( "error while invoking generate", ite );
298         }
299     }
300 
301     public String getOutputName()
302     {
303         return renderingContext.getOutputName();
304     }
305 
306     public RenderingContext getRenderingContext()
307     {
308         return renderingContext;
309     }
310 
311     public boolean isOverwrite()
312     {
313         // TODO: would be nice to query the report to see if it is modified
314         return true;
315     }
316 
317     /**
318      * @return true if the current report is external, false otherwise
319      */
320     public boolean isExternalReport()
321     {
322         return report.isExternalReport();
323     }
324 }