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