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