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