View Javadoc

1   package org.apache.maven.report.projectinfo;
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.artifact.Artifact;
23  import org.apache.maven.artifact.factory.ArtifactFactory;
24  import org.apache.maven.artifact.repository.ArtifactRepository;
25  import org.apache.maven.artifact.resolver.ArtifactResolver;
26  import org.apache.maven.doxia.sink.render.RenderingContext;
27  import org.apache.maven.doxia.site.decoration.Body;
28  import org.apache.maven.doxia.site.decoration.DecorationModel;
29  import org.apache.maven.doxia.siterenderer.Renderer;
30  import org.apache.maven.doxia.siterenderer.RendererException;
31  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
32  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
33  import org.apache.maven.doxia.tools.SiteTool;
34  import org.apache.maven.doxia.tools.SiteToolException;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.project.MavenProject;
37  import org.apache.maven.reporting.AbstractMavenReport;
38  import org.apache.maven.reporting.MavenReportException;
39  import org.apache.maven.settings.Settings;
40  import org.codehaus.plexus.i18n.I18N;
41  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
42  import org.codehaus.plexus.interpolation.InterpolationException;
43  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
44  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
45  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
46  import org.codehaus.plexus.util.IOUtil;
47  
48  
49  import java.io.File;
50  import java.io.FileOutputStream;
51  import java.io.IOException;
52  import java.io.OutputStreamWriter;
53  import java.io.Writer;
54  import java.net.MalformedURLException;
55  import java.net.URL;
56  import java.net.URLClassLoader;
57  import java.text.MessageFormat;
58  import java.util.HashMap;
59  import java.util.Locale;
60  import java.util.Map;
61  import java.util.MissingResourceException;
62  import java.util.ResourceBundle;
63  
64  /**
65   * Base class with the things that should be in AbstractMavenReport anyway.
66   *
67   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
68   * @version $Id: AbstractProjectInfoReport.java 1049044 2010-12-14 11:10:04Z ltheussl $
69   * @since 2.0
70   */
71  public abstract class AbstractProjectInfoReport
72      extends AbstractMavenReport
73  {
74      // ----------------------------------------------------------------------
75      // Mojo components
76      // ----------------------------------------------------------------------
77  
78      /**
79       * SiteTool component.
80       *
81       * @since 2.1
82       * @component
83       */
84      protected SiteTool siteTool;
85  
86      /**
87       * Doxia Site Renderer component.
88       *
89       * @component
90       */
91      protected Renderer siteRenderer;
92  
93      /**
94       * Artifact Resolver component.
95       *
96       * @component
97       */
98      protected ArtifactResolver resolver;
99  
100     /**
101      * Artifact Factory component.
102      *
103      * @component
104      */
105     protected ArtifactFactory factory;
106 
107     /**
108      * Internationalization component, could support also custom bundle using {@link #customBundle}.
109      *
110      * @component
111      */
112     private I18N i18n;
113 
114     // ----------------------------------------------------------------------
115     // Mojo parameters
116     // ----------------------------------------------------------------------
117 
118     /**
119      * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from
120      * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in
121      * the Maven Site Plugin is used instead.
122      *
123      * @parameter expression="${project.reporting.outputDirectory}"
124      * @required
125      */
126     protected File outputDirectory;
127 
128     /**
129      * The Maven Project.
130      *
131      * @parameter expression="${project}"
132      * @required
133      * @readonly
134      */
135     protected MavenProject project;
136 
137     /**
138      * Local Repository.
139      *
140      * @parameter expression="${localRepository}"
141      * @required
142      * @readonly
143      */
144     protected ArtifactRepository localRepository;
145 
146     /**
147      * The current user system settings for use in Maven.
148      *
149      * @parameter expression="${settings}"
150      * @required
151      * @readonly
152      * @since 2.3
153      */
154     protected Settings settings;
155 
156     /**
157      * Path for a custom bundle instead of using the default one. <br/>
158      * Using this field, you could change the texts in the generated reports.
159      *
160      * @parameter expression="${project.basedir}/src/site/custom/project-info-report.properties"
161      * @since 2.3
162      */
163     protected String customBundle;
164 
165     // ----------------------------------------------------------------------
166     // Public methods
167     // ----------------------------------------------------------------------
168 
169     @Override
170     public void execute()
171         throws MojoExecutionException
172     {
173         if ( !canGenerateReport() )
174         {
175             return;
176         }
177 
178         // TODO: push to a helper? Could still be improved by taking more of the site information from the site plugin
179         Writer writer = null;
180         try
181         {
182             String filename = getOutputName() + ".html";
183 
184             DecorationModel model = new DecorationModel();
185             model.setBody( new Body() );
186 
187             Map<String, Object> attributes = new HashMap<String, Object>();
188             attributes.put( "outputEncoding", "UTF-8" );
189             attributes.put( "project", project );
190 
191             Locale locale = Locale.getDefault();
192             Artifact defaultSkin =
193                 siteTool.getDefaultSkinArtifact( localRepository, project.getRemoteArtifactRepositories() );
194 
195             SiteRenderingContext siteContext =
196                 siteRenderer.createContextForSkin( defaultSkin.getFile(), attributes, model, getName( locale ), locale );
197 
198             RenderingContext context = new RenderingContext( outputDirectory, filename );
199 
200             SiteRendererSink sink = new SiteRendererSink( context );
201 
202             generate( sink, null, locale );
203 
204             outputDirectory.mkdirs();
205 
206             writer = new OutputStreamWriter( new FileOutputStream( new File( outputDirectory, filename ) ), "UTF-8" );
207 
208             siteRenderer.generateDocument( writer, sink, siteContext );
209 
210             siteRenderer.copyResources( siteContext, new File( project.getBasedir(), "src/site/resources" ),
211                                         outputDirectory );
212         }
213         catch ( RendererException e )
214         {
215             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
216                 + " report generation.", e );
217         }
218         catch ( IOException e )
219         {
220             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
221                 + " report generation.", e );
222         }
223         catch ( SiteToolException e )
224         {
225             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
226                 + " report generation.", e );
227         }
228         catch ( MavenReportException e )
229         {
230             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
231                 + " report generation.", e );
232         }
233         finally
234         {
235             IOUtil.close( writer );
236         }
237     }
238 
239     @Override
240     public String getCategoryName()
241     {
242         return CATEGORY_PROJECT_INFORMATION;
243     }
244 
245     // ----------------------------------------------------------------------
246     // Protected methods
247     // ----------------------------------------------------------------------
248 
249     @Override
250     protected String getOutputDirectory()
251     {
252         return outputDirectory.getAbsolutePath();
253     }
254 
255     @Override
256     public File getReportOutputDirectory()
257     {
258         return outputDirectory;
259     }
260 
261     @Override
262     public void setReportOutputDirectory( File reportOutputDirectory )
263     {
264         this.outputDirectory = reportOutputDirectory;
265     }
266 
267     @Override
268     protected MavenProject getProject()
269     {
270         return project;
271     }
272 
273     @Override
274     protected Renderer getSiteRenderer()
275     {
276         return siteRenderer;
277     }
278 
279     protected String getI18nString( Locale locale, String key )
280     {
281         return getI18N( locale ).getString( "project-info-report", locale, "report." + getI18Nsection() + '.' + key );
282     }
283 
284     protected I18N getI18N( Locale locale )
285     {
286         if ( customBundle != null )
287         {
288             File customBundleFile = new File( customBundle );
289             if ( customBundleFile.isFile() && customBundleFile.getName().endsWith( ".properties" ) )
290             {
291                 if ( !i18n.getClass().isAssignableFrom( CustomI18N.class ) )
292                 {
293                     // first load
294                     i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
295                 }
296                 else if ( !i18n.getDefaultLanguage().equals( locale.getLanguage() ) )
297                 {
298                     i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
299                 }
300             }
301         }
302 
303         return i18n;
304     }
305 
306     protected abstract String getI18Nsection();
307 
308     /** {@inheritDoc} */
309     public String getName( Locale locale )
310     {
311         return getI18nString( locale, "name" );
312     }
313 
314     /** {@inheritDoc} */
315     public String getDescription( Locale locale )
316     {
317         return getI18nString( locale, "description" );
318     }
319 
320     private static class CustomI18N
321         implements I18N
322     {
323         private final MavenProject project;
324 
325         private final Settings settings;
326 
327         private final String bundleName;
328 
329         private final Locale locale;
330 
331         private final I18N i18nOriginal;
332 
333         private ResourceBundle bundle;
334 
335         private final Object[] NO_ARGS = new Object[0];
336 
337         public CustomI18N( MavenProject project, Settings settings, File customBundleFile, Locale locale,
338                            I18N i18nOriginal )
339         {
340             super();
341             this.project = project;
342             this.settings = settings;
343             this.locale = locale;
344             this.i18nOriginal = i18nOriginal;
345             this.bundleName =
346                 customBundleFile.getName().substring( 0, customBundleFile.getName().indexOf( ".properties" ) );
347 
348             URLClassLoader classLoader = null;
349             try
350             {
351                 classLoader = new URLClassLoader( new URL[] { customBundleFile.getParentFile().toURI().toURL() } );
352             }
353             catch ( MalformedURLException e )
354             {
355             }
356 
357             this.bundle = ResourceBundle.getBundle( this.bundleName, locale, classLoader );
358             if ( !this.bundle.getLocale().getLanguage().equals( locale.getLanguage() ) )
359             {
360                 this.bundle = ResourceBundle.getBundle( this.bundleName, Locale.getDefault(), classLoader );
361             }
362         }
363 
364         /** {@inheritDoc} */
365         public String getDefaultLanguage()
366         {
367             return locale.getLanguage();
368         }
369 
370         /** {@inheritDoc} */
371         public String getDefaultCountry()
372         {
373             return locale.getCountry();
374         }
375 
376         /** {@inheritDoc} */
377         public String getDefaultBundleName()
378         {
379             return bundleName;
380         }
381 
382         /** {@inheritDoc} */
383         public String[] getBundleNames()
384         {
385             return new String[] { bundleName };
386         }
387 
388         /** {@inheritDoc} */
389         public ResourceBundle getBundle()
390         {
391             return bundle;
392         }
393 
394         /** {@inheritDoc} */
395         public ResourceBundle getBundle( String bundleName )
396         {
397             return bundle;
398         }
399 
400         /** {@inheritDoc} */
401         public ResourceBundle getBundle( String bundleName, String languageHeader )
402         {
403             return bundle;
404         }
405 
406         /** {@inheritDoc} */
407         public ResourceBundle getBundle( String bundleName, Locale locale )
408         {
409             return bundle;
410         }
411 
412         /** {@inheritDoc} */
413         public Locale getLocale( String languageHeader )
414         {
415             return new Locale( languageHeader );
416         }
417 
418         /** {@inheritDoc} */
419         public String getString( String key )
420         {
421             return getString( bundleName, locale, key );
422         }
423 
424         /** {@inheritDoc} */
425         public String getString( String key, Locale locale )
426         {
427             return getString( bundleName, locale, key );
428         }
429 
430         /** {@inheritDoc} */
431         public String getString( String bundleName, Locale locale, String key )
432         {
433             String value;
434 
435             if ( locale == null )
436             {
437                 locale = getLocale( null );
438             }
439 
440             ResourceBundle rb = getBundle( bundleName, locale );
441             value = getStringOrNull( rb, key );
442 
443             if ( value == null )
444             {
445                 // try to load default
446                 value = i18nOriginal.getString( bundleName, locale, key );
447             }
448 
449             if ( value.indexOf( "${" ) < 0 )
450             {
451                 return value;
452             }
453 
454             final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
455             try
456             {
457                 interpolator.addValueSource( new EnvarBasedValueSource() );
458             }
459             catch ( final IOException e )
460             {
461             }
462 
463             interpolator.addValueSource( new PropertiesBasedValueSource( System.getProperties() ) );
464             interpolator.addValueSource( new PropertiesBasedValueSource( project.getProperties() ) );
465             interpolator.addValueSource( new PrefixedObjectValueSource( "project", project ) );
466             interpolator.addValueSource( new PrefixedObjectValueSource( "pom", project ) );
467             interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
468 
469             try
470             {
471                 value = interpolator.interpolate( value );
472             }
473             catch ( final InterpolationException e )
474             {
475             }
476 
477             return value;
478         }
479 
480         /** {@inheritDoc} */
481         public String format( String key, Object arg1 )
482         {
483             return format( bundleName, locale, key, new Object[] { arg1 } );
484         }
485 
486         /** {@inheritDoc} */
487         public String format( String key, Object arg1, Object arg2 )
488         {
489             return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
490         }
491 
492         /** {@inheritDoc} */
493         public String format( String bundleName, Locale locale, String key, Object arg1 )
494         {
495             return format( bundleName, locale, key, new Object[] { arg1 } );
496         }
497 
498         /** {@inheritDoc} */
499         public String format( String bundleName, Locale locale, String key, Object arg1, Object arg2 )
500         {
501             return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
502         }
503 
504         /** {@inheritDoc} */
505         public String format( String bundleName, Locale locale, String key, Object[] args )
506         {
507             if ( locale == null )
508             {
509                 locale = getLocale( null );
510             }
511 
512             String value = getString( bundleName, locale, key );
513             if ( args == null )
514             {
515                 args = NO_ARGS;
516             }
517 
518             MessageFormat messageFormat = new MessageFormat( "" );
519             messageFormat.setLocale( locale );
520             messageFormat.applyPattern( value );
521 
522             return messageFormat.format( args );
523         }
524 
525         private final String getStringOrNull( ResourceBundle rb, String key )
526         {
527             if ( rb != null )
528             {
529                 try
530                 {
531                     return rb.getString( key );
532                 }
533                 catch ( MissingResourceException ignored )
534                 {
535                     // intentional
536                 }
537             }
538             return null;
539         }
540     }
541 }