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.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.io.OutputStreamWriter;
27  import java.io.PrintWriter;
28  import java.io.StringReader;
29  import java.nio.charset.StandardCharsets;
30  
31  import javax.xml.transform.TransformerException;
32  import javax.xml.transform.dom.DOMSource;
33  import javax.xml.transform.stream.StreamResult;
34  
35  import org.apache.commons.io.function.IOSupplier;
36  import org.apache.rat.api.RatException;
37  import org.apache.rat.document.DocumentName;
38  import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
39  import org.apache.rat.report.RatReport;
40  import org.apache.rat.report.claim.ClaimStatistic;
41  import org.apache.rat.report.xml.XmlReportFactory;
42  import org.apache.rat.report.xml.writer.XmlWriter;
43  import org.apache.rat.utils.StandardXmlFactory;
44  import org.w3c.dom.Document;
45  import org.xml.sax.InputSource;
46  import org.xml.sax.SAXException;
47  
48  /**
49   * Class that executes the report as defined in a {@link ReportConfiguration} and stores
50   * the result for later handling.
51   */
52  public class Reporter {
53  
54      /**
55       * Format used for listing licenses.
56       */
57      private static final String LICENSE_FORMAT = "%s:\t%s%n\t\t%s%n";
58  
59      /**
60       * The configuration for the report.
61       */
62      private final ReportConfiguration configuration;
63  
64      /**
65       * The output from the execution.
66       */
67      private Output output;
68  
69      /**
70       * Create the reporter.
71       *
72       * @param configuration the configuration to use.
73       */
74      public Reporter(final ReportConfiguration configuration) {
75          this.configuration = configuration;
76      }
77  
78      /**
79       * Executes the report and builds the output.
80       *
81       * @return the Output object.
82       * @throws RatException on error.
83       */
84      public Output execute() throws RatException {
85          try {
86              Output.Builder builder = Output.builder().configuration(configuration);
87              if (configuration.hasSource()) {
88                  StringBuilder sb = new StringBuilder();
89                  try (XmlWriter writer = new XmlWriter(sb)) {
90                      writer.startDocument();
91                      ClaimStatistic statistic = new ClaimStatistic();
92                      builder.statistic(statistic);
93                      RatReport report = XmlReportFactory.createStandardReport(writer, statistic, configuration);
94                      report.startReport();
95                      configuration.getSources().build().run(report);
96                      report.endReport();
97                      InputSource inputSource = new InputSource(new StringReader(sb.toString()));
98                      builder.document(StandardXmlFactory.documentBuilder().parse(inputSource));
99                  }
100             } else {
101                 builder.document = StandardXmlFactory.documentBuilder().newDocument();
102                 builder.statistic(new ClaimStatistic());
103             }
104             this.output = builder.build();
105             return output;
106         } catch (Exception e) {
107             throw RatException.makeRatException(e);
108         }
109     }
110 
111     /**
112      * Gets the output from the last {@link #execute} call or {@code null} if {@link #execute} has not been called.
113      *
114      * @return the output
115      */
116     public Output getOutput() {
117         return output;
118     }
119 
120     /**
121      * The output from a report run.
122      */
123     public static final class Output {
124         /**
125          * The XML output document.
126          */
127         private final Document document;
128         /**
129          * The claim statics from the execution that generated the document.
130          * May be empty if the Document was read from disk.
131          */
132         private final ClaimStatistic statistic;
133         /**
134          * The configuration that generated the document.
135          */
136         private final ReportConfiguration configuration;
137 
138         /**
139          * Create an output with statistics.
140          *
141          * @param builder the Builder
142          */
143         private Output(final Builder builder) {
144             this.document = builder.document;
145             this.statistic = builder.statistic == null ? new ClaimStatistic() : builder.statistic;
146             this.configuration = builder.configuration == null ? new ReportConfiguration() : builder.configuration;
147         }
148 
149         public static Builder builder() {
150             return new Builder();
151         }
152 
153         /**
154          * Gets the document that was generated during execution.
155          *
156          * @return the document that was generated during execution.
157          */
158         public Document getDocument() {
159             return document;
160         }
161 
162         /**
163          * Get the claim statistics from the run.
164          *
165          * @return the claim statistics.
166          */
167         public ClaimStatistic getStatistic() {
168             return statistic;
169         }
170 
171         public ReportConfiguration getConfiguration() {
172             return configuration;
173         }
174 
175         /**
176          * Formats the report to the output and using the stylesheet found in the report configuration.
177          *
178          * @param config the RAT report configuration.
179          * @throws RatException on error.
180          */
181         public void format(final ReportConfiguration config) throws RatException {
182             format(config.getStyleSheet(), config.getOutput());
183         }
184 
185         /**
186          * Formats the report to the specified output using the stylesheet. It is safe to call this method more than once
187          * in order to generate multiple reports from the same run.
188          *
189          * @param stylesheet the style sheet to use for XSLT formatting.
190          * @param output the output stream to write to.
191          * @throws RatException on error.
192          */
193         public void format(final IOSupplier<InputStream> stylesheet, final IOSupplier<OutputStream> output) throws RatException {
194             try (OutputStream out = output.get();
195                  InputStream styleIn = stylesheet.get()) {
196                 StandardXmlFactory.createTransformer(styleIn).transform(new DOMSource(document),
197                         new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
198             } catch (TransformerException | IOException e) {
199                 throw new RatException(e);
200             }
201         }
202 
203         /**
204          * Lists the licenses on the print writer.
205          *
206          * @param printWriter the print writer to write to.
207          * @param filter the license filter that specifies which licenses to output.
208          */
209         public void listLicenses(final PrintWriter printWriter, final LicenseFilter filter) {
210             printWriter.format("Licenses (%s):%n", filter);
211             configuration.getLicenses(filter)
212                     .forEach(lic -> printWriter.format(LICENSE_FORMAT, lic.getLicenseFamily().getFamilyCategory(),
213                             lic.getLicenseFamily().getFamilyName(), lic.getNote()));
214             printWriter.println();
215         }
216 
217         /**
218          * Lists the licenses on the output specified in the configuration.
219          *
220          * @param filter the license filter that specifies which licenses to output.
221          * @throws IOException if PrintWriter can not be retrieved from configuration.
222          */
223         public void listLicenses(final LicenseFilter filter) throws IOException {
224             try (PrintWriter pw = configuration.getWriter().get()) {
225                 listLicenses(pw, filter);
226             }
227         }
228 
229         /**
230          * Writes a text summary of issues with the run.
231          *
232          * @param appendable the appendable to write to.
233          * @throws IOException on error.
234          */
235         public void writeSummary(final Appendable appendable) throws IOException {
236             appendable.append("RAT summary:").append(System.lineSeparator());
237             for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) {
238                 appendable.append("  ").append(counter.displayName()).append(":  ")
239                         .append(Integer.toString(statistic.getCounter(counter)))
240                         .append(System.lineSeparator());
241             }
242         }
243 
244         public static final class Builder {
245             /**
246              * The document that was generated.
247              */
248             private Document document;
249             /**
250              * The claim statistic from the execution that generated the document.
251              * May be empty if the Document was read from disk.
252              */
253             private ClaimStatistic statistic;
254             /**
255              * The configuration that generated the document
256              */
257             private ReportConfiguration configuration;
258 
259             public Builder document(final Document document) {
260                 this.document = document;
261                 return this;
262             }
263 
264             public Builder document(final String fileName, final DocumentName workingDirectory) {
265                 File inputFile = workingDirectory.resolve(fileName).asFile();
266                 try (InputStream inputStream = new FileInputStream(inputFile)) {
267                     this.document = StandardXmlFactory.documentBuilder().parse(inputStream);
268                 } catch (SAXException | IOException e) {
269                     throw new ConfigurationException("Unable to read file: " + inputFile, e);
270                 }
271                 return this;
272             }
273 
274             public Output build() {
275                 return new Output(this);
276             }
277 
278             public Builder statistic(final ClaimStatistic statistic) {
279                 this.statistic = statistic;
280                 return this;
281             }
282 
283             public Builder statistic(final String fileName, final DocumentName workingDirectory) {
284                 File sourceFile = workingDirectory.resolve(fileName).asFile();
285                 try {
286                     ClaimStatistic newStatistic = new ClaimStatistic();
287                     newStatistic.serDes().deserialize(() -> new FileInputStream(sourceFile));
288                     this.statistic = newStatistic;
289                     return this;
290                 } catch (IOException e) {
291                     throw new ConfigurationException("Unable to read file: " + sourceFile, e);
292                 }
293             }
294 
295             public Builder configuration(final ReportConfiguration configuration) {
296                 this.configuration = configuration;
297                 return this;
298             }
299 
300             public Builder configuration(final String fileName, final DocumentName workingDirectory) {
301                 File configurationFile = workingDirectory.resolve(fileName).asFile();
302                 try {
303                     ReportConfiguration config = new ReportConfiguration();
304                     config.serDes().deserialize(() -> new FileInputStream(configurationFile), workingDirectory);
305                     this.configuration = config;
306                     return this;
307                 } catch (IOException e) {
308                     throw new ConfigurationException("Unable to read file: " + configurationFile, e);
309                 }
310             }
311         }
312     }
313 }