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.plugin.MojoExecution;
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.reporting.MavenReportException;
44  import org.apache.maven.repository.RepositorySystem;
45  import org.apache.maven.settings.Settings;
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 parameters
64      // ----------------------------------------------------------------------
65  
66      @Parameter(defaultValue = "${session}", readonly = true, required = true)
67      private MavenSession session;
68  
69      @Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true)
70      protected List<ArtifactRepository> remoteRepositories;
71  
72      /**
73       * Plugin repositories used for the project.
74       *
75       * @since 3.1.0
76       */
77      @Parameter(defaultValue = "${project.pluginArtifactRepositories}", readonly = true, required = true)
78      protected List<ArtifactRepository> pluginRepositories;
79  
80      /**
81       * The current user system settings for use in Maven.
82       *
83       * @since 2.3
84       */
85      @Parameter(defaultValue = "${settings}", readonly = true, required = true)
86      protected Settings settings;
87  
88      /**
89       * Path for a custom bundle instead of using the default one. <br>
90       * Using this field, you could change the texts in the generated reports.
91       *
92       * @since 2.3
93       */
94      @Parameter(defaultValue = "${project.basedir}/src/site/custom/project-info-reports.properties")
95      protected String customBundle;
96  
97      /**
98       * Skip report.
99       *
100      * @since 2.8
101      */
102     @Parameter(property = "mpir.skip", defaultValue = "false")
103     private boolean skip;
104 
105     /**
106      * Skip the project info report generation if a report-specific section of the POM is empty. Defaults to
107      * <code>true</code>.
108      *
109      * @since 2.8
110      */
111     @Parameter(defaultValue = "true")
112     protected boolean skipEmptyReport;
113 
114     /**
115      * A mapping of license names to group licenses referred to with different names together
116      *
117      * @since 3.3.1
118      */
119     @Parameter
120     private List<LicenseMapping> licenseMappings;
121 
122     // ----------------------------------------------------------------------
123     // Mojo components
124     // ----------------------------------------------------------------------
125 
126     /**
127      * Artifact Factory component.
128      */
129     final RepositorySystem repositorySystem;
130 
131     /**
132      * Internationalization component, could support also custom bundle using {@link #customBundle}.
133      */
134     private I18N i18n;
135 
136     protected final ProjectBuilder projectBuilder;
137 
138     protected AbstractProjectInfoReport(RepositorySystem repositorySystem, I18N i18n, ProjectBuilder projectBuilder) {
139         this.repositorySystem = repositorySystem;
140         this.i18n = i18n;
141         this.projectBuilder = projectBuilder;
142     }
143 
144     // ----------------------------------------------------------------------
145     // Public methods
146     // ----------------------------------------------------------------------
147 
148     @Override
149     public boolean canGenerateReport() throws MavenReportException {
150         return !skip;
151     }
152 
153     @Override
154     public String getCategoryName() {
155         return CATEGORY_PROJECT_INFORMATION;
156     }
157 
158     // ----------------------------------------------------------------------
159     // Protected methods
160     // ----------------------------------------------------------------------
161 
162     protected Map<String, String> getLicenseMappings() {
163         Map<String, String> map = new HashMap<>();
164         if (licenseMappings != null) {
165             for (LicenseMapping mapping : licenseMappings) {
166                 for (String from : mapping.getFroms()) {
167                     map.put(from, mapping.getTo());
168                 }
169             }
170         }
171         return map;
172     }
173 
174     /**
175      * @param coll The collection to be checked.
176      * @return true if coll is empty false otherwise.
177      */
178     protected boolean isEmpty(Collection<?> coll) {
179         return coll == null || coll.isEmpty();
180     }
181 
182     protected MavenSession getSession() {
183         return session;
184     }
185 
186     protected List<MavenProject> getReactorProjects() {
187         return reactorProjects;
188     }
189 
190     protected MojoExecution getMojoExecution() {
191         return mojoExecution;
192     }
193 
194     /**
195      * @param pluginId The id of the plugin
196      * @return The information about the plugin
197      */
198     protected Plugin getPlugin(String pluginId) {
199         if ((getProject().getBuild() == null) || (getProject().getBuild().getPluginsAsMap() == null)) {
200             return null;
201         }
202 
203         Plugin plugin = getProject().getBuild().getPluginsAsMap().get(pluginId);
204 
205         if ((plugin == null)
206                 && (getProject().getBuild().getPluginManagement() != null)
207                 && (getProject().getBuild().getPluginManagement().getPluginsAsMap() != null)) {
208             plugin = getProject()
209                     .getBuild()
210                     .getPluginManagement()
211                     .getPluginsAsMap()
212                     .get(pluginId);
213         }
214 
215         return plugin;
216     }
217 
218     /**
219      * @param pluginId the pluginId
220      * @param param the child which should be checked
221      * @return the value of the dom tree
222      */
223     protected String getPluginParameter(String pluginId, String param) {
224         Plugin plugin = getPlugin(pluginId);
225         if (plugin != null) {
226             Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
227             if (xpp3Dom != null
228                     && xpp3Dom.getChild(param) != null
229                     && StringUtils.isNotEmpty(xpp3Dom.getChild(param).getValue())) {
230                 return xpp3Dom.getChild(param).getValue();
231             }
232         }
233 
234         return null;
235     }
236 
237     /**
238      * @param locale The locale
239      * @param key The key to search for
240      * @return The text appropriate for the locale.
241      */
242     protected String getI18nString(Locale locale, String key) {
243         return getI18N(locale).getString("project-info-reports", locale, "report." + getI18Nsection() + '.' + key);
244     }
245 
246     /**
247      * @param locale The local.
248      * @return I18N for the locale
249      */
250     protected I18N getI18N(Locale locale) {
251         if (customBundle != null) {
252             File customBundleFile = new File(customBundle);
253             if (customBundleFile.isFile() && customBundleFile.getName().endsWith(".properties")) {
254                 if (!i18n.getClass().isAssignableFrom(CustomI18N.class)
255                         || !i18n.getDefaultLanguage().equals(locale.getLanguage())) {
256                     // first load
257                     i18n = new CustomI18N(project, settings, customBundleFile, locale, i18n);
258                 }
259             }
260         }
261 
262         return i18n;
263     }
264 
265     /**
266      * @return The according string for the section.
267      */
268     protected abstract String getI18Nsection();
269 
270     /** {@inheritDoc} */
271     public String getName(Locale locale) {
272         return getI18nString(locale, "name");
273     }
274 
275     /** {@inheritDoc} */
276     public String getDescription(Locale locale) {
277         return getI18nString(locale, "description");
278     }
279 
280     // TODO Review, especially Locale.getDefault()
281     private static class CustomI18N implements I18N {
282         private final MavenProject project;
283 
284         private final Settings settings;
285 
286         private final String bundleName;
287 
288         private final Locale locale;
289 
290         private final I18N i18nOriginal;
291 
292         private ResourceBundle bundle;
293 
294         private static final Object[] NO_ARGS = new Object[0];
295 
296         CustomI18N(MavenProject project, Settings settings, File customBundleFile, Locale locale, I18N i18nOriginal) {
297             super();
298             this.project = project;
299             this.settings = settings;
300             this.locale = locale;
301             this.i18nOriginal = i18nOriginal;
302             this.bundleName = customBundleFile
303                     .getName()
304                     .substring(0, customBundleFile.getName().indexOf(".properties"));
305 
306             URLClassLoader classLoader = null;
307             try {
308                 classLoader = new URLClassLoader(
309                         new URL[] {customBundleFile.getParentFile().toURI().toURL()}, null);
310             } catch (MalformedURLException e) {
311                 // could not happen.
312             }
313 
314             this.bundle = ResourceBundle.getBundle(this.bundleName, locale, classLoader);
315             if (!this.bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
316                 this.bundle = ResourceBundle.getBundle(this.bundleName, Locale.getDefault(), classLoader);
317             }
318         }
319 
320         /** {@inheritDoc} */
321         public String getDefaultLanguage() {
322             return locale.getLanguage();
323         }
324 
325         /** {@inheritDoc} */
326         public String getDefaultCountry() {
327             return locale.getCountry();
328         }
329 
330         /** {@inheritDoc} */
331         public String getDefaultBundleName() {
332             return bundleName;
333         }
334 
335         /** {@inheritDoc} */
336         public String[] getBundleNames() {
337             return new String[] {bundleName};
338         }
339 
340         /** {@inheritDoc} */
341         public ResourceBundle getBundle() {
342             return bundle;
343         }
344 
345         /** {@inheritDoc} */
346         public ResourceBundle getBundle(String bundleName) {
347             return bundle;
348         }
349 
350         /** {@inheritDoc} */
351         public ResourceBundle getBundle(String bundleName, String languageHeader) {
352             return bundle;
353         }
354 
355         /** {@inheritDoc} */
356         public ResourceBundle getBundle(String bundleName, Locale locale) {
357             return bundle;
358         }
359 
360         /** {@inheritDoc} */
361         public Locale getLocale(String languageHeader) {
362             return new Locale(languageHeader);
363         }
364 
365         /** {@inheritDoc} */
366         public String getString(String key) {
367             return getString(bundleName, locale, key);
368         }
369 
370         /** {@inheritDoc} */
371         public String getString(String key, Locale locale) {
372             return getString(bundleName, locale, key);
373         }
374 
375         /** {@inheritDoc} */
376         public String getString(String bundleName, Locale locale, String key) {
377             String value;
378 
379             if (locale == null) {
380                 locale = getLocale(null);
381             }
382 
383             ResourceBundle rb = getBundle(bundleName, locale);
384             value = getStringOrNull(rb, key);
385 
386             if (value == null) {
387                 // try to load default
388                 value = i18nOriginal.getString(bundleName, locale, key);
389             }
390 
391             if (!value.contains("${")) {
392                 return value;
393             }
394 
395             final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
396             try {
397                 interpolator.addValueSource(new EnvarBasedValueSource());
398             } catch (final IOException e) {
399                 // In which cases could this happen? And what should we do?
400             }
401 
402             interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
403             interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
404             interpolator.addValueSource(new PrefixedObjectValueSource("project", project));
405             interpolator.addValueSource(new PrefixedObjectValueSource("pom", project));
406             interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));
407 
408             try {
409                 value = interpolator.interpolate(value);
410             } catch (final InterpolationException e) {
411                 // What does this exception mean?
412             }
413 
414             return value;
415         }
416 
417         /** {@inheritDoc} */
418         public String format(String key, Object arg1) {
419             return format(bundleName, locale, key, new Object[] {arg1});
420         }
421 
422         /** {@inheritDoc} */
423         public String format(String key, Object arg1, Object arg2) {
424             return format(bundleName, locale, key, new Object[] {arg1, arg2});
425         }
426 
427         /** {@inheritDoc} */
428         public String format(String bundleName, Locale locale, String key, Object arg1) {
429             return format(bundleName, locale, key, new Object[] {arg1});
430         }
431 
432         /** {@inheritDoc} */
433         public String format(String bundleName, Locale locale, String key, Object arg1, Object arg2) {
434             return format(bundleName, locale, key, new Object[] {arg1, arg2});
435         }
436 
437         /** {@inheritDoc} */
438         public String format(String bundleName, Locale locale, String key, Object[] args) {
439             if (locale == null) {
440                 locale = getLocale(null);
441             }
442 
443             String value = getString(bundleName, locale, key);
444             if (args == null) {
445                 args = NO_ARGS;
446             }
447 
448             MessageFormat messageFormat = new MessageFormat("");
449             messageFormat.setLocale(locale);
450             messageFormat.applyPattern(value);
451 
452             return messageFormat.format(args);
453         }
454 
455         private String getStringOrNull(ResourceBundle rb, String key) {
456             if (rb != null) {
457                 try {
458                     return rb.getString(key);
459                 } catch (MissingResourceException ignored) {
460                     // intentional
461                 }
462             }
463             return null;
464         }
465     }
466 }