1 | /* |
---|---|
2 | * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. |
3 | * Copyright (C) 2011, 2013-2020 The JavaParser Team. |
4 | * |
5 | * This file is part of JavaParser. |
6 | * |
7 | * JavaParser can be used either under the terms of |
8 | * a) the GNU Lesser General Public License as published by |
9 | * the Free Software Foundation, either version 3 of the License, or |
10 | * (at your option) any later version. |
11 | * b) the terms of the Apache License |
12 | * |
13 | * You should have received a copy of both licenses in LICENCE.LGPL and |
14 | * LICENCE.APACHE. Please refer to those files for details. |
15 | * |
16 | * JavaParser is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU Lesser General Public License for more details. |
20 | */ |
21 | |
22 | package com.github.javaparser; |
23 | |
24 | import com.github.javaparser.ast.ArrayCreationLevel; |
25 | import com.github.javaparser.ast.Node; |
26 | import com.github.javaparser.ast.NodeList; |
27 | import com.github.javaparser.ast.body.Parameter; |
28 | import com.github.javaparser.ast.comments.CommentsCollection; |
29 | import com.github.javaparser.ast.expr.*; |
30 | import com.github.javaparser.ast.stmt.Statement; |
31 | import com.github.javaparser.ast.type.ArrayType; |
32 | import com.github.javaparser.ast.type.Type; |
33 | import com.github.javaparser.ast.type.UnknownType; |
34 | import com.github.javaparser.utils.Pair; |
35 | |
36 | import java.util.*; |
37 | |
38 | import static com.github.javaparser.GeneratedJavaParserConstants.EOF; |
39 | import static com.github.javaparser.ast.type.ArrayType.unwrapArrayTypes; |
40 | import static com.github.javaparser.ast.type.ArrayType.wrapInArrayTypes; |
41 | import static com.github.javaparser.utils.Utils.assertNotNull; |
42 | |
43 | /** |
44 | * Base class for {@link GeneratedJavaParser} |
45 | */ |
46 | abstract class GeneratedJavaParserBase { |
47 | //// Interface with the generated code |
48 | abstract GeneratedJavaParserTokenManager getTokenSource(); |
49 | |
50 | abstract void ReInit(Provider provider); |
51 | |
52 | /* Returns the JavaParser specific token type of the last matched token */ |
53 | abstract JavaToken token(); |
54 | |
55 | abstract Token getNextToken(); |
56 | |
57 | //// |
58 | |
59 | /* The problems encountered while parsing */ |
60 | List<Problem> problems = new ArrayList<>(); |
61 | /* Configuration flag whether we store tokens and tokenranges */ |
62 | boolean storeTokens; |
63 | |
64 | /* Resets the parser for reuse, gaining a little performance */ |
65 | void reset(Provider provider) { |
66 | ReInit(provider); |
67 | problems = new ArrayList<>(); |
68 | getTokenSource().reset(); |
69 | } |
70 | |
71 | /** |
72 | * Return the list of JavaParser specific tokens that have been encountered while parsing code using this parser. |
73 | * |
74 | * @return a list of tokens |
75 | */ |
76 | public List<JavaToken> getTokens() { |
77 | return getTokenSource().getTokens(); |
78 | } |
79 | |
80 | /* The collection of comments encountered */ |
81 | CommentsCollection getCommentsCollection() { |
82 | return getTokenSource().getCommentsCollection(); |
83 | } |
84 | |
85 | /* Reports a problem to the user */ |
86 | void addProblem(String message) { |
87 | // TODO tokenRange only takes the final token. Need all the tokens. |
88 | problems.add(new Problem(message, tokenRange(), null)); |
89 | } |
90 | |
91 | /* Returns a tokenRange that spans the last matched token */ |
92 | TokenRange tokenRange() { |
93 | if (storeTokens) { |
94 | return new TokenRange(token(), token()); |
95 | } |
96 | return null; |
97 | } |
98 | |
99 | /** |
100 | * Return a TokenRange spanning from begin to end |
101 | */ |
102 | TokenRange range(JavaToken begin, JavaToken end) { |
103 | if (storeTokens) { |
104 | return new TokenRange(begin, end); |
105 | } |
106 | return null; |
107 | } |
108 | |
109 | /** |
110 | * Return a TokenRange spanning from begin to end |
111 | */ |
112 | TokenRange range(Node begin, JavaToken end) { |
113 | if (storeTokens) { |
114 | return new TokenRange(begin.getTokenRange().get().getBegin(), end); |
115 | } |
116 | return null; |
117 | } |
118 | |
119 | /** |
120 | * Return a TokenRange spanning from begin to end |
121 | */ |
122 | TokenRange range(JavaToken begin, Node end) { |
123 | if (storeTokens) { |
124 | return new TokenRange(begin, end.getTokenRange().get().getEnd()); |
125 | } |
126 | return null; |
127 | } |
128 | |
129 | /** |
130 | * Return a TokenRange spanning from begin to end |
131 | */ |
132 | TokenRange range(Node begin, Node end) { |
133 | if (storeTokens) { |
134 | return new TokenRange(begin.getTokenRange().get().getBegin(), end.getTokenRange().get().getEnd()); |
135 | } |
136 | return null; |
137 | } |
138 | |
139 | /** |
140 | * @return secondChoice if firstChoice is JavaToken.UNKNOWN, otherwise firstChoice |
141 | */ |
142 | JavaToken orIfInvalid(JavaToken firstChoice, JavaToken secondChoice) { |
143 | if (storeTokens) { |
144 | assertNotNull(firstChoice); |
145 | assertNotNull(secondChoice); |
146 | if (firstChoice.valid() || secondChoice.invalid()) { |
147 | return firstChoice; |
148 | } |
149 | return secondChoice; |
150 | } |
151 | return null; |
152 | } |
153 | |
154 | /** |
155 | * @return the begin-token secondChoice if firstChoice is JavaToken.UNKNOWN, otherwise firstChoice |
156 | */ |
157 | JavaToken orIfInvalid(JavaToken firstChoice, Node secondChoice) { |
158 | if (storeTokens) { |
159 | return orIfInvalid(firstChoice, secondChoice.getTokenRange().get().getBegin()); |
160 | } |
161 | return null; |
162 | } |
163 | |
164 | /** |
165 | * Get the token that starts the NodeList l |
166 | */ |
167 | JavaToken nodeListBegin(NodeList<?> l) { |
168 | if (!storeTokens || l.isEmpty()) { |
169 | return JavaToken.INVALID; |
170 | } |
171 | return l.get(0).getTokenRange().get().getBegin(); |
172 | } |
173 | |
174 | /* Sets the kind of the last matched token to newKind */ |
175 | void setTokenKind(int newKind) { |
176 | token().setKind(newKind); |
177 | } |
178 | |
179 | /* Makes the parser keep a list of tokens */ |
180 | void setStoreTokens(boolean storeTokens) { |
181 | this.storeTokens = storeTokens; |
182 | getTokenSource().setStoreTokens(storeTokens); |
183 | } |
184 | |
185 | /* Called from within a catch block to skip forward to a known token, |
186 | and report the occurred exception as a problem. */ |
187 | TokenRange recover(int recoveryTokenType, ParseException p) { |
188 | JavaToken begin = null; |
189 | if (p.currentToken != null) { |
190 | begin = token(); |
191 | } |
192 | Token t; |
193 | do { |
194 | t = getNextToken(); |
195 | } while (t.kind != recoveryTokenType && t.kind != EOF); |
196 | |
197 | JavaToken end = token(); |
198 | |
199 | TokenRange tokenRange = null; |
200 | if (begin != null && end != null) { |
201 | tokenRange = range(begin, end); |
202 | } |
203 | |
204 | problems.add(new Problem(makeMessageForParseException(p), tokenRange, p)); |
205 | return tokenRange; |
206 | } |
207 | |
208 | /** |
209 | * Quickly create a new, empty, NodeList |
210 | */ |
211 | <T extends Node> NodeList<T> emptyNodeList() { |
212 | return new NodeList<>(); |
213 | } |
214 | |
215 | /** |
216 | * Add obj to list and return it. Create a new list if list is null |
217 | */ |
218 | <T extends Node> NodeList<T> add(NodeList<T> list, T obj) { |
219 | if (list == null) { |
220 | list = new NodeList<>(); |
221 | } |
222 | list.add(obj); |
223 | return list; |
224 | } |
225 | |
226 | /** |
227 | * Add obj to list only when list is not null |
228 | */ |
229 | <T extends Node> NodeList<T> addWhenNotNull(NodeList<T> list, T obj) { |
230 | if (obj == null) { |
231 | return list; |
232 | } |
233 | return add(list, obj); |
234 | } |
235 | |
236 | /** |
237 | * Add obj to list at position pos |
238 | */ |
239 | <T extends Node> NodeList<T> prepend(NodeList<T> list, T obj) { |
240 | if (list == null) { |
241 | list = new NodeList<>(); |
242 | } |
243 | list.addFirst(obj); |
244 | return list; |
245 | } |
246 | |
247 | /** |
248 | * Add obj to list |
249 | */ |
250 | <T> List<T> add(List<T> list, T obj) { |
251 | if (list == null) { |
252 | list = new LinkedList<>(); |
253 | } |
254 | list.add(obj); |
255 | return list; |
256 | } |
257 | |
258 | /** |
259 | * Propagate expansion of the range on the right to the parent. This is necessary when the right border of the child |
260 | * is determining the right border of the parent (i.e., the child is the last element of the parent). In this case |
261 | * when we "enlarge" the child we should enlarge also the parent. |
262 | */ |
263 | private void propagateRangeGrowthOnRight(Node node, Node endNode) { |
264 | if (storeTokens) { |
265 | node.getParentNode().ifPresent(nodeParent -> { |
266 | boolean isChildOnTheRightBorderOfParent = node.getTokenRange().get().getEnd().equals(nodeParent.getTokenRange().get().getEnd()); |
267 | if (isChildOnTheRightBorderOfParent) { |
268 | propagateRangeGrowthOnRight(nodeParent, endNode); |
269 | } |
270 | }); |
271 | node.setTokenRange(range(node, endNode)); |
272 | } |
273 | } |
274 | |
275 | /** |
276 | * Workaround for rather complex ambiguity that lambda's create |
277 | */ |
278 | Expression generateLambda(Expression ret, Statement lambdaBody) { |
279 | if (ret instanceof EnclosedExpr) { |
280 | Expression inner = ((EnclosedExpr) ret).getInner(); |
281 | SimpleName id = ((NameExpr) inner).getName(); |
282 | NodeList<Parameter> params = add(new NodeList<>(), new Parameter(ret.getTokenRange().orElse(null), new NodeList<>(), new NodeList<>(), new UnknownType(), false, new NodeList<>(), id)); |
283 | ret = new LambdaExpr(range(ret, lambdaBody), params, lambdaBody, true); |
284 | } else if (ret instanceof NameExpr) { |
285 | SimpleName id = ((NameExpr) ret).getName(); |
286 | NodeList<Parameter> params = add(new NodeList<>(), new Parameter(ret.getTokenRange().orElse(null), new NodeList<>(), new NodeList<>(), new UnknownType(), false, new NodeList<>(), id)); |
287 | ret = new LambdaExpr(range(ret, lambdaBody), params, lambdaBody, false); |
288 | } else if (ret instanceof LambdaExpr) { |
289 | ((LambdaExpr) ret).setBody(lambdaBody); |
290 | propagateRangeGrowthOnRight(ret, lambdaBody); |
291 | } else if (ret instanceof CastExpr) { |
292 | CastExpr castExpr = (CastExpr) ret; |
293 | Expression inner = generateLambda(castExpr.getExpression(), lambdaBody); |
294 | castExpr.setExpression(inner); |
295 | } else { |
296 | addProblem("Failed to parse lambda expression! Please create an issue at https://github.com/javaparser/javaparser/issues"); |
297 | } |
298 | return ret; |
299 | } |
300 | |
301 | /** |
302 | * Throws together an ArrayCreationExpr from a lot of pieces |
303 | */ |
304 | ArrayCreationExpr juggleArrayCreation(TokenRange range, List<TokenRange> levelRanges, Type type, NodeList<Expression> dimensions, List<NodeList<AnnotationExpr>> arrayAnnotations, ArrayInitializerExpr arrayInitializerExpr) { |
305 | NodeList<ArrayCreationLevel> levels = new NodeList<>(); |
306 | |
307 | for (int i = 0; i < arrayAnnotations.size(); i++) { |
308 | levels.add(new ArrayCreationLevel(levelRanges.get(i), dimensions.get(i), arrayAnnotations.get(i))); |
309 | } |
310 | return new ArrayCreationExpr(range, type, levels, arrayInitializerExpr); |
311 | } |
312 | |
313 | /** |
314 | * Throws together a Type, taking care of all the array brackets |
315 | */ |
316 | Type juggleArrayType(Type partialType, List<ArrayType.ArrayBracketPair> additionalBrackets) { |
317 | Pair<Type, List<ArrayType.ArrayBracketPair>> partialParts = unwrapArrayTypes(partialType); |
318 | Type elementType = partialParts.a; |
319 | List<ArrayType.ArrayBracketPair> leftMostBrackets = partialParts.b; |
320 | return wrapInArrayTypes(elementType, additionalBrackets, leftMostBrackets).clone(); |
321 | } |
322 | |
323 | /** |
324 | * This is the code from ParseException.initialise, modified to be more horizontal. |
325 | */ |
326 | private String makeMessageForParseException(ParseException exception) { |
327 | final StringBuilder sb = new StringBuilder("Parse error. Found "); |
328 | final StringBuilder expected = new StringBuilder(); |
329 | |
330 | int maxExpectedTokenSequenceLength = 0; |
331 | TreeSet<String> sortedOptions = new TreeSet<>(); |
332 | for (int i = 0; i < exception.expectedTokenSequences.length; i++) { |
333 | if (maxExpectedTokenSequenceLength < exception.expectedTokenSequences[i].length) { |
334 | maxExpectedTokenSequenceLength = exception.expectedTokenSequences[i].length; |
335 | } |
336 | for (int j = 0; j < exception.expectedTokenSequences[i].length; j++) { |
337 | sortedOptions.add(exception.tokenImage[exception.expectedTokenSequences[i][j]]); |
338 | } |
339 | } |
340 | |
341 | for (String option : sortedOptions) { |
342 | expected.append(" ").append(option); |
343 | } |
344 | |
345 | Token token = exception.currentToken.next; |
346 | for (int i = 0; i < maxExpectedTokenSequenceLength; i++) { |
347 | String tokenText = token.image; |
348 | String escapedTokenText = ParseException.add_escapes(tokenText); |
349 | if (i != 0) { |
350 | sb.append(" "); |
351 | } |
352 | if (token.kind == 0) { |
353 | sb.append(exception.tokenImage[0]); |
354 | break; |
355 | } |
356 | escapedTokenText = "\"" + escapedTokenText + "\""; |
357 | String image = exception.tokenImage[token.kind]; |
358 | if (image.equals(escapedTokenText)) { |
359 | sb.append(image); |
360 | } else { |
361 | sb.append(" ") |
362 | .append(escapedTokenText) |
363 | .append(" ") |
364 | .append(image); |
365 | } |
366 | token = token.next; |
367 | } |
368 | |
369 | if (exception.expectedTokenSequences.length != 0) { |
370 | int numExpectedTokens = exception.expectedTokenSequences.length; |
371 | sb.append(", expected") |
372 | .append(numExpectedTokens == 1 ? "" : " one of ") |
373 | .append(expected.toString()); |
374 | } |
375 | return sb.toString(); |
376 | } |
377 | |
378 | /** |
379 | * Converts a NameExpr or a FieldAccessExpr scope to a Name. |
380 | */ |
381 | Name scopeToName(Expression scope) { |
382 | if (scope.isNameExpr()) { |
383 | SimpleName simpleName = scope.asNameExpr().getName(); |
384 | return new Name(simpleName.getTokenRange().get(), null, simpleName.getIdentifier()); |
385 | } |
386 | if (scope.isFieldAccessExpr()) { |
387 | FieldAccessExpr fieldAccessExpr = scope.asFieldAccessExpr(); |
388 | return new Name(fieldAccessExpr.getTokenRange().get(), scopeToName(fieldAccessExpr.getScope()), fieldAccessExpr.getName().getIdentifier()); |
389 | |
390 | } |
391 | throw new IllegalStateException("Unexpected expression type: " + scope.getClass().getSimpleName()); |
392 | } |
393 | |
394 | String unquote(String s) { |
395 | return s.substring(1, s.length() - 1); |
396 | } |
397 | |
398 | String unTripleQuote(String s) { |
399 | int start = 3; |
400 | // Skip over the first end of line too: |
401 | if (s.charAt(start) == '\r') { |
402 | start++; |
403 | } |
404 | if (s.charAt(start) == '\n') { |
405 | start++; |
406 | } |
407 | return s.substring(start, s.length() - 3); |
408 | } |
409 | |
410 | void setYieldSupported() { |
411 | getTokenSource().setYieldSupported(); |
412 | } |
413 | } |
414 |
Members