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.analysis.license;
20  
21  import java.util.Locale;
22  
23  import org.apache.rat.analysis.IHeaderMatcher;
24  import org.apache.rat.analysis.RatHeaderAnalysisException;
25  import org.apache.rat.api.Document;
26  import org.apache.rat.api.MetaData.Datum;
27  
28  /**
29   * Accumulates all letters and numbers contained inside the header and
30   * compares it to the full text of a given license (after reducing it
31   * to letters and numbers as well).
32   *
33   * <p>The text comparison is case insensitive but assumes only
34   * characters in the US-ASCII charset are being matched.</p>
35   *
36   * @since Rat 0.9
37   */
38  public class FullTextMatchingLicense extends BaseLicense
39      implements IHeaderMatcher {
40  
41      // Number of match characters assumed to be present on first line
42      private static final int DEFAULT_INITIAL_LINE_LENGTH = 20;
43  
44      private String fullText;
45      
46      private String firstLine;
47  
48      private boolean seenFirstLine = false;
49  
50      private final StringBuilder buffer = new StringBuilder();
51  
52      public FullTextMatchingLicense() {
53      }
54  
55      protected FullTextMatchingLicense(Datum licenseFamilyCategory,
56                                        Datum licenseFamilyName,
57                                        String notes,
58                                        String fullText) {
59          super(licenseFamilyCategory, licenseFamilyName, notes);
60          setFullText(fullText);
61      }
62  
63      public final void setFullText(String text) {
64          int offset = text.indexOf('\n');
65          if (offset == -1) {
66              offset = Math.min(DEFAULT_INITIAL_LINE_LENGTH, text.length());
67          }
68          firstLine = prune(text.substring(0, offset)).toLowerCase(Locale.ENGLISH);
69          fullText = prune(text).toLowerCase(Locale.ENGLISH);
70          init();
71      }
72  
73      public final boolean hasFullText() {
74          return fullText != null;
75      }
76  
77      public boolean match(Document subject, String line) throws RatHeaderAnalysisException {
78          final String inputToMatch = prune(line).toLowerCase(Locale.ENGLISH);
79          if (seenFirstLine) { // Accumulate more input
80              buffer.append(inputToMatch);
81          } else {
82              int offset = inputToMatch.indexOf(firstLine);
83              if (offset >= 0) {
84                  // we have a match, save the text starting with the match
85                  buffer.append(inputToMatch.substring(offset));
86                  seenFirstLine = true;
87                  // Drop out to check whether full text is matched
88              } else {
89                  // we assume that the first line must appear in a single line
90                  return false; // no more to do here
91              }
92          }
93   
94          if (buffer.length() >= fullText.length()) { // we have enough data to match
95              if (buffer.toString().contains(fullText)) {
96                  reportOnLicense(subject);
97                  return true; // we found a match
98              } else { // buffer contains first line but does not contain full text
99                  // It's possible that the buffer contains the first line again
100                 int offset = buffer.substring(1).toString().indexOf(firstLine);
101                 if (offset >= 0) { // first line found again
102                     buffer.delete(0,offset); // reset buffer to the new start
103                 } else { // buffer does not even contain first line, so cannot be used to match full text
104                     init();
105                 }
106             }
107         }
108         return false;
109     }
110 
111     public void reset() {
112         init();
113     }
114 
115     // This is called indirectly from a ctor so must be final or private
116     private void init() {
117         buffer.setLength(0);
118         seenFirstLine = false;
119     }
120 }