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;
20  
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileNotFoundException;
24  import java.io.FilenameFilter;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.OutputStreamWriter;
28  import java.io.PipedReader;
29  import java.io.PipedWriter;
30  import java.io.PrintStream;
31  import java.io.Writer;
32  
33  import javax.xml.transform.TransformerConfigurationException;
34  
35  import org.apache.commons.cli.CommandLine;
36  import org.apache.commons.cli.HelpFormatter;
37  import org.apache.commons.cli.Option;
38  import org.apache.commons.cli.OptionBuilder;
39  import org.apache.commons.cli.OptionGroup;
40  import org.apache.commons.cli.Options;
41  import org.apache.commons.cli.ParseException;
42  import org.apache.commons.cli.PosixParser;
43  import org.apache.commons.io.filefilter.NotFileFilter;
44  import org.apache.commons.io.filefilter.WildcardFileFilter;
45  import org.apache.rat.api.RatException;
46  import org.apache.rat.report.IReportable;
47  import org.apache.rat.report.RatReport;
48  import org.apache.rat.report.claim.ClaimStatistic;
49  import org.apache.rat.report.xml.XmlReportFactory;
50  import org.apache.rat.report.xml.writer.IXmlWriter;
51  import org.apache.rat.report.xml.writer.impl.base.XmlWriter;
52  import org.apache.rat.walker.ArchiveWalker;
53  import org.apache.rat.walker.DirectoryWalker;
54  
55  
56  public class Report {
57      private static final char EXCLUDE_CLI = 'e';
58      private static final char STYLESHEET_CLI = 's';
59  
60      //@SuppressWarnings("unchecked")
61      public static final void main(String args[]) throws Exception {
62          final ReportConfiguration configuration = new ReportConfiguration();
63          Options opts = buildOptions();
64  
65          PosixParser parser = new PosixParser();
66          CommandLine cl = null;
67          try {
68              cl = parser.parse(opts, args);
69          } catch (ParseException e) {
70              System.err.println("Please use the \"--help\" option to see a list of valid commands and options");
71              System.exit(1);
72          }
73  
74          if (cl.hasOption('h')) {
75              printUsage(opts);
76          }
77  
78          args = cl.getArgs();
79          if (args == null || args.length != 1) {
80              printUsage(opts);
81          } else {
82              Report report = new Report(args[0]);
83  
84              if (cl.hasOption('a')) {
85                  configuration.setAddingLicenses(true);
86                  configuration.setAddingLicensesForced(cl.hasOption('f'));
87                  configuration.setCopyrightMessage(cl.getOptionValue("c"));
88              }
89  
90              if (cl.hasOption(EXCLUDE_CLI)) {
91                  String[] excludes = cl.getOptionValues(EXCLUDE_CLI);
92                  if (excludes != null) {
93                      final FilenameFilter filter = new NotFileFilter(new WildcardFileFilter(excludes));
94                      report.setInputFileFilter(filter);
95                  }
96              }
97  
98              if (cl.hasOption('x')) {
99                  report.report(System.out);
100             } else {
101                 if (!cl.hasOption(STYLESHEET_CLI)) {
102                     report.styleReport(System.out);
103                 } else {
104                     String[] style = cl.getOptionValues(STYLESHEET_CLI);
105                     if (style.length != 1) {
106                         System.err.println("please specify a single stylesheet");
107                         System.exit(1);
108                     }
109                     try {
110                         configuration.setHeaderMatcher(Defaults.createDefaultMatcher());
111                         report(System.out,
112                                 report.getDirectory(System.out),
113                                 new FileInputStream(style[0]),
114                                 configuration);
115                     } catch (FileNotFoundException fnfe) {
116                         System.err.println("stylesheet " + style[0]
117                                            + " doesn't exist");
118                         System.exit(1);
119                     }
120                 }
121             }
122         }
123     }
124 
125     private static Options buildOptions() {
126         Options opts = new Options();
127 
128         Option help = new Option("h", "help", false,
129         "Print help for the RAT command line interface and exit");
130         opts.addOption(help);
131 
132         Option addLicence = new Option(
133                 "a",
134                 "addLicence",
135                 false,
136         "Add the default licence header to any file with an unknown licence that is not in the exclusion list. By default new files will be created with the licence header, to force the modification of existing files use the --force option.");
137         opts.addOption(addLicence);
138 
139         Option write = new Option(
140                 "f",
141                 "force",
142                 false,
143         "Forces any changes in files to be written directly to the source files (i.e. new files are not created)");
144         opts.addOption(write);
145 
146         Option copyright = new Option(
147                 "c",
148                 "copyright",
149                 true,
150         "The copyright message to use in the licence headers, usually in the form of \"Copyright 2008 Foo\"");
151         opts.addOption(copyright);
152 
153         final Option exclude = OptionBuilder
154                             .withArgName("expression")
155                             .withLongOpt("exclude")
156                             .hasArgs()
157                             .withDescription("Excludes files matching <expression>. " +
158                                     "Note that --dir is required when using this parameter. " +
159                                     "Allows multiple arguments.")
160                             .create(EXCLUDE_CLI);
161         opts.addOption(exclude);
162 
163         Option dir = new Option(
164                 "d",
165                 "dir",
166                 false,
167         "Used to indicate source when using --exclude");
168         opts.addOption(dir);
169 
170         OptionGroup outputType = new OptionGroup();
171 
172         Option xml = new Option(
173                 "x",
174                 "xml",
175                 false,
176                 "Output the report in raw XML format.  Not compatible with -s");
177         outputType.addOption(xml);
178 
179         Option xslt = new Option(String.valueOf(STYLESHEET_CLI),
180                                  "stylesheet",
181                                  true,
182                                  "XSLT stylesheet to use when creating the"
183                                  + " report.  Not compatible with -x");
184         outputType.addOption(xslt);
185         opts.addOptionGroup(outputType);
186 
187         return opts;
188     }
189 
190     private static final void printUsage(Options opts) {
191         HelpFormatter f = new HelpFormatter();
192         String header = "Options";
193 
194         StringBuffer footer = new StringBuffer("\n");
195         footer.append("NOTE:\n");
196         footer.append("RAT is really little more than a grep ATM\n");
197         footer.append("RAT is also rather memory hungry ATM\n");
198         footer.append("RAT is very basic ATM\n");
199         footer.append("RAT highlights possible issues\n");
200         footer.append("RAT reports require intepretation\n");
201         footer.append("RAT often requires some tuning before it runs well against a project\n");
202         footer.append("RAT relies on heuristics: it may miss issues\n");
203 
204         f.printHelp("java rat.report [options] [DIR|TARBALL]",
205                 header, opts, footer.toString(), false);
206         System.exit(0);
207     }
208 
209     private final String baseDirectory;
210 
211     private FilenameFilter inputFileFilter = null;
212 
213     private Report(String baseDirectory) {
214         this.baseDirectory = baseDirectory;
215     }
216 
217     /**
218      * Gets the current filter used to select files.
219      * @return current file filter, or null when no filter has been set
220      */
221     public FilenameFilter getInputFileFilter() {
222         return inputFileFilter;
223     }
224 
225     /**
226      * Sets the current filter used to select files.
227      * @param inputFileFilter filter, or null when no filter has been set
228      */
229     public void setInputFileFilter(FilenameFilter inputFileFilter) {
230         this.inputFileFilter = inputFileFilter;
231     }
232 
233     public ClaimStatistic report(PrintStream out) throws Exception {
234         final IReportable base = getDirectory(out);
235         if (base != null) {
236             final ReportConfiguration configuration = new ReportConfiguration();
237             configuration.setHeaderMatcher(Defaults.createDefaultMatcher());
238             return report(base, new OutputStreamWriter(out), configuration);
239         }
240         return null;
241     }
242 
243     private IReportable getDirectory(PrintStream out) {
244         File base = new File(baseDirectory);
245         if (!base.exists()) {
246             out.print("ERROR: ");
247             out.print(baseDirectory);
248             out.print(" does not exist.\n");
249             return null;
250         } 
251 
252         if (base.isDirectory()) {
253             return new DirectoryWalker(base, inputFileFilter);
254         }
255 
256         try {
257                 return new ArchiveWalker(base, inputFileFilter);
258         } catch (IOException ex) {
259             out.print("ERROR: ");
260             out.print(baseDirectory);
261             out.print(" is not valid gzip data.\n");
262             return null;
263         }
264     }
265 
266     /**
267      * Output a report in the default style and default licence
268      * header matcher. 
269      * 
270      * @param out - the output stream to recieve the styled report
271      * @throws Exception
272      */
273     public void styleReport(PrintStream out) throws Exception {
274         final IReportable base = getDirectory(out);
275         if (base != null) {
276             InputStream style = Defaults.getDefaultStyleSheet();
277             final ReportConfiguration configuration = new ReportConfiguration();
278             configuration.setHeaderMatcher(Defaults.createDefaultMatcher());
279             report(out, base, style, configuration);
280         }
281     }
282 
283     /**
284      * Output a report that is styled using a defined stylesheet.
285      * 
286      * @param out the stream to write the report to
287      * @param base the files or directories to report on
288      * @param style an input stream representing the stylesheet to use for styling the report
289      * @param matcher the header matcher for matching licence headers
290      * @param approvedLicenseNames a list of licence families that are approved for use in the project
291      * @throws IOException
292      * @throws TransformerConfigurationException
293      * @throws InterruptedException
294      * @throws RatException
295      */
296     public static void report(PrintStream out, IReportable base, final InputStream style,
297                               ReportConfiguration pConfiguration) 
298             throws IOException, TransformerConfigurationException,  InterruptedException, RatException {
299         report(new OutputStreamWriter(out), base, style, pConfiguration);
300     }
301 
302     /**
303      * 
304      * Output a report that is styled using a defined stylesheet.
305      * 
306      * @param out the writer to write the report to
307      * @param base the files or directories to report on
308      * @param style an input stream representing the stylesheet to use for styling the report
309      * @param matcher the header matcher for matching licence headers
310      * @param approvedLicenseNames a list of licence families that are approved for use in the project
311      * @throws IOException
312      * @throws TransformerConfigurationException
313      * @throws FileNotFoundException
314      * @throws InterruptedException
315      * @throws RatException
316      */
317     public static ClaimStatistic report(Writer out, IReportable base, final InputStream style, 
318             ReportConfiguration pConfiguration) 
319     throws IOException, TransformerConfigurationException, FileNotFoundException, InterruptedException, RatException {
320         PipedReader reader = new PipedReader();
321         PipedWriter writer = new PipedWriter(reader);
322         ReportTransformer transformer = new ReportTransformer(out, style, reader);
323         Thread transformerThread = new Thread(transformer);
324         transformerThread.start();
325         final ClaimStatistic statistic = report(base, writer, pConfiguration);
326         writer.flush();
327         writer.close();
328         transformerThread.join();
329         return statistic;
330     }
331 
332     /**
333      * 
334      * @param container the files or directories to report on
335      * @param out the writer to write the report to
336      * @param matcher the header matcher for matching licence headers
337      * @param approvedLicenseNames a list of licence families that are approved for use in the project
338      * @throws IOException
339      * @throws RatException
340      */
341     public static ClaimStatistic report(final IReportable container, final Writer out,
342             ReportConfiguration pConfiguration) throws IOException, RatException {
343         IXmlWriter writer = new XmlWriter(out);
344         final ClaimStatistic statistic = new ClaimStatistic();
345         RatReport report = XmlReportFactory.createStandardReport(writer, statistic, pConfiguration);
346         report.startReport();
347         container.run(report);
348         report.endReport();
349         writer.closeDocument();
350         return statistic;
351     }
352 }