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