1 package org.apache.rat.analysis;
2 /*
3 * Licensed to the Apache Software Foundation (ASF) under one *
4 * or more contributor license agreements. See the NOTICE file *
5 * distributed with this work for additional information *
6 * regarding copyright ownership. The ASF licenses this file *
7 * to you under the Apache License, Version 2.0 (the *
8 * "License"); you may not use this file except in compliance *
9 * with the License. You may obtain a copy of the License at *
10 * *
11 * http://www.apache.org/licenses/LICENSE-2.0 *
12 * *
13 * Unless required by applicable law or agreed to in writing, *
14 * software distributed under the License is distributed on an *
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
16 * KIND, either express or implied. See the License for the *
17 * specific language governing permissions and limitations *
18 * under the License. *
19 */
20
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.Reader;
24 import java.util.Locale;
25 import java.util.Objects;
26
27 import org.apache.commons.collections4.set.UnmodifiableSortedSet;
28 import org.apache.rat.ConfigurationException;
29 import org.apache.rat.analysis.matchers.FullTextMatcher;
30 import org.apache.rat.api.Document;
31 import org.apache.rat.license.ILicense;
32
33 /**
34 * Reads from a stream to check for a license.
35 * <p>
36 * <strong>Note</strong> that this class is not thread safe.
37 * </p>
38 */
39 public final class HeaderCheckWorker {
40
41 /*
42 * TODO revisit this class. It is only used in one place and can be moved inline
43 * as the DocumentHeaderAnalyser states. However, it may also be possible to
44 * make the entire set thread safe so that multiple files can be checked
45 * simultaneously.
46 */
47 /**
48 * The default number of header lines to read while looking for the license
49 * information.
50 */
51 public static final int DEFAULT_NUMBER_OF_RETAINED_HEADER_LINES = 50;
52 /** The number of header lines to retain for processing */
53 private final int numberOfRetainedHeaderLines;
54 /** The BufferedReader used to read the lines */
55 private final BufferedReader reader;
56 /** The licenses to check for match */
57 private final UnmodifiableSortedSet<ILicense> licenses;
58 /** The document being processed */
59 private final Document document;
60 /** The matcher for generated headers */
61 private final IHeaderMatcher generatedMatcher;
62
63 /**
64 * Read the input and perform the header check.
65 * <p>
66 * The number of lines indicates how many lines from the top of the file will be read for processing.
67 *
68 * @param reader the reader for the document.
69 * @param numberOfLines the number of lines to read from the header.
70 * @return the IHeaders instance for the header.
71 * @throws IOException on input failure
72 */
73 public static IHeaders readHeader(final BufferedReader reader, final int numberOfLines) throws IOException {
74 final StringBuilder headers = new StringBuilder();
75 int headerLinesRead = 0;
76 String line;
77
78 while (headerLinesRead < numberOfLines && (line = reader.readLine()) != null) {
79 headers.append(line).append(System.lineSeparator());
80 }
81 final String raw = headers.toString();
82 final String pruned = FullTextMatcher.prune(raw).toLowerCase(Locale.ENGLISH);
83 return new IHeaders() {
84 @Override
85 public String raw() {
86 return raw;
87 }
88
89 @Override
90 public String pruned() {
91 return pruned;
92 }
93
94 @Override
95 public String toString() {
96 return this.getClass().getSimpleName();
97 }
98 };
99 }
100
101 /**
102 * Convenience constructor wraps given <code>Reader</code> in a
103 * <code>BufferedReader</code>.
104 *
105 * @param generatedMatcher the matcher for generated headers.
106 * @param reader the reader on the document. Not null.
107 * @param licenses the licenses to check against. Not null.
108 * @param name the document that is being checked. Possibly null.
109 */
110 public HeaderCheckWorker(final IHeaderMatcher generatedMatcher, final Reader reader, final UnmodifiableSortedSet<ILicense> licenses, final Document name) {
111 this(generatedMatcher, reader, DEFAULT_NUMBER_OF_RETAINED_HEADER_LINES, licenses, name);
112 }
113
114 /**
115 * Constructs a check worker for the license against the specified document.
116 *
117 * @param generatedMatcher The matcher for generated headers.
118 * @param reader The reader on the document. Not null.
119 * @param numberOfRetainedHeaderLine the maximum number of lines to read to find
120 * the license information.
121 * @param licenses The licenses to check against. Not null.
122 * @param document The document that is being checked. Possibly null.
123 */
124 public HeaderCheckWorker(final IHeaderMatcher generatedMatcher, final Reader reader,
125 final int numberOfRetainedHeaderLine, final UnmodifiableSortedSet<ILicense> licenses,
126 final Document document) {
127 Objects.requireNonNull(reader, "Reader may not be null");
128 Objects.requireNonNull(licenses, "Licenses may not be null");
129 if (numberOfRetainedHeaderLine < 0) {
130 throw new ConfigurationException("numberOfRetainedHeaderLine may not be less than zero");
131 }
132 this.reader = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
133 this.numberOfRetainedHeaderLines = numberOfRetainedHeaderLine;
134 this.licenses = licenses;
135 this.document = document;
136 this.generatedMatcher = generatedMatcher;
137 }
138
139 /**
140 * Read the input and perform the header check.
141 *
142 * @throws RatHeaderAnalysisException in case of I/O exceptions.
143 */
144 public void read() throws RatHeaderAnalysisException {
145 try {
146 final IHeaders headers = readHeader(reader, numberOfRetainedHeaderLines);
147 if (generatedMatcher.matches(headers)) {
148 document.getMetaData().setDocumentType(Document.Type.IGNORED);
149 } else {
150 licenses.stream().filter(lic -> lic.matches(headers)).forEach(document.getMetaData()::reportOnLicense);
151 if (!document.getMetaData().detectedLicense()) {
152 document.getMetaData().reportOnLicense(UnknownLicense.INSTANCE);
153 }
154 }
155 } catch (IOException e) {
156 throw new RatHeaderAnalysisException("Cannot read header for " + document, e);
157 } finally {
158 licenses.forEach(ILicense::reset);
159 }
160 }
161 }