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.matchers;
20  
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.Objects;
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.rat.ConfigurationException;
29  import org.apache.rat.analysis.IHeaderMatcher;
30  
31  /**
32   * Defines a factory to produce matchers for an SPDX tag. SPDX tag is of the format
33   * {@code SPDX-License-Identifier: short-name} where {@code short-name} matches
34   * the regex pattern [A-Za-z0-9\.\-]+
35   * <p>
36   * SPDX identifiers are specified by the Software Package Data Exchange(R) also
37   * known as SPDX(R) project from the Linux foundation.
38   * </p>
39   *
40   * @see <a href="https://spdx.dev/ids/">List of Ids at spdx.dev</a>
41   */
42  public class SPDXMatcherFactory {
43  
44      /**
45       * The collection of all matchers produced by this factory.
46       */
47      private static final Map<String, SPDXMatcherFactory.Match> matchers = new HashMap<>();
48  
49      /**
50       * The instance of this factory.
51       */
52      public static final SPDXMatcherFactory INSTANCE = new SPDXMatcherFactory();
53  
54      /**
55       * The regular expression to locate the SPDX license identifier in the text stream
56       */
57      private static Pattern groupSelector = Pattern.compile(".*SPDX-License-Identifier:\\s([A-Za-z0-9\\.\\-]+)");
58  
59      /**
60       * the last line that this factory scanned.
61       */
62      private String lastLine;
63      /**
64       * The last matcer to match the line.
65       */
66      private Match lastMatch;
67  
68      private SPDXMatcherFactory() {
69          lastLine = null;
70      };
71  
72      /**
73       * Creates the spdx matcher.
74       * @param spdxId the spdx name to match.
75       * @return a spdx matcher.
76       */
77      public IHeaderMatcher create(String spdxId) {
78          if (StringUtils.isBlank(spdxId)) {
79              throw new ConfigurationException("'spdx' type matcher requires a name");
80          }
81          Match matcher = matchers.get(spdxId);
82          if (matcher == null) {
83              matcher = new Match(spdxId);
84              matchers.put(spdxId, matcher);
85          }
86          return matcher;
87      }
88  
89      /**
90       * Each matcher calls this method to present the line it is working on.
91       * @param line The line the caller is looking at.
92       * @param caller the Match that is calling this method.
93       * @return true if the caller matches the text.
94       */
95      private boolean check(String line, Match caller) {
96          // if the line has not been seen yet see if we can extract the SPDX id from the line.
97          // if so then see if that name has been registered.  If so then we have a match and set 
98          // lastMatch.
99          if ((lastLine == null || !lastLine.equals(line)) && line.contains("SPDX-License-Identifier")) {
100             Matcher matcher = groupSelector.matcher(line);
101             if (matcher.find()) {
102                 lastMatch = matchers.get(matcher.group(1));
103             } else {
104                 lastMatch = null;
105             }
106         }
107         // see if the caller matches lastMatch.
108         return (lastMatch != null) && caller.spdxId.equals(lastMatch.spdxId);
109     }
110 
111     public class Match extends AbstractSimpleMatcher {
112 
113         String spdxId;
114         /**
115          * Constructor.
116          * 
117          * @param spdxId A regular expression that matches the @{short-name} of the SPDX
118          * Identifier.
119          */
120         Match(final String spdxId) {
121             super("SPDX:" + spdxId);
122             Objects.requireNonNull(spdxId, "SpdxId is required");
123             this.spdxId = spdxId;
124         }
125 
126         @Override
127         protected boolean doMatch(String line) {
128             return SPDXMatcherFactory.this.check(line, this);
129         }
130         
131         @Override
132         public void reset() {
133             super.reset();
134             SPDXMatcherFactory.this.lastMatch = null;
135             
136         }
137     }
138 }