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.rat.anttasks;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  import java.util.stream.Collectors;
30  
31  import org.apache.commons.cli.Option;
32  import org.apache.commons.io.filefilter.IOFileFilter;
33  import org.apache.rat.ConfigurationException;
34  import org.apache.rat.ImplementationException;
35  import org.apache.rat.OptionCollection;
36  import org.apache.rat.ReportConfiguration;
37  import org.apache.rat.Reporter;
38  import org.apache.rat.commandline.Arg;
39  import org.apache.rat.commandline.StyleSheets;
40  import org.apache.rat.document.DocumentName;
41  import org.apache.rat.license.LicenseSetFactory;
42  import org.apache.rat.utils.DefaultLog;
43  import org.apache.rat.utils.Log;
44  import org.apache.tools.ant.BuildEvent;
45  import org.apache.tools.ant.BuildException;
46  import org.apache.tools.ant.Project;
47  import org.apache.tools.ant.taskdefs.LogOutputStream;
48  import org.apache.tools.ant.types.EnumeratedAttribute;
49  import org.apache.tools.ant.types.Resource;
50  import org.apache.tools.ant.types.ResourceCollection;
51  import org.apache.tools.ant.types.resources.Union;
52  
53  /**
54   * A basic Ant task that generates a report on all files specified by the nested
55   * resource collection(s).
56   *
57   * <p>
58   * IHeaderMatcher(s) can be specified as nested elements as well.
59   * </p>
60   *
61   * <p>
62   * The attribute <code>format</code> defines the output format and can take the
63   * values
64   * <ul>
65   * <li>xml - Rat's native XML output.</li>
66   * <li>styled - transforms the XML output using the given stylesheet. The
67   * stylesheet attribute must be set as well if this attribute is used.</li>
68   * <li>plain - plain text using Rat's built-in stylesheet. This is the
69   * default.</li>
70   * </ul>
71   */
72  public class Report extends BaseAntTask {
73      /** The list of licenses */
74      private final List<License> licenses = new ArrayList<>();
75      /** The list of license families */
76      private final List<Family> families = new ArrayList<>();
77      /** the options that are deprecated.  TODO remove this. */
78      private final DeprecatedConfig deprecatedConfig = new DeprecatedConfig();
79      /**
80       * will hold any nested resource collection
81       */
82      private Union nestedResources;
83  
84      /**
85       * Collection of objects that support Ant specific deprecated options
86       */
87      private static class DeprecatedConfig {
88          /** The input file filter */
89          private IOFileFilter inputFileFilter;
90          /** the set of approved licence categories */
91          private final Set<String> approvedLicenseCategories = new HashSet<>();
92          /** the set of removed (unapproved) license categories */
93          private final Set<String> removedLicenseCategories = new HashSet<>();
94      }
95  
96      /**
97       * Constructor.
98       */
99      public Report() {
100         // replace the logger only if it has not already been set.
101         if (DefaultLog.getInstance() instanceof DefaultLog) {
102             DefaultLog.setInstance(new Logger());
103         }
104     }
105 
106     /**
107      * Adds resources that will be checked.
108      * @param rc resource to check.
109      */
110     public void add(final ResourceCollection rc) {
111         if (nestedResources == null) {
112             nestedResources = new Union();
113         }
114         nestedResources.add(rc);
115     }
116 
117     /**
118      * Adds an input file filter.
119      * @param inputFileFilter The input file filter to add.
120      */
121     @Deprecated
122     public void setInputFileFilter(final IOFileFilter inputFileFilter) {
123         deprecatedConfig.inputFileFilter = inputFileFilter;
124     }
125 
126     /**
127      * Sets the report file.
128      * @param reportFile the report file.
129      * @deprecated use {@link #setOut(String)}
130      */
131     @Deprecated
132     public void setReportFile(final File reportFile) {
133         setOut(reportFile.getAbsolutePath());
134     }
135 
136     /**
137      * Adds an inline License definition to the system.
138      * @param license the license to add.
139      */
140     public void addLicense(final License license) {
141         licenses.add(license);
142     }
143 
144     /**
145      * Add an inline license family definition to the system.
146      * @param family the license family to add.
147      */
148     public void addFamily(final Family family) {
149         families.add(family);
150     }
151 
152     /**
153      * Adds a style sheet to the system.
154      * @param styleSheet
155      * @deprecated use {@link #setStylesheet(String)}
156      */
157     @Deprecated
158     public void addStylesheet(final Resource styleSheet) {
159         setStylesheet(styleSheet.getName());
160     }
161 
162     /**
163      * Adds a given style sheet to the report.
164      * @param styleSheet style sheet to use in this report.
165      * @deprecated use {@link #setStylesheet(String)}
166      */
167     @Deprecated
168     public void addStyleSheet(final Resource styleSheet) {
169         setStylesheet(styleSheet.getName());
170     }
171 
172     /**
173      * Sets a stylesheet for the report.
174      * @param styleReport
175      * @deprecated use {@link #setXml(boolean)}.  Note reversal of boolean value
176      */
177     @Deprecated
178     public void setStyleReport(final boolean styleReport) {
179         setXml(!styleReport);
180     }
181 
182     /**
183      * Determines if the output should be styled.
184      * @param style
185      * @deprecated use {@link #setStylesheet(String)} or {@link #setXml(boolean)}
186      */
187     @Deprecated
188     public void setFormat(final String style) {
189         setStyleReport("styled".equalsIgnoreCase(style));
190     }
191 
192     /**
193      * Adds as a file containing the definitions of licenses to the system.
194      * @param fileName the file to add.
195      * @deprecated use licenses child element.
196      */
197     public void setLicenses(final File fileName) {
198         try {
199             createLicenses().addText(fileName.getCanonicalPath());
200         } catch (IOException e) {
201             throw new BuildException("Unable to read license file " + fileName, e);
202         }
203     }
204 
205     /**
206      * Specifies whether to add the default list of license matchers.
207      * @param useDefaultLicenses if {@code true} use the default licenses.
208      * @deprecated  use noDefaultLicenses attribute
209      */
210     @Deprecated
211     public void setUseDefaultLicenses(final boolean useDefaultLicenses) {
212         setNoDefaultLicenses(!useDefaultLicenses);
213     }
214 
215     /**
216      * Adds a family category to the list of approved licenses.
217      * @param familyCategory the category to add.
218      * @deprecated use addApprovedLicense child element.
219      */
220     @Deprecated
221     public void setAddApprovedLicense(final String familyCategory) {
222         deprecatedConfig.approvedLicenseCategories.add(familyCategory);
223     }
224 
225     /**
226      * Adds a family category to the list of approved licenses.
227      * @param familyCategory the category to add
228      */
229     public void addAddApprovedLicense(final String familyCategory) {
230         deprecatedConfig.approvedLicenseCategories.add(familyCategory);
231     }
232 
233     /**
234      * Removes a family category to the list of approved licenses.
235      * @param familyCategory the category to add.
236      * @deprecated use removeApprovedLicense child element
237      */
238     @Deprecated
239     public void setRemoveApprovedLicense(final String familyCategory) {
240         deprecatedConfig.removedLicenseCategories.add(familyCategory);
241     }
242 
243     /**
244      * Removes a family category to the list of approved licenses.
245      * @param familyCategory the category to add.
246      */
247     public void addRemoveApprovedLicense(final String familyCategory) {
248         deprecatedConfig.removedLicenseCategories.add(familyCategory);
249     }
250 
251     /**
252      * Removes a family category to the list of approved licenses.
253      * @param familyCategory the category to add.
254      * @deprecated use removeApprovedLicense element
255      */
256     @Deprecated
257     public void setRemoveApprovedLicense(final String[] familyCategory) {
258         deprecatedConfig.removedLicenseCategories.addAll(Arrays.asList(familyCategory));
259     }
260 
261     /**
262      * Removes a family category to the list of approved licenses.
263      * @param familyCategory the category to add.
264      */
265     public void addRemoveApprovedLicense(final String[] familyCategory) {
266         deprecatedConfig.removedLicenseCategories.addAll(Arrays.asList(familyCategory));
267     }
268     /**
269      * Sets the copyright message
270      * @param copyrightMessage the copyright message
271      * @deprecated use copyright attribute
272      */
273     @Deprecated
274     public void setCopyrightMessage(final String copyrightMessage) {
275        setCopyright(copyrightMessage);
276     }
277 
278     /**
279      * Determines if license headers should be added.
280      * @param setting the setting.
281      * @deprecated use addLicense and force attributes
282      */
283     @Deprecated
284     public void setAddLicenseHeaders(final AddLicenseHeaders setting) {
285         switch (setting.getNative()) {
286             case TRUE:
287                 setAddLicense(true);
288                 break;
289             case FALSE:
290                 setAddLicense(false);
291                 break;
292             case FORCED:
293                 setAddLicense(true);
294                 setForce(true);
295                 break;
296         }
297     }
298 
299     /**
300      * Adds definition information
301      * @param fileName the file to add
302      * @deprecated Use {@link #addLicense}
303      */
304     @Deprecated
305     public void setAddDefaultDefinitions(final File fileName) {
306         try {
307             Licenses lic = createLicenses();
308             lic.addText(fileName.getCanonicalPath());
309         } catch (IOException e) {
310             throw new BuildException("Unable to read license file " + fileName, e);
311         }
312     }
313 
314     /**
315      * Reads values for the Arg.
316      *
317      * @param arg The Arg to get the values for.
318      * @return The list of values or an empty list.
319      */
320     protected List<String> getValues(final Arg arg) {
321         List<String> result = new ArrayList<>();
322         for (Option option : arg.group().getOptions()) {
323             if (option.getLongOpt() != null) {
324                 List<String> args = getArg(option.getLongOpt());
325                 if (args != null) {
326                     result.addAll(args);
327                 }
328             }
329         }
330         return result;
331     }
332 
333     /**
334      * Removes the values for the arg.
335      * @param arg the arg to remove the values for.
336      */
337     protected void removeKey(final Arg arg) {
338         for (Option option : arg.group().getOptions()) {
339             if (option.getLongOpt() != null) {
340                 removeArg(option.getLongOpt());
341             }
342         }
343     }
344 
345 
346     /**
347      * Creates the ReportConfiguration from the ant options.
348      * @return the ReportConfiguration.
349      */
350     public ReportConfiguration getConfiguration() {
351         try {
352             boolean helpLicenses = !getValues(Arg.HELP_LICENSES).isEmpty();
353             removeKey(Arg.HELP_LICENSES);
354 
355             final ReportConfiguration configuration = OptionCollection.parseCommands(new File("."), args().toArray(new String[0]),
356                     o -> DefaultLog.getInstance().warn("Help option not supported"),
357                     true);
358             if (getValues(Arg.OUTPUT_FILE).isEmpty()) {
359                 configuration.setOut(() -> new LogOutputStream(this, Project.MSG_INFO));
360             }
361             DocumentName name = DocumentName.builder(getProject().getBaseDir()).build();
362             configuration.addSource(new ResourceCollectionContainer(name, configuration, nestedResources));
363             configuration.addApprovedLicenseCategories(deprecatedConfig.approvedLicenseCategories);
364             configuration.removeApprovedLicenseCategories(deprecatedConfig.removedLicenseCategories);
365             if (deprecatedConfig.inputFileFilter != null) {
366                 configuration.addExcludedFilter(deprecatedConfig.inputFileFilter);
367             }
368             families.stream().map(Family::build).forEach(configuration::addFamily);
369             licenses.stream().map(License::asBuilder)
370                     .forEach(l -> configuration.addApprovedLicenseCategory(configuration.addLicense(l).getLicenseFamily()));
371             if (helpLicenses) {
372                 new org.apache.rat.help.Licenses(configuration, new PrintWriter(DefaultLog.getInstance().asWriter())).printHelp();
373             }
374             return configuration;
375         } catch (IOException | ImplementationException e) {
376             throw new BuildException(e.getMessage(), e);
377         }
378     }
379 
380     /**
381      * Generates the report.
382      */
383     @Override
384     public void execute() {
385         try {
386             Reporter r = new Reporter(validate(getConfiguration()));
387             r.output(StyleSheets.PLAIN.getStyleSheet(), () -> new ReportConfiguration.NoCloseOutputStream(System.out));
388             r.output();
389         } catch (BuildException e) {
390             throw e;
391         } catch (Exception ioex) {
392             throw new BuildException(ioex);
393         }
394     }
395 
396     /**
397      * validates the task's configuration.
398      */
399     protected ReportConfiguration validate(final ReportConfiguration cfg) {
400         try {
401             cfg.validate(s -> log(s, Project.MSG_WARN));
402         } catch (ConfigurationException e) {
403             throw new BuildException(e.getMessage(), e.getCause());
404         }
405         if (nestedResources == null) {
406             throw new BuildException("You must specify at least one file to create the report for.");
407         }
408         return cfg;
409     }
410 
411     /**
412      * Type for the addLicenseHeaders attribute.
413      * @deprecated No longer required, use stylesheet or xml attributes.
414      */
415     @Deprecated
416     public static class AddLicenseHeaders extends EnumeratedAttribute {
417         /** add license headers and create *.new file */
418         static final String TRUE = "true";
419         /** do not add license headers */
420         static final String FALSE = "false";
421         /** add license headers and overwrite existing files */
422         static final String FORCED = "forced";
423 
424         public AddLicenseHeaders() {
425         }
426 
427         public AddLicenseHeaders(final String s) {
428             setValue(s);
429         }
430 
431         @Override
432         public String[] getValues() {
433             return new String[] { TRUE, FALSE, FORCED };
434         }
435 
436         public org.apache.rat.config.AddLicenseHeaders getNative() {
437             return org.apache.rat.config.AddLicenseHeaders.valueOf(getValue().toUpperCase());
438         }
439     }
440 
441     /**
442      * Type for the addLicenseHeaders attribute.
443      * @deprecated use listLicenses or listFamilies attributes.
444      */
445     @Deprecated
446     public static class ApprovalFilter extends EnumeratedAttribute {
447 
448         public ApprovalFilter() {
449         }
450 
451         public ApprovalFilter(final String s) {
452             setValue(s);
453         }
454 
455         @Override
456         public String[] getValues() {
457             return Arrays.stream(LicenseSetFactory.LicenseFilter.values()).map(LicenseSetFactory.LicenseFilter::name)
458                     .collect(Collectors.toList()).toArray(new String[LicenseSetFactory.LicenseFilter.values().length]);
459         }
460 
461         public LicenseSetFactory.LicenseFilter internalFilter() {
462             return LicenseSetFactory.LicenseFilter.valueOf(getValue());
463         }
464     }
465 
466     /**
467      * A facade for the Logger provided by Ant.
468      */
469     private class Logger implements Log {
470         /** the actual logger */
471         private final org.apache.tools.ant.DefaultLogger delegate = new org.apache.tools.ant.DefaultLogger();
472 
473         @Override
474         public Level getLevel() {
475             switch (delegate.getMessageOutputLevel()) {
476                 case Project.MSG_ERR:
477                     return Level.ERROR;
478                 case Project.MSG_WARN:
479                     return Level.WARN;
480                 case Project.MSG_INFO:
481                     return Level.INFO;
482                 case Project.MSG_VERBOSE:
483                 case Project.MSG_DEBUG:
484                     return Level.DEBUG;
485                 default:
486                     return Level.OFF;
487             }
488         }
489 
490         @Override
491         public void log(final Level level, final String msg) {
492             log(level, msg, null);
493         }
494 
495         @Override
496         public void log(final Level level, final String message, final Throwable throwable) {
497             BuildEvent event = new BuildEvent(Report.this);
498             switch (level) {
499                 case DEBUG:
500                     event.setMessage(message, Project.MSG_DEBUG);
501                     break;
502                 case INFO:
503                     event.setMessage(message, Project.MSG_INFO);
504                     break;
505                 case WARN:
506                     event.setMessage(message, Project.MSG_WARN);
507                     break;
508                 case ERROR:
509                     event.setMessage(message, Project.MSG_ERR);
510                     break;
511                 case OFF:
512                 default:
513                     return;
514             }
515             if (throwable != null) {
516                 event.setException(throwable);
517             }
518             delegate.messageLogged(event);
519         }
520     }
521 }