]> source.dussan.org Git - sonarqube.git/blob
b479000d013bbf698ec97a40dfa4119f63fe4df5
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2013 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * SonarQube is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * SonarQube is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20
21 package org.sonar.plugins.core.issue.ignore.scanner;
22
23 import com.google.common.collect.Lists;
24 import com.google.common.collect.Sets;
25 import org.apache.commons.io.FileUtils;
26 import org.apache.commons.lang.StringUtils;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.sonar.api.BatchExtension;
30 import org.sonar.plugins.core.issue.ignore.pattern.ExclusionPatternInitializer;
31 import org.sonar.plugins.core.issue.ignore.pattern.IssuePattern;
32 import org.sonar.plugins.core.issue.ignore.pattern.LineRange;
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.nio.charset.Charset;
37 import java.util.List;
38 import java.util.Set;
39
40 public class RegexpScanner implements BatchExtension {
41
42   private static final Logger LOG = LoggerFactory.getLogger(RegexpScanner.class);
43
44   private ExclusionPatternInitializer exclusionPatternInitializer;
45   private List<java.util.regex.Pattern> allFilePatterns;
46   private List<DoubleRegexpMatcher> blockMatchers;
47
48   // fields to be reset at every new scan
49   private DoubleRegexpMatcher currentMatcher;
50   private int fileLength;
51   private List<LineExclusion> lineExclusions;
52   private LineExclusion currentLineExclusion;
53
54   public RegexpScanner(ExclusionPatternInitializer patternsInitializer) {
55     this.exclusionPatternInitializer = patternsInitializer;
56
57     lineExclusions = Lists.newArrayList();
58     allFilePatterns = Lists.newArrayList();
59     blockMatchers = Lists.newArrayList();
60
61     for (IssuePattern pattern : patternsInitializer.getAllFilePatterns()) {
62       allFilePatterns.add(java.util.regex.Pattern.compile(pattern.getAllFileRegexp()));
63     }
64     for (IssuePattern pattern : patternsInitializer.getBlockPatterns()) {
65       blockMatchers.add(new DoubleRegexpMatcher(
66           java.util.regex.Pattern.compile(pattern.getBeginBlockRegexp()),
67           java.util.regex.Pattern.compile(pattern.getEndBlockRegexp())));
68     }
69
70     init();
71   }
72
73   private void init() {
74     currentMatcher = null;
75     fileLength = 0;
76     lineExclusions.clear();
77     currentLineExclusion = null;
78   }
79
80   public void scan(String resource, File file, Charset sourcesEncoding) throws IOException {
81     LOG.debug("Scanning {}", resource);
82     init();
83
84     List<String> lines = FileUtils.readLines(file, sourcesEncoding.name());
85     int lineIndex = 0;
86     for (String line : lines) {
87       lineIndex++;
88       if (line.trim().length() == 0) {
89         continue;
90       }
91
92       // first check the single regexp patterns that can be used to totally exclude a file
93       for (java.util.regex.Pattern pattern : allFilePatterns) {
94         if (pattern.matcher(line).find()) {
95           exclusionPatternInitializer.getPatternMatcher().addPatternToExcludeResource(resource);
96           // nothing more to do on this file
97           LOG.debug("- Exclusion pattern '{}': every violation in this file will be ignored.", pattern);
98           return;
99         }
100       }
101
102       // then check the double regexps if we're still here
103       checkDoubleRegexps(line, lineIndex);
104     }
105
106     if (currentMatcher != null && !currentMatcher.hasSecondPattern()) {
107       // this will happen when there is a start block regexp but no end block regexp
108       endExclusion(lineIndex + 1);
109     }
110
111     // now create the new line-based pattern for this file if there are exclusions
112     fileLength = lineIndex;
113     if (!lineExclusions.isEmpty()) {
114       Set<LineRange> lineRanges = convertLineExclusionsToLineRanges();
115       LOG.debug("- Line exclusions found: {}", lineRanges);
116       exclusionPatternInitializer.getPatternMatcher().addPatternToExcludeLines(resource, lineRanges);
117     }
118   }
119
120   private Set<LineRange> convertLineExclusionsToLineRanges() {
121     Set<LineRange> lineRanges = Sets.newHashSet();
122     for (LineExclusion lineExclusion : lineExclusions) {
123       lineRanges.add(lineExclusion.toLineRange());
124     }
125     return lineRanges;
126   }
127
128   private void checkDoubleRegexps(String line, int lineIndex) {
129     if (currentMatcher == null) {
130       for (DoubleRegexpMatcher matcher : blockMatchers) {
131         if (matcher.matchesFirstPattern(line)) {
132           startExclusion(lineIndex);
133           currentMatcher = matcher;
134           break;
135         }
136       }
137     } else {
138       if (currentMatcher.matchesSecondPattern(line)) {
139         endExclusion(lineIndex);
140         currentMatcher = null;
141       }
142     }
143   }
144
145   private void startExclusion(int lineIndex) {
146     currentLineExclusion = new LineExclusion(lineIndex);
147     lineExclusions.add(currentLineExclusion);
148   }
149
150   private void endExclusion(int lineIndex) {
151     currentLineExclusion.setEnd(lineIndex);
152     currentLineExclusion = null;
153   }
154
155   private class LineExclusion {
156
157     private int start;
158     private int end;
159
160     LineExclusion(int start) {
161       this.start = start;
162       this.end = -1;
163     }
164
165     void setEnd(int end) {
166       this.end = end;
167     }
168
169     public LineRange toLineRange() {
170       return new LineRange(start, end == -1 ? fileLength : end);
171     }
172
173   }
174
175   private static class DoubleRegexpMatcher {
176
177     private java.util.regex.Pattern firstPattern;
178     private java.util.regex.Pattern secondPattern;
179
180     DoubleRegexpMatcher(java.util.regex.Pattern firstPattern, java.util.regex.Pattern secondPattern) {
181       this.firstPattern = firstPattern;
182       this.secondPattern = secondPattern;
183     }
184
185     boolean matchesFirstPattern(String line) {
186       return firstPattern.matcher(line).find();
187     }
188
189     boolean matchesSecondPattern(String line) {
190       return hasSecondPattern() && secondPattern.matcher(line).find();
191     }
192
193     boolean hasSecondPattern() {
194       return StringUtils.isNotEmpty(secondPattern.toString());
195     }
196   }
197
198 }