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