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.FileWriter;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStreamWriter;
26  import java.io.PrintWriter;
27  import java.util.ArrayList;
28  
29  import javax.xml.transform.TransformerException;
30  
31  import org.apache.tools.ant.BuildException;
32  import org.apache.tools.ant.Project;
33  import org.apache.tools.ant.Task;
34  import org.apache.tools.ant.taskdefs.LogOutputStream;
35  import org.apache.tools.ant.types.EnumeratedAttribute;
36  import org.apache.tools.ant.types.Resource;
37  import org.apache.tools.ant.types.ResourceCollection;
38  import org.apache.tools.ant.types.resources.Union;
39  import org.apache.tools.ant.util.FileUtils;
40  
41  import org.apache.rat.Defaults;
42  import org.apache.rat.ReportConfiguration;
43  import org.apache.rat.analysis.IHeaderMatcher;
44  import org.apache.rat.analysis.util.HeaderMatcherMultiplexer;
45  import org.apache.rat.api.RatException;
46  import org.apache.rat.license.ILicenseFamily;
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>ILicenseMatcher(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 licenseMatchers = new ArrayList();
75  
76      private ArrayList licenseNames = new ArrayList();
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      */
106     public void add(ResourceCollection rc) {
107         if (nestedResources == null) {
108             nestedResources = new Union();
109         }
110         nestedResources.add(rc);
111     }
112 
113     /**
114      * Adds a license matcher.
115      */
116     public void add(IHeaderMatcher matcher) {
117         licenseMatchers.add(matcher);
118     }
119 
120     public void add(ILicenseFamily license) {
121         licenseNames.add(license);
122     }
123 
124     /**
125      * Whether to add the default list of license matchers.
126      */
127     public void setAddDefaultLicenseMatchers(boolean b) {
128         addDefaultLicenseMatchers = b;
129     }
130 
131     /**
132      * Where to send the report to.
133      */
134     public void setReportFile(File f) {
135         reportFile = f;
136     }
137 
138     /**
139      * Which format to use.
140      */
141     public void setFormat(Format f) {
142         if (f == null) {
143             throw new IllegalArgumentException("format must not be null");
144         }
145         format = f;
146     }
147 
148     /**
149      * Wether to add license headers.
150      */
151     public void setAddLicenseHeaders(AddLicenseHeaders pAdd) {
152         if (pAdd == null) {
153             throw new IllegalArgumentException("addLicenseHeaders must not be null");
154         }
155         addLicenseHeaders = pAdd;
156     }
157 
158     /**
159      * Sets the copyright message.
160      */
161     public void setCopyrightMessage(String pMessage) {
162         copyrightMessage = pMessage;
163     }
164     
165     /**
166      * Which stylesheet to use (only meaningful with format='styled').
167      */
168     public void addConfiguredStylesheet(Union u) {
169         if (stylesheet != null || u.size() != 1) {
170             throw new BuildException("You must not specify more than one"
171                                      + " stylesheet.");
172         }
173         stylesheet = (Resource) u.iterator().next();
174     }
175 
176     /**
177      * Generates the report.
178      */
179     public void execute() {
180         validate();
181 
182         PrintWriter out = null;
183         try {
184             if (reportFile == null) {
185                 out = new PrintWriter(
186                           new OutputStreamWriter(
187                               new LogOutputStream(this, Project.MSG_INFO)
188                               )
189                           );
190             } else {
191                 out = new PrintWriter(new FileWriter(reportFile));
192             }
193             createReport(out);
194             out.flush();
195         } catch (IOException ioex) {
196             throw new BuildException(ioex);
197         } catch (TransformerException e) {
198             throw new BuildException(e);
199         } catch (InterruptedException e) {
200             throw new BuildException(e);
201         } catch (RatException e) {
202             throw new BuildException(e);
203         } finally {
204             if (reportFile != null) {
205                 FileUtils.close(out);
206             }
207         }
208     }
209 
210     /**
211      * validates the task's configuration.
212      */
213     private void validate() {
214         if (nestedResources == null) {
215             throw new BuildException("You must specify at least one file to"
216                                      + " create the report for.");
217         }
218         if (!addDefaultLicenseMatchers && licenseMatchers.size() == 0) {
219             throw new BuildException("You must specify at least one license"
220                                      + " matcher");
221         }
222         if (format.getValue().equals(Format.STYLED_KEY)) {
223             if (stylesheet == null) {
224                 throw new BuildException("You must specify a stylesheet when"
225                                          + " using the 'styled' format");
226             }
227             if (!stylesheet.isExists()) {
228                 throw new BuildException("Cannot find specified stylesheet '"
229                                          + stylesheet + "'");
230             }
231         } else if (stylesheet != null) {
232             log("Ignoring stylesheet '" + stylesheet + "' when using format '"
233                 + format.getValue() + "'", Project.MSG_WARN);
234         }
235     }
236 
237     /**
238      * Writes the report to the given stream.
239      * @throws InterruptedException 
240      * @throws TransformerException 
241      * @throws RatException 
242      */
243     private void createReport(PrintWriter out) throws IOException, TransformerException, InterruptedException, RatException {
244         final ReportConfiguration configuration = new ReportConfiguration();
245         configuration.setHeaderMatcher(new HeaderMatcherMultiplexer(getLicenseMatchers()));
246         configuration.setApprovedLicenseNames(getApprovedLicenseNames());
247         if (AddLicenseHeaders.FALSE.equalsIgnoreCase(addLicenseHeaders.getValue())) {
248             // Nothing to do
249         } else if (AddLicenseHeaders.FORCED.equalsIgnoreCase(addLicenseHeaders.getValue())) {
250             configuration.setAddingLicenses(true);
251             configuration.setAddingLicensesForced(true);
252             configuration.setCopyrightMessage(copyrightMessage);
253         } else if (AddLicenseHeaders.TRUE.equalsIgnoreCase(addLicenseHeaders.getValue())) {
254             configuration.setAddingLicenses(true);
255             configuration.setCopyrightMessage(copyrightMessage);
256         } else {
257             throw new BuildException("Invalid value for addLicenseHeaders: " + addLicenseHeaders.getValue());
258         }
259         ResourceCollectionContainer rcElement = new ResourceCollectionContainer(nestedResources);
260         if (format.getValue().equals(Format.XML_KEY)) {
261             org.apache.rat.Report.report(rcElement, out, configuration);
262         } else {
263             InputStream style = null;
264             try {
265                 if (format.getValue().equals(Format.PLAIN_KEY)) {
266                     style = Defaults.getPlainStyleSheet();
267                 } else if (format.getValue().equals(Format.STYLED_KEY)) {
268                     style = stylesheet.getInputStream();
269                 } else {
270                     throw new BuildException("unsupported format '"
271                                              + format.getValue() + "'");
272                 }
273                 org.apache.rat.Report.report(out, rcElement, style,
274                                              configuration);
275             } finally {
276                 FileUtils.close(style);
277             }
278         }
279     }
280 
281     /**
282      * Flattens all nested matchers plus the default matchers (if
283      * required) into a single array.
284      */
285     private IHeaderMatcher[] getLicenseMatchers() {
286         IHeaderMatcher[] matchers = null;
287         if (addDefaultLicenseMatchers) {
288             int nestedSize = licenseMatchers.size();
289             if (nestedSize == 0) {
290                 matchers = Defaults.DEFAULT_MATCHERS;
291             } else {
292                 matchers = new IHeaderMatcher[Defaults.DEFAULT_MATCHERS.length
293                                                + nestedSize];
294                 licenseMatchers.toArray(matchers);
295                 System.arraycopy(Defaults.DEFAULT_MATCHERS, 0, matchers,
296                                  nestedSize, Defaults.DEFAULT_MATCHERS.length);
297             }
298         } else {
299             matchers = (IHeaderMatcher[])
300                 licenseMatchers.toArray(new IHeaderMatcher[0]);
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 = (ILicenseFamily[]) 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         public String[] getValues() {
332             return new String[] {
333                 XML_KEY, STYLED_KEY, PLAIN_KEY
334             };
335         }
336     }
337 
338     /**
339      * Type for the addLicenseHeaders attribute.
340      */
341     public static class AddLicenseHeaders extends EnumeratedAttribute {
342         static final String TRUE = "true";
343         static final String FALSE = "false";
344         static final String FORCED = "forced";
345 
346         public AddLicenseHeaders() {}
347         public AddLicenseHeaders(String s) {
348             setValue(s);
349         }
350         
351         
352         public String[] getValues() {
353             return new String[] {
354                 TRUE, FALSE, FORCED
355             };
356         }
357     }
358 }