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