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