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 org.apache.commons.io.IOUtils;
22   import org.apache.rat.Defaults;
23   import org.apache.rat.ReportConfiguration;
24   import org.apache.rat.analysis.IHeaderMatcher;
25   import org.apache.rat.analysis.util.HeaderMatcherMultiplexer;
26   import org.apache.rat.api.RatException;
27   import org.apache.rat.license.ILicenseFamily;
28   import org.apache.tools.ant.BuildException;
29   import org.apache.tools.ant.Project;
30   import org.apache.tools.ant.Task;
31   import org.apache.tools.ant.taskdefs.LogOutputStream;
32   import org.apache.tools.ant.types.EnumeratedAttribute;
33   import org.apache.tools.ant.types.Resource;
34   import org.apache.tools.ant.types.ResourceCollection;
35   import org.apache.tools.ant.types.resources.Union;
36   import org.apache.tools.ant.util.FileUtils;
37  
38   import javax.xml.transform.TransformerException;
39   import java.io.File;
40   import java.io.FileWriter;
41   import java.io.IOException;
42   import java.io.InputStream;
43   import java.io.OutputStreamWriter;
44   import java.io.PrintWriter;
45   import java.util.ArrayList;
46   import java.util.List;
47  
48  /**
49   * A basic Ant task that generates a report on all files specified by
50   * the nested resource collection(s).
51   *
52   * <p>IHeaderMatcher(s) can be specified as nested elements as well.</p>
53   *
54   * <p>The attribute <code>format</code> defines the output format and
55   * can take the values
56   * <ul>
57   *   <li>xml - Rat's native XML output.</li>
58   *   <li>styled - transforms the XML output using the given
59   *   stylesheet.  The stylesheet attribute must be set as well if this
60   *   attribute is used.</li>
61   *   <li>plain - plain text using Rat's built-in stylesheet.  This is
62   *   the default.</li>
63   * </ul>
64   */
65  public class Report extends Task {
66  
67      /**
68       * will hold any nested resource collection
69       */
70      private Union nestedResources;
71      /**
72       * The licenses we want to match on.
73       */
74      private ArrayList<IHeaderMatcher> licenseMatchers = new ArrayList<IHeaderMatcher>();
75  
76      private ArrayList<ILicenseFamily> licenseNames = new ArrayList<ILicenseFamily>();
77  
78      /**
79       * Whether to add the default list of license matchers.
80       */
81      private boolean addDefaultLicenseMatchers = true;
82      /**
83       * Where to send the report.
84       */
85      private File reportFile;
86      /**
87       * Which format to use.
88       */
89      private Format format = Format.PLAIN;
90      /**
91       * Which stylesheet to use.
92       */
93      private Resource stylesheet;
94      /**
95       * Whether to add license headers.
96       */
97      private AddLicenseHeaders addLicenseHeaders = new AddLicenseHeaders(AddLicenseHeaders.FALSE);
98      /**
99       * The copyright message.
100      */
101     private String copyrightMessage;
102 
103     /**
104      * Adds resources that will be checked.
105      * @param rc resource to check.
106      */
107     public void add(ResourceCollection rc) {
108         if (nestedResources == null) {
109             nestedResources = new Union();
110         }
111         nestedResources.add(rc);
112     }
113 
114     /**
115      * @param matcher Adds a license matcher.
116      */
117     public void add(IHeaderMatcher matcher) {
118         licenseMatchers.add(matcher);
119     }
120 
121     public void add(ILicenseFamily license) {
122         licenseNames.add(license);
123     }
124 
125     /**
126      * @param addDefaultLicenseMatchers Whether to add the default list of license matchers.
127      */
128     public void setAddDefaultLicenseMatchers(boolean addDefaultLicenseMatchers) {
129         this.addDefaultLicenseMatchers = addDefaultLicenseMatchers;
130     }
131 
132     /**
133      * Where to send the report to.
134      * @param f report output file.
135      */
136     public void setReportFile(File f) {
137         reportFile = f;
138     }
139 
140     /**
141      * Which format to use.
142      * @param f format. 
143      */
144     public void setFormat(Format f) {
145         if (f == null) {
146             throw new IllegalArgumentException("format must not be null");
147         }
148         format = f;
149     }
150 
151     /**
152      * @param pAdd Whether to add license headers. 
153      */
154     public void setAddLicenseHeaders(AddLicenseHeaders pAdd) {
155         if (pAdd == null) {
156             throw new IllegalArgumentException("addLicenseHeaders must not be null");
157         }
158         addLicenseHeaders = pAdd;
159     }
160 
161     /**
162      * @param pMessage copyright message to set.
163      */
164     public void setCopyrightMessage(String pMessage) {
165         copyrightMessage = pMessage;
166     }
167     
168     /**
169      * Which stylesheet to use (only meaningful with format='styled').
170      * @param u stylesheet.
171      */
172     public void addConfiguredStylesheet(Union u) {
173         if (stylesheet != null || u.size() != 1) {
174             throw new BuildException("You must not specify more than one"
175                                      + " stylesheet.");
176         }
177         stylesheet = (Resource) u.iterator().next();
178     }
179 
180     /**
181      * Generates the report.
182      */
183     @Override
184     public void execute() {
185         validate();
186 
187         PrintWriter out = null;
188         try {
189             if (reportFile == null) {
190                 out = new PrintWriter(
191                           new OutputStreamWriter(
192                               new LogOutputStream(this, Project.MSG_INFO)
193                               )
194                           );
195             } else {
196                 out = new PrintWriter(new FileWriter(reportFile));
197             }
198             createReport(out);
199             out.flush();
200         } catch (IOException ioex) {
201             throw new BuildException(ioex);
202         } catch (TransformerException e) {
203             throw new BuildException(e);
204         } catch (InterruptedException e) {
205             throw new BuildException(e);
206         } catch (RatException e) {
207             throw new BuildException(e);
208         } finally {
209             IOUtils.closeQuietly(out);
210         }
211     }
212 
213     /**
214      * validates the task's configuration.
215      */
216     private void validate() {
217         if (nestedResources == null) {
218             throw new BuildException("You must specify at least one file to"
219                                      + " create the report for.");
220         }
221         if (!addDefaultLicenseMatchers && licenseMatchers.size() == 0) {
222             throw new BuildException("You must specify at least one license"
223                                      + " matcher");
224         }
225         if (format.getValue().equals(Format.STYLED_KEY)) {
226             if (stylesheet == null) {
227                 throw new BuildException("You must specify a stylesheet when"
228                                          + " using the 'styled' format");
229             }
230             if (!stylesheet.isExists()) {
231                 throw new BuildException("Cannot find specified stylesheet '"
232                                          + stylesheet + "'");
233             }
234         } else if (stylesheet != null) {
235             log("Ignoring stylesheet '" + stylesheet + "' when using format '"
236                 + format.getValue() + "'", Project.MSG_WARN);
237         }
238     }
239 
240     /**
241      * Writes the report to the given stream.
242      * 
243      * @param out stream to write report to.
244      * 
245      * @throws IOException in case of I/O errors.
246      * @throws InterruptedException in case of threading errors.
247      * @throws TransformerException in case of XML errors.
248      * @throws RatException in case of general errors.
249      */
250     private void createReport(PrintWriter out) throws IOException, TransformerException, InterruptedException, RatException {
251         final ReportConfiguration configuration = new ReportConfiguration();
252         configuration.setHeaderMatcher(new HeaderMatcherMultiplexer(getLicenseMatchers()));
253         configuration.setApprovedLicenseNames(getApprovedLicenseNames());
254         configuration.setApproveDefaultLicenses(addDefaultLicenseMatchers);
255         
256         if (AddLicenseHeaders.FORCED.equalsIgnoreCase(addLicenseHeaders.getValue())) {
257             configuration.setAddingLicenses(true);
258             configuration.setAddingLicensesForced(true);
259             configuration.setCopyrightMessage(copyrightMessage);
260         } else if (AddLicenseHeaders.TRUE.equalsIgnoreCase(addLicenseHeaders.getValue())) {
261             configuration.setAddingLicenses(true);
262             configuration.setCopyrightMessage(copyrightMessage);
263         } else if (!AddLicenseHeaders.FALSE.equalsIgnoreCase(addLicenseHeaders.getValue())) {
264             throw new BuildException("Invalid value for addLicenseHeaders: " + addLicenseHeaders.getValue());
265         }
266         ResourceCollectionContainer rcElement = new ResourceCollectionContainer(nestedResources);
267         if (format.getValue().equals(Format.XML_KEY)) {
268             org.apache.rat.Report.report(rcElement, out, configuration);
269         } else {
270             InputStream style = null;
271             try {
272                 if (format.getValue().equals(Format.PLAIN_KEY)) {
273                     style = Defaults.getPlainStyleSheet();
274                 } else if (format.getValue().equals(Format.STYLED_KEY)) {
275                     style = stylesheet.getInputStream();
276                 } else {
277                     throw new BuildException("unsupported format '"
278                                              + format.getValue() + "'");
279                 }
280                 org.apache.rat.Report.report(out, rcElement, style,
281                                              configuration);
282             } finally {
283                 FileUtils.close(style);
284             }
285         }
286     }
287 
288     /**
289      * Flattens all nested matchers plus the default matchers (if
290      * required) into a single array.
291      */
292     private List<IHeaderMatcher> getLicenseMatchers() {
293         List<IHeaderMatcher> matchers = new ArrayList<IHeaderMatcher>(Defaults.DEFAULT_MATCHERS);
294         if (addDefaultLicenseMatchers) {
295             int nestedSize = licenseMatchers.size();
296             if (nestedSize != 0) {
297                 matchers.addAll(licenseMatchers);
298             }
299         } else {
300             matchers = new ArrayList<IHeaderMatcher>();
301         }
302         return matchers;
303     }
304 
305     private ILicenseFamily[] getApprovedLicenseNames() {
306         // TODO: add support for adding default licenses
307         ILicenseFamily[] results = null;
308         if (licenseNames.size() > 0) {
309             results = licenseNames.toArray(new ILicenseFamily[0]);
310         }
311         return results;
312     }
313 
314     /**
315      * Type for the format attribute.
316      */
317     public static class Format extends EnumeratedAttribute {
318         static final String XML_KEY = "xml";
319         static final String STYLED_KEY = "styled";
320         static final String PLAIN_KEY = "plain";
321 
322         static final Format PLAIN = new Format(PLAIN_KEY);
323 
324         public Format() { super(); }
325 
326         private Format(String s) {
327             this();
328             setValue(s);
329         }
330 
331         @Override
332         public String[] getValues() {
333             return new String[] {
334                 XML_KEY, STYLED_KEY, PLAIN_KEY
335             };
336         }
337     }
338 
339     /**
340      * Type for the addLicenseHeaders attribute.
341      */
342     public static class AddLicenseHeaders extends EnumeratedAttribute {
343         static final String TRUE = "true";
344         static final String FALSE = "false";
345         static final String FORCED = "forced";
346 
347         public AddLicenseHeaders() {}
348         public AddLicenseHeaders(String s) {
349             setValue(s);
350         }
351         
352         @Override
353         public String[] getValues() {
354             return new String[] {
355                 TRUE, FALSE, FORCED
356             };
357         }
358     }
359 }