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 
414             final ReportConfiguration configuration = OptionCollection.parseCommands(new File("."), args().toArray(new String[0]),
415                     o -> DefaultLog.getInstance().warn("Help option not supported"),
416                     true);
417             if (getValues(Arg.OUTPUT_FILE).isEmpty()) {
418                 configuration.setOut(() -> new LogOutputStream(this, Project.MSG_INFO));
419             }
420             DocumentName name = DocumentName.builder(getProject().getBaseDir()).build();
421             configuration.addSource(new ResourceCollectionContainer(name, configuration, nestedResources));
422             configuration.addApprovedLicenseCategories(deprecatedConfig.approvedLicenseCategories);
423             configuration.removeApprovedLicenseCategories(deprecatedConfig.removedLicenseCategories);
424             if (deprecatedConfig.inputFileFilter != null) {
425                 configuration.addExcludedFilter(deprecatedConfig.inputFileFilter);
426             }
427             families.stream().map(Family::build).forEach(configuration::addFamily);
428             licenses.stream().map(License::asBuilder)
429                     .forEach(l -> configuration.addApprovedLicenseCategory(configuration.addLicense(l).getLicenseFamily()));
430             if (helpLicenses) {
431                 new org.apache.rat.help.Licenses(configuration, new PrintWriter(DefaultLog.getInstance().asWriter())).printHelp();
432             }
433             return configuration;
434         } catch (IOException | ImplementationException e) {
435             throw new BuildException(e.getMessage(), e);
436         }
437     }
438 
439     /**
440      * Generates the report.
441      */
442     @Override
443     public void execute() {
444         try {
445             Reporter r = new Reporter(validate(getConfiguration()));
446             r.output(StyleSheets.PLAIN.getStyleSheet(), () -> new ReportConfiguration.NoCloseOutputStream(System.out));
447             r.output();
448         } catch (BuildException e) {
449             throw e;
450         } catch (Exception ioex) {
451             throw new BuildException(ioex);
452         }
453     }
454 
455     /**
456      * Validates the task's configuration.
457      */
458     protected ReportConfiguration validate(final ReportConfiguration cfg) {
459         try {
460             cfg.validate(s -> log(s, Project.MSG_WARN));
461         } catch (ConfigurationException e) {
462             throw new BuildException(e.getMessage(), e.getCause());
463         }
464         if (nestedResources == null) {
465             throw new BuildException("You must specify at least one file to create the report for.");
466         }
467         return cfg;
468     }
469 
470     /**
471      * Add headers to files that do not have them.
472      *
473      * @deprecated use &lt;editCopyright&gt; amd &lt;editOverwrite&gt; instead.
474      */
475     @Deprecated
476     public static class AddLicenseHeaders extends EnumeratedAttribute {
477         /**
478          * add license headers and create *.new file
479          */
480         static final String TRUE = "true";
481         /**
482          * do not add license headers
483          */
484         static final String FALSE = "false";
485         /**
486          * add license headers and overwrite existing files
487          */
488         static final String FORCED = "forced";
489 
490         public AddLicenseHeaders() {
491         }
492 
493         public AddLicenseHeaders(final String s) {
494             setValue(s);
495         }
496 
497         @Override
498         public String[] getValues() {
499             return new String[]{TRUE, FALSE, FORCED};
500         }
501 
502         public org.apache.rat.config.AddLicenseHeaders getNative() {
503             return org.apache.rat.config.AddLicenseHeaders.valueOf(getValue().toUpperCase());
504         }
505     }
506 
507     /**
508      * Specify the licenses that are approved.
509      *
510      * @deprecated use listLicenses or listFamilies attributes.
511      */
512     @Deprecated
513     public static class ApprovalFilter extends EnumeratedAttribute {
514 
515         public ApprovalFilter() {
516         }
517 
518         public ApprovalFilter(final String s) {
519             setValue(s);
520         }
521 
522         @Override
523         public String[] getValues() {
524             return Arrays.stream(LicenseSetFactory.LicenseFilter.values()).map(LicenseSetFactory.LicenseFilter::name)
525                     .toList().toArray(new String[LicenseSetFactory.LicenseFilter.values().length]);
526         }
527 
528         public LicenseSetFactory.LicenseFilter internalFilter() {
529             return LicenseSetFactory.LicenseFilter.valueOf(getValue());
530         }
531     }
532 
533     @Override
534     public void log(final String msg, final int msgLevel) {
535         if (getProject() != null) {
536             getProject().log(msg, msgLevel);
537         } else {
538             DefaultLog.createDefault().log(fromProjectLevel(msgLevel), msg);
539         }
540     }
541 
542     @Override
543     public void log(final String msg, final Throwable t, final int msgLevel) {
544         if (getProject() == null) {
545             log(Log.formatLogEntry(msg, t), msgLevel);
546         } else {
547             getProject().log(this, msg, t, msgLevel);
548         }
549     }
550 
551     /**
552      * Converts to RAT log level from Ant Project log level.
553      * @param level the Ant Project log level to convert.
554      * @return the equivalent RAT log level.
555      */
556     public static Log.Level fromProjectLevel(final int level) {
557         return switch (level) {
558             case Project.MSG_DEBUG, Project.MSG_VERBOSE -> Log.Level.DEBUG;
559             case Project.MSG_INFO -> Log.Level.INFO;
560             case Project.MSG_WARN -> Log.Level.WARN;
561             case Project.MSG_ERR -> Log.Level.ERROR;
562             default -> Log.Level.OFF;
563         };
564     }
565 
566     /**
567      * Converts RAT log level to Ant Project log level.
568      * @param level the RAT log level to convert.
569      * @return the equivalent Ant Project log level.
570      */
571     static int toProjectLevel(final Log.Level level) {
572         return switch (level) {
573             case DEBUG -> Project.MSG_DEBUG;
574             case INFO -> Project.MSG_INFO;
575             case WARN -> Project.MSG_WARN;
576             case ERROR -> Project.MSG_ERR;
577             default -> -1;
578         };
579     }
580 
581     /**
582      * A facade for the Logger provided by Ant.
583      */
584     private final class Logger implements Log {
585         @Override
586         public Level getLevel() {
587             return Level.DEBUG;
588         }
589 
590         @Override
591         public void log(final Log.Level level, final String message, final Throwable throwable) {
592             log(level, Log.formatLogEntry(message, throwable));
593         }
594 
595         @Override
596         public void log(final Level level, final String msg) {
597             Report.this.log(msg, toProjectLevel(level));
598         }
599     }
600 }