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