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