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 java.io.File;
23  import java.io.IOException;
24  import java.net.MalformedURLException;
25  import java.net.URL;
26  import java.net.URLClassLoader;
27  import java.text.MessageFormat;
28  import java.util.Collection;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.MissingResourceException;
34  import java.util.ResourceBundle;
35  
36  import org.apache.maven.artifact.repository.ArtifactRepository;
37  import org.apache.maven.execution.MavenSession;
38  import org.apache.maven.model.Plugin;
39  import org.apache.maven.plugins.annotations.Component;
40  import org.apache.maven.plugins.annotations.Parameter;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.project.ProjectBuilder;
43  import org.apache.maven.reporting.AbstractMavenReport;
44  import org.apache.maven.repository.RepositorySystem;
45  import org.apache.maven.settings.Settings;
46  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
47  import org.codehaus.plexus.i18n.I18N;
48  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
49  import org.codehaus.plexus.interpolation.InterpolationException;
50  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
51  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
52  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
53  import org.codehaus.plexus.util.StringUtils;
54  import org.codehaus.plexus.util.xml.Xpp3Dom;
55  
56  /**
57   * Base class with the things that should be in AbstractMavenReport anyway.
58   *
59   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
60   * @since 2.0
61   */
62  public abstract class AbstractProjectInfoReport
63      extends AbstractMavenReport
64  {
65      // ----------------------------------------------------------------------
66      // Mojo components
67      // ----------------------------------------------------------------------
68  
69      /**
70       * Artifact Resolver component.
71       */
72      @Component
73      protected ArtifactResolver resolver;
74  
75      /**
76       * Artifact Factory component.
77       */
78      @Component
79      RepositorySystem repositorySystem;
80  
81      /**
82       * Internationalization component, could support also custom bundle using {@link #customBundle}.
83       */
84      @Component
85      private I18N i18n;
86  
87      @Component
88      protected ProjectBuilder projectBuilder;
89  
90      // ----------------------------------------------------------------------
91      // Mojo parameters
92      // ----------------------------------------------------------------------
93  
94      @Parameter( defaultValue = "${session}", readonly = true, required = true )
95      private MavenSession session;
96  
97      /**
98       * Plugin repositories used for the project.
99       *
100      * @since 3.1.0
101      */
102     @Parameter( property = "project.pluginArtifactRepositories" )
103     protected List<ArtifactRepository> pluginRepositories;
104 
105     /**
106      * The reactor projects.
107      *
108      * @since 2.10
109      */
110     @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
111     protected List<MavenProject> reactorProjects;
112 
113     /**
114      * The current user system settings for use in Maven.
115      *
116      * @since 2.3
117      */
118     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
119     protected Settings settings;
120 
121     /**
122      * Path for a custom bundle instead of using the default one. <br>
123      * Using this field, you could change the texts in the generated reports.
124      *
125      * @since 2.3
126      */
127     @Parameter( defaultValue = "${project.basedir}/src/site/custom/project-info-reports.properties" )
128     protected String customBundle;
129 
130     /**
131      * Skip report.
132      *
133      * @since 2.8
134      */
135     @Parameter( property = "mpir.skip", defaultValue = "false" )
136     private boolean skip;
137 
138     /**
139      * Skip the project info report generation if a report-specific section of the POM is empty. Defaults to
140      * <code>true</code>.
141      *
142      * @since 2.8
143      */
144     @Parameter( defaultValue = "true" )
145     protected boolean skipEmptyReport;
146 
147     /**
148      * A mapping of license names to group licenses referred to with different names together
149      *
150      * @since 3.3.1
151      */
152     @Parameter
153     private List<LicenseMapping> licenseMappings;
154 
155     // ----------------------------------------------------------------------
156     // Public methods
157     // ----------------------------------------------------------------------
158 
159     @Override
160     public boolean canGenerateReport()
161     {
162         return !skip;
163     }
164 
165     @Override
166     public String getCategoryName()
167     {
168         return CATEGORY_PROJECT_INFORMATION;
169     }
170 
171     // ----------------------------------------------------------------------
172     // Protected methods
173     // ----------------------------------------------------------------------
174 
175     protected Map<String, String> getLicenseMappings()
176     {
177         Map<String, String> map = new HashMap<>();
178         if ( licenseMappings != null )
179         {
180             for ( LicenseMapping mapping : licenseMappings )
181             {
182                 for ( String from : mapping.getFroms() )
183                 {
184                     map.put( from, mapping.getTo() );
185                 }
186             }
187         }
188         return map;
189     }
190 
191     /**
192      * @param coll The collection to be checked.
193      * @return true if coll is empty false otherwise.
194      */
195     protected boolean isEmpty( Collection<?> coll )
196     {
197         return coll == null || coll.isEmpty();
198     }
199 
200     @Override
201     protected String getOutputDirectory()
202     {
203         return outputDirectory.getAbsolutePath();
204     }
205 
206     @Override
207     public File getReportOutputDirectory()
208     {
209         return outputDirectory;
210     }
211 
212     @Override
213     public void setReportOutputDirectory( File reportOutputDirectory )
214     {
215         this.outputDirectory = reportOutputDirectory;
216     }
217 
218     @Override
219     protected MavenProject getProject()
220     {
221         return project;
222     }
223 
224     protected MavenSession getSession()
225     {
226         return session;
227     }
228 
229     /**
230      * Reactor projects
231      *
232      * @return List of projects
233      */
234     protected List<MavenProject> getReactorProjects()
235     {
236         return reactorProjects;
237     }
238 
239     /**
240      * @param pluginId The id of the plugin
241      * @return The information about the plugin.
242      */
243     protected Plugin getPlugin( String pluginId )
244     {
245         if ( ( getProject().getBuild() == null ) || ( getProject().getBuild().getPluginsAsMap() == null ) )
246         {
247             return null;
248         }
249 
250         Plugin plugin = getProject().getBuild().getPluginsAsMap().get( pluginId );
251 
252         if ( ( plugin == null ) && ( getProject().getBuild().getPluginManagement() != null )
253             && ( getProject().getBuild().getPluginManagement().getPluginsAsMap() != null ) )
254         {
255             plugin = getProject().getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
256         }
257 
258         return plugin;
259     }
260 
261     /**
262      * @param pluginId The pluginId
263      * @param param The child which should be checked.
264      * @return The value of the dom tree.
265      */
266     protected String getPluginParameter( String pluginId, String param )
267     {
268         Plugin plugin = getPlugin( pluginId );
269         if ( plugin != null )
270         {
271             Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
272             if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
273                 && StringUtils.isNotEmpty( xpp3Dom.getChild( param ).getValue() ) )
274             {
275                 return xpp3Dom.getChild( param ).getValue();
276             }
277         }
278 
279         return null;
280     }
281 
282     /**
283      * @param locale The locale
284      * @param key The key to search for
285      * @return The text appropriate for the locale.
286      */
287     protected String getI18nString( Locale locale, String key )
288     {
289         return getI18N( locale ).getString( "project-info-reports", locale, "report." + getI18Nsection() + '.' + key );
290     }
291 
292     /**
293      * @param locale The local.
294      * @return I18N for the locale
295      */
296     protected I18N getI18N( Locale locale )
297     {
298         if ( customBundle != null )
299         {
300             File customBundleFile = new File( customBundle );
301             if ( customBundleFile.isFile() && customBundleFile.getName().endsWith( ".properties" ) )
302             {
303                 if ( !i18n.getClass().isAssignableFrom( CustomI18N.class )
304                         || !i18n.getDefaultLanguage().equals( locale.getLanguage() ) )
305                 {
306                     // first load
307                     i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
308                 }
309             }
310         }
311 
312         return i18n;
313     }
314 
315     /**
316      * @return The according string for the section.
317      */
318     protected abstract String getI18Nsection();
319 
320     /** {@inheritDoc} */
321     public String getName( Locale locale )
322     {
323         return getI18nString( locale, "name" );
324     }
325 
326     /** {@inheritDoc} */
327     public String getDescription( Locale locale )
328     {
329         return getI18nString( locale, "description" );
330     }
331 
332     private static class CustomI18N
333         implements I18N
334     {
335         private final MavenProject project;
336 
337         private final Settings settings;
338 
339         private final String bundleName;
340 
341         private final Locale locale;
342 
343         private final I18N i18nOriginal;
344 
345         private ResourceBundle bundle;
346 
347         private static final Object[] NO_ARGS = new Object[0];
348 
349         CustomI18N( MavenProject project, Settings settings, File customBundleFile, Locale locale,
350                            I18N i18nOriginal )
351         {
352             super();
353             this.project = project;
354             this.settings = settings;
355             this.locale = locale;
356             this.i18nOriginal = i18nOriginal;
357             this.bundleName =
358                 customBundleFile.getName().substring( 0, customBundleFile.getName().indexOf( ".properties" ) );
359 
360             URLClassLoader classLoader = null;
361             try
362             {
363                 classLoader = new URLClassLoader( new URL[] { customBundleFile.getParentFile().toURI().toURL() } );
364             }
365             catch ( MalformedURLException e )
366             {
367                 // could not happen.
368             }
369 
370             this.bundle = ResourceBundle.getBundle( this.bundleName, locale, classLoader );
371             if ( !this.bundle.getLocale().getLanguage().equals( locale.getLanguage() ) )
372             {
373                 this.bundle = ResourceBundle.getBundle( this.bundleName, Locale.getDefault(), classLoader );
374             }
375         }
376 
377         /** {@inheritDoc} */
378         public String getDefaultLanguage()
379         {
380             return locale.getLanguage();
381         }
382 
383         /** {@inheritDoc} */
384         public String getDefaultCountry()
385         {
386             return locale.getCountry();
387         }
388 
389         /** {@inheritDoc} */
390         public String getDefaultBundleName()
391         {
392             return bundleName;
393         }
394 
395         /** {@inheritDoc} */
396         public String[] getBundleNames()
397         {
398             return new String[] { bundleName };
399         }
400 
401         /** {@inheritDoc} */
402         public ResourceBundle getBundle()
403         {
404             return bundle;
405         }
406 
407         /** {@inheritDoc} */
408         public ResourceBundle getBundle( String bundleName )
409         {
410             return bundle;
411         }
412 
413         /** {@inheritDoc} */
414         public ResourceBundle getBundle( String bundleName, String languageHeader )
415         {
416             return bundle;
417         }
418 
419         /** {@inheritDoc} */
420         public ResourceBundle getBundle( String bundleName, Locale locale )
421         {
422             return bundle;
423         }
424 
425         /** {@inheritDoc} */
426         public Locale getLocale( String languageHeader )
427         {
428             return new Locale( languageHeader );
429         }
430 
431         /** {@inheritDoc} */
432         public String getString( String key )
433         {
434             return getString( bundleName, locale, key );
435         }
436 
437         /** {@inheritDoc} */
438         public String getString( String key, Locale locale )
439         {
440             return getString( bundleName, locale, key );
441         }
442 
443         /** {@inheritDoc} */
444         public String getString( String bundleName, Locale locale, String key )
445         {
446             String value;
447 
448             if ( locale == null )
449             {
450                 locale = getLocale( null );
451             }
452 
453             ResourceBundle rb = getBundle( bundleName, locale );
454             value = getStringOrNull( rb, key );
455 
456             if ( value == null )
457             {
458                 // try to load default
459                 value = i18nOriginal.getString( bundleName, locale, key );
460             }
461 
462             if ( !value.contains( "${" ) )
463             {
464                 return value;
465             }
466 
467             final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
468             try
469             {
470                 interpolator.addValueSource( new EnvarBasedValueSource() );
471             }
472             catch ( final IOException e )
473             {
474                 // In which cases could this happen? And what should we do?
475             }
476 
477             interpolator.addValueSource( new PropertiesBasedValueSource( System.getProperties() ) );
478             interpolator.addValueSource( new PropertiesBasedValueSource( project.getProperties() ) );
479             interpolator.addValueSource( new PrefixedObjectValueSource( "project", project ) );
480             interpolator.addValueSource( new PrefixedObjectValueSource( "pom", project ) );
481             interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
482 
483             try
484             {
485                 value = interpolator.interpolate( value );
486             }
487             catch ( final InterpolationException e )
488             {
489                 // What does this exception mean?
490             }
491 
492             return value;
493         }
494 
495         /** {@inheritDoc} */
496         public String format( String key, Object arg1 )
497         {
498             return format( bundleName, locale, key, new Object[] { arg1 } );
499         }
500 
501         /** {@inheritDoc} */
502         public String format( String key, Object arg1, Object arg2 )
503         {
504             return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
505         }
506 
507         /** {@inheritDoc} */
508         public String format( String bundleName, Locale locale, String key, Object arg1 )
509         {
510             return format( bundleName, locale, key, new Object[] { arg1 } );
511         }
512 
513         /** {@inheritDoc} */
514         public String format( String bundleName, Locale locale, String key, Object arg1, Object arg2 )
515         {
516             return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
517         }
518 
519         /** {@inheritDoc} */
520         public String format( String bundleName, Locale locale, String key, Object[] args )
521         {
522             if ( locale == null )
523             {
524                 locale = getLocale( null );
525             }
526 
527             String value = getString( bundleName, locale, key );
528             if ( args == null )
529             {
530                 args = NO_ARGS;
531             }
532 
533             MessageFormat messageFormat = new MessageFormat( "" );
534             messageFormat.setLocale( locale );
535             messageFormat.applyPattern( value );
536 
537             return messageFormat.format( args );
538         }
539 
540         private String getStringOrNull( ResourceBundle rb, String key )
541         {
542             if ( rb != null )
543             {
544                 try
545                 {
546                     return rb.getString( key );
547                 }
548                 catch ( MissingResourceException ignored )
549                 {
550                     // intentional
551                 }
552             }
553             return null;
554         }
555     }
556 }