Skip to content

Commit f0df1e6

Browse files
authored
Merge pull request spotbugs#104 from h3xstream/master
Add basic syntax highlighter spotbugs#84
2 parents 205d89f + 0d72e75 commit f0df1e6

35 files changed

+2160
-11
lines changed

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
<sonar-java.version>4.0</sonar-java.version>
5555
<fbcontrib.version>7.0.0</fbcontrib.version>
5656
<findsecbugs.version>1.6.0</findsecbugs.version>
57+
58+
5759
</properties>
5860

5961
<dependencies>
@@ -210,6 +212,15 @@
210212
<type>test-jar</type>
211213
<scope>test</scope>
212214
</dependency>
215+
216+
217+
<!-- Utilities use by the lexer -->
218+
<dependency>
219+
<groupId>org.sonarsource.sslr</groupId>
220+
<artifactId>sslr-core</artifactId>
221+
<version>1.21</version>
222+
</dependency>
223+
213224
</dependencies>
214225

215226
<build>

src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.sonar.api.Plugin;
2323
import org.sonar.plugins.findbugs.language.Jsp;
24+
import org.sonar.plugins.findbugs.language.JspSyntaxSensor;
2425
import org.sonar.plugins.findbugs.profiles.FindbugsContribProfile;
2526
import org.sonar.plugins.findbugs.profiles.FindbugsProfile;
2627
import org.sonar.plugins.findbugs.profiles.FindbugsSecurityAuditProfile;
@@ -39,7 +40,10 @@ public class FindbugsPlugin implements Plugin {
3940
@Override
4041
public void define(Context context) {
4142
context.addExtensions(FindbugsConfiguration.getPropertyDefinitions());
42-
context.addExtensions(Arrays.asList(Jsp.class,
43+
context.addExtensions(Arrays.asList(
44+
Jsp.class,
45+
JspSyntaxSensor.class,
46+
4347
FindbugsSensor.class,
4448
FindbugsConfiguration.class,
4549
FindbugsExecutor.class,

src/main/java/org/sonar/plugins/findbugs/FindbugsSensor.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@
4545
import org.sonar.plugins.java.Java;
4646
import org.sonar.plugins.java.api.JavaResourceLocator;
4747

48-
import java.util.HashSet;
49-
import java.util.Set;
50-
5148
public class FindbugsSensor implements Sensor {
5249

5350
private static final Logger LOG = LoggerFactory.getLogger(FindbugsSensor.class);
@@ -195,7 +192,7 @@ protected void insertIssue(ActiveRule rule, InputFile resource, int line, String
195192
* @return File handle of the original class file analyzed
196193
*/
197194
private File findOriginalClassForBug(String className) {
198-
String sourceFile = javaResourceLocator.findSourceFileKeyByClassName(className);
195+
String sourceFile = byteCodeResourceLocator.findSourceFileKeyByClassName(className,javaResourceLocator);
199196
if (sourceFile == null) {
200197
return null;
201198
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* SonarSource :: Web :: Sonar Plugin
3+
* Copyright (c) 2010-2017 SonarSource SA and Matthijs Galesloot
4+
* sonarqube@googlegroups.com
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* 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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.sonar.plugins.findbugs.language;
19+
20+
import com.sonar.sslr.api.TokenType;
21+
import com.sonar.sslr.impl.Lexer;
22+
import com.sonar.sslr.impl.channel.BlackHoleChannel;
23+
import com.sonar.sslr.impl.channel.BomCharacterChannel;
24+
import com.sonar.sslr.impl.channel.IdentifierAndKeywordChannel;
25+
import com.sonar.sslr.impl.channel.UnknownCharacterChannel;
26+
27+
import java.nio.charset.Charset;
28+
29+
import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.commentRegexp;
30+
import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.regexp;
31+
32+
final class JspLexer {
33+
34+
private JspLexer() {
35+
}
36+
37+
public static Lexer create(Charset charset) {
38+
return Lexer.builder()
39+
.withCharset(charset)
40+
41+
.withFailIfNoChannelToConsumeOneCharacter(true)
42+
43+
.withChannel(new BomCharacterChannel())
44+
.withChannel(new BlackHoleChannel("\\s++"))
45+
46+
.withChannel(regexp(JspTokenType.DOCTYPE, "<!DOCTYPE.*>"))
47+
48+
.withChannel(regexp(JspTokenType.TAG, "</?[:\\w]+>?"))
49+
.withChannel(regexp(JspTokenType.TAG, "/?>"))
50+
51+
// JSP comment
52+
.withChannel(commentRegexp("<%--[\\w\\W]*?%>"))
53+
// HTML comment
54+
.withChannel(commentRegexp("<!--[\\w\\W]*?-->"))
55+
// C comment
56+
.withChannel(commentRegexp("/\\*[\\w\\W]*?\\*/"))
57+
// CPP comment
58+
.withChannel(commentRegexp("//[^\n\r]*"))
59+
60+
//JSP Scriptlet
61+
.withChannel(regexp(JspTokenType.EXPRESSION, "<%[\\w\\W]*?%>"))
62+
//EL Expression
63+
.withChannel(regexp(JspTokenType.EXPRESSION, "\\$\\{[\\w\\W]*?\\}"))
64+
65+
.withChannel(regexp(JspTokenType.ATTRIBUTE, "=[\"']{1}[\\w\\W]*?[\"']{1}"))
66+
.withChannel(regexp(JspTokenType.ATTRIBUTE, "=[^\\s'\"=<>`]++"))
67+
68+
.withChannel(new IdentifierAndKeywordChannel("\\w++", true, new TokenType[]{}))
69+
70+
.withChannel(new UnknownCharacterChannel())
71+
72+
.build();
73+
}
74+
75+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.sonar.plugins.findbugs.language;
2+
3+
import com.google.common.collect.ImmutableList;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.sonar.api.batch.fs.FilePredicates;
7+
import org.sonar.api.batch.fs.FileSystem;
8+
import org.sonar.api.batch.fs.InputFile;
9+
import org.sonar.api.batch.rule.CheckFactory;
10+
import org.sonar.api.batch.sensor.Sensor;
11+
import org.sonar.api.batch.sensor.SensorContext;
12+
import org.sonar.api.batch.sensor.SensorDescriptor;
13+
import org.sonar.api.issue.NoSonarFilter;
14+
import org.sonar.api.measures.FileLinesContextFactory;
15+
import org.sonar.plugins.findbugs.language.lex.PageLexer;
16+
import org.sonar.plugins.findbugs.language.visitor.HtmlAstScanner;
17+
import org.sonar.plugins.findbugs.language.visitor.NoSonarScanner;
18+
import org.sonar.plugins.findbugs.language.visitor.WebSourceCode;
19+
20+
import java.io.FileReader;
21+
22+
public class JspSyntaxSensor implements Sensor {
23+
24+
private static final Logger LOG = LoggerFactory.getLogger(JspSyntaxSensor.class);
25+
26+
private final NoSonarFilter noSonarFilter;
27+
private final FileLinesContextFactory fileLinesContextFactory;
28+
29+
public JspSyntaxSensor(NoSonarFilter noSonarFilter, FileLinesContextFactory fileLinesContextFactory, CheckFactory checkFactory) {
30+
this.noSonarFilter = noSonarFilter;
31+
this.fileLinesContextFactory = fileLinesContextFactory;
32+
}
33+
34+
@Override
35+
public void describe(SensorDescriptor descriptor) {
36+
descriptor
37+
.name(Jsp.KEY)
38+
.onlyOnFileType(InputFile.Type.MAIN)
39+
.onlyOnLanguage(Jsp.KEY);
40+
}
41+
42+
@Override
43+
public void execute(SensorContext sensorContext) {
44+
final PageLexer lexer = new PageLexer();
45+
46+
FileSystem fileSystem = sensorContext.fileSystem();
47+
48+
final HtmlAstScanner scanner = setupScanner(sensorContext);
49+
50+
FilePredicates predicates = fileSystem.predicates();
51+
Iterable<InputFile> inputFiles = fileSystem.inputFiles(
52+
predicates.and(
53+
predicates.hasType(InputFile.Type.MAIN),
54+
predicates.hasLanguage(Jsp.KEY))
55+
);
56+
57+
for (InputFile inputFile : inputFiles) {
58+
WebSourceCode sourceCode = new WebSourceCode(inputFile);
59+
60+
try (FileReader reader = new FileReader(inputFile.file())) {
61+
scanner.scan(lexer.parse(reader), sourceCode, fileSystem.encoding());
62+
63+
} catch (Exception e) {
64+
LOG.error("Cannot analyze file " + inputFile.file().getAbsolutePath(), e);
65+
}
66+
}
67+
}
68+
69+
/**
70+
* Create PageScanner with Visitors.
71+
*/
72+
private HtmlAstScanner setupScanner(SensorContext context) {
73+
HtmlAstScanner scanner = new HtmlAstScanner(ImmutableList.of(
74+
new JspTokensVisitor(context),
75+
new NoSonarScanner(noSonarFilter)));
76+
77+
return scanner;
78+
}
79+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* SonarSource :: Web :: Sonar Plugin
3+
* Copyright (c) 2010-2017 SonarSource SA and Matthijs Galesloot
4+
* sonarqube@googlegroups.com
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* 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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.sonar.plugins.findbugs.language;
19+
20+
import com.sonar.sslr.api.AstNode;
21+
import com.sonar.sslr.api.TokenType;
22+
23+
public enum JspTokenType implements TokenType {
24+
TAG,
25+
EXPRESSION,
26+
ATTRIBUTE,
27+
DOCTYPE;
28+
29+
@Override
30+
public String getName() {
31+
return name();
32+
}
33+
34+
@Override
35+
public String getValue() {
36+
return name();
37+
}
38+
39+
@Override
40+
public boolean hasToBeSkippedFromAst(AstNode node) {
41+
return false;
42+
}
43+
44+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* SonarSource :: Web :: Sonar Plugin
3+
* Copyright (c) 2010-2017 SonarSource SA and Matthijs Galesloot
4+
* sonarqube@googlegroups.com
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* 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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.sonar.plugins.findbugs.language;
19+
20+
import com.sonar.sslr.api.GenericTokenType;
21+
import com.sonar.sslr.api.Token;
22+
import com.sonar.sslr.api.TokenType;
23+
import com.sonar.sslr.api.Trivia;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.sonar.api.batch.fs.InputFile;
27+
import org.sonar.api.batch.sensor.SensorContext;
28+
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
29+
import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
30+
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
31+
import org.sonar.plugins.findbugs.language.node.Node;
32+
import org.sonar.plugins.findbugs.language.visitor.DefaultNodeVisitor;
33+
34+
import java.util.List;
35+
36+
public class JspTokensVisitor extends DefaultNodeVisitor {
37+
38+
private static final Logger LOG = LoggerFactory.getLogger(JspTokensVisitor.class);
39+
40+
private final SensorContext context;
41+
42+
public JspTokensVisitor(SensorContext context) {
43+
this.context = context;
44+
}
45+
46+
@Override
47+
public void startDocument(List<Node> nodes) {
48+
try {
49+
highlightAndDuplicate();
50+
} catch (IllegalArgumentException e) {
51+
LOG.warn("Giving up highlighting/handling duplication for file " + getWebSourceCode().inputFile(), e);
52+
}
53+
}
54+
55+
private void highlightAndDuplicate() {
56+
NewHighlighting highlighting = context.newHighlighting();
57+
InputFile inputFile = getWebSourceCode().inputFile();
58+
highlighting.onFile(inputFile);
59+
60+
NewCpdTokens cpdTokens = context.newCpdTokens();
61+
cpdTokens.onFile(inputFile);
62+
63+
for (Token token : JspLexer.create(context.fileSystem().encoding()).lex(inputFile.file())) {
64+
TokenType tokenType = token.getType();
65+
if (!tokenType.equals(GenericTokenType.EOF)) {
66+
TokenLocation tokenLocation = new TokenLocation(token);
67+
cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startCharacter(), tokenLocation.endLine(), tokenLocation.endCharacter(), token.getValue());
68+
}
69+
if (tokenType.equals(JspTokenType.DOCTYPE)) {
70+
highlight(highlighting, token, TypeOfText.STRUCTURED_COMMENT);
71+
} else if (tokenType.equals(JspTokenType.EXPRESSION)) {
72+
highlight(highlighting, token, TypeOfText.ANNOTATION);
73+
} else if (tokenType.equals(JspTokenType.TAG)) {
74+
highlight(highlighting, token, TypeOfText.KEYWORD);
75+
} else if (tokenType.equals(JspTokenType.ATTRIBUTE)) {
76+
TokenLocation tokenLocation = new TokenLocation(token);
77+
highlighting.highlight(tokenLocation.startLine(), tokenLocation.startCharacter() + /* = */ 1, tokenLocation.endLine(), tokenLocation.endCharacter(), TypeOfText.STRING);
78+
}
79+
for (Trivia trivia : token.getTrivia()) {
80+
highlight(highlighting, trivia.getToken(), TypeOfText.COMMENT);
81+
}
82+
}
83+
84+
highlighting.save();
85+
cpdTokens.save();
86+
}
87+
88+
private static void highlight(NewHighlighting highlighting, Token token, TypeOfText typeOfText) {
89+
TokenLocation tokenLocation = new TokenLocation(token);
90+
highlighting.highlight(tokenLocation.startLine(), tokenLocation.startCharacter(), tokenLocation.endLine(), tokenLocation.endCharacter(), typeOfText);
91+
}
92+
93+
}

0 commit comments

Comments
 (0)