1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.nio.charset.StandardCharsets;
25 import java.util.List;
26 import java.util.ListIterator;
27 import java.util.Locale;
28
29 import org.antlr.v4.runtime.BaseErrorListener;
30 import org.antlr.v4.runtime.CharStream;
31 import org.antlr.v4.runtime.CharStreams;
32 import org.antlr.v4.runtime.CommonToken;
33 import org.antlr.v4.runtime.CommonTokenStream;
34 import org.antlr.v4.runtime.RecognitionException;
35 import org.antlr.v4.runtime.Recognizer;
36 import org.antlr.v4.runtime.Token;
37
38 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
39 import com.puppycrawl.tools.checkstyle.api.DetailAST;
40 import com.puppycrawl.tools.checkstyle.api.FileContents;
41 import com.puppycrawl.tools.checkstyle.api.FileText;
42 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
43 import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
44 import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
45 import com.puppycrawl.tools.checkstyle.utils.ParserUtil;
46
47
48
49
50
51
52 public final class JavaParser {
53
54
55
56
57 public enum Options {
58
59
60
61
62 WITH_COMMENTS,
63
64
65
66
67 WITHOUT_COMMENTS,
68
69 }
70
71
72 private JavaParser() {
73 }
74
75
76
77
78
79
80
81
82 public static DetailAST parse(FileContents contents)
83 throws CheckstyleException {
84 final String fullText = contents.getText().getFullText().toString();
85 final CharStream codePointCharStream = CharStreams.fromString(fullText);
86 final JavaLanguageLexer lexer = new JavaLanguageLexer(codePointCharStream, true);
87 lexer.setCommentListener(contents);
88
89 final CommonTokenStream tokenStream = new CommonTokenStream(lexer);
90 final JavaLanguageParser parser =
91 new JavaLanguageParser(tokenStream, JavaLanguageParser.CLEAR_DFA_LIMIT);
92 parser.setErrorHandler(new CheckstyleParserErrorStrategy());
93 parser.removeErrorListeners();
94 parser.addErrorListener(new CheckstyleErrorListener());
95
96 final JavaLanguageParser.CompilationUnitContext compilationUnit;
97 try {
98 compilationUnit = parser.compilationUnit();
99 }
100 catch (IllegalStateException ex) {
101 final String exceptionMsg = String.format(Locale.ROOT,
102 "%s occurred while parsing file %s.",
103 ex.getClass().getSimpleName(), contents.getFileName());
104 throw new CheckstyleException(exceptionMsg, ex);
105 }
106
107 return new JavaAstVisitor(tokenStream).visit(compilationUnit);
108 }
109
110
111
112
113
114
115
116
117
118 public static DetailAST parseFileText(FileText text, Options options)
119 throws CheckstyleException {
120 final FileContents contents = new FileContents(text);
121 final DetailAST ast = parse(contents);
122 if (options == Options.WITH_COMMENTS) {
123 appendHiddenCommentNodes(ast);
124 }
125 return ast;
126 }
127
128
129
130
131
132
133
134
135
136
137 public static DetailAST parseFile(File file, Options options)
138 throws IOException, CheckstyleException {
139 final FileText text = new FileText(file,
140 StandardCharsets.UTF_8.name());
141 return parseFileText(text, options);
142 }
143
144
145
146
147
148
149
150
151
152 public static DetailAST appendHiddenCommentNodes(DetailAST root) {
153 DetailAST curNode = root;
154 DetailAST lastNode = root;
155
156 while (curNode != null) {
157 lastNode = curNode;
158
159 final List<Token> hiddenBefore = ((DetailAstImpl) curNode).getHiddenBefore();
160 if (hiddenBefore != null) {
161 DetailAST currentSibling = curNode;
162
163 final ListIterator<Token> reverseCommentsIterator =
164 hiddenBefore.listIterator(hiddenBefore.size());
165
166 while (reverseCommentsIterator.hasPrevious()) {
167 final DetailAST newCommentNode =
168 createCommentAstFromToken((CommonToken)
169 reverseCommentsIterator.previous());
170 ((DetailAstImpl) currentSibling).addPreviousSibling(newCommentNode);
171
172 currentSibling = newCommentNode;
173 }
174 }
175
176 DetailAST toVisit = curNode.getFirstChild();
177 while (curNode != null && toVisit == null) {
178 toVisit = curNode.getNextSibling();
179 curNode = curNode.getParent();
180 }
181 curNode = toVisit;
182 }
183 if (lastNode != null) {
184 final List<Token> hiddenAfter = ((DetailAstImpl) lastNode).getHiddenAfter();
185 if (hiddenAfter != null) {
186 DetailAST currentSibling = lastNode;
187 for (Token token : hiddenAfter) {
188 final DetailAST newCommentNode =
189 createCommentAstFromToken((CommonToken) token);
190
191 ((DetailAstImpl) currentSibling).addNextSibling(newCommentNode);
192
193 currentSibling = newCommentNode;
194 }
195 }
196 }
197 return root;
198 }
199
200
201
202
203
204
205
206
207 private static DetailAST createCommentAstFromToken(CommonToken token) {
208 final DetailAST commentAst;
209 if (token.getType() == TokenTypes.SINGLE_LINE_COMMENT) {
210 commentAst = createSlCommentNode(token);
211 }
212 else {
213 commentAst = ParserUtil.createBlockCommentNode(token);
214 }
215 return commentAst;
216 }
217
218
219
220
221
222
223
224 private static DetailAST createSlCommentNode(Token token) {
225 final DetailAstImpl slComment = new DetailAstImpl();
226 slComment.setType(TokenTypes.SINGLE_LINE_COMMENT);
227 slComment.setText("//");
228
229 slComment.setColumnNo(token.getCharPositionInLine());
230 slComment.setLineNo(token.getLine());
231
232 final DetailAstImpl slCommentContent = new DetailAstImpl();
233 slCommentContent.setType(TokenTypes.COMMENT_CONTENT);
234
235
236 slCommentContent.setColumnNo(token.getCharPositionInLine() + 2);
237 slCommentContent.setLineNo(token.getLine());
238 slCommentContent.setText(token.getText());
239
240 slComment.addChild(slCommentContent);
241 return slComment;
242 }
243
244
245
246
247 private static final class CheckstyleErrorListener extends BaseErrorListener {
248
249 @Override
250 public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
251 int line, int charPositionInLine,
252 String msg, RecognitionException ex) {
253 final String message = line + ":" + charPositionInLine + ": " + msg;
254 throw new IllegalStateException(message, ex);
255 }
256 }
257 }