| package com.github.javaparser; |
| |
| import com.github.javaparser.ast.ArrayCreationLevel; |
| import com.github.javaparser.ast.Modifier; |
| import com.github.javaparser.ast.Node; |
| import com.github.javaparser.ast.NodeList; |
| import com.github.javaparser.ast.body.Parameter; |
| import com.github.javaparser.ast.comments.CommentsCollection; |
| import com.github.javaparser.ast.expr.*; |
| import com.github.javaparser.ast.stmt.Statement; |
| import com.github.javaparser.ast.type.ArrayType; |
| import com.github.javaparser.ast.type.Type; |
| import com.github.javaparser.ast.type.UnknownType; |
| import com.github.javaparser.utils.Pair; |
| |
| import java.util.*; |
| |
| import static com.github.javaparser.GeneratedJavaParserConstants.EOF; |
| import static com.github.javaparser.ast.type.ArrayType.unwrapArrayTypes; |
| import static com.github.javaparser.ast.type.ArrayType.wrapInArrayTypes; |
| import static com.github.javaparser.utils.Utils.assertNotNull; |
| |
| /** |
| * Base class for {@link GeneratedJavaParser} |
| */ |
| abstract class GeneratedJavaParserBase { |
| //// Interface with the generated code |
| abstract GeneratedJavaParserTokenManager getTokenSource(); |
| |
| abstract void ReInit(Provider provider); |
| |
| /* Returns the JavaParser specific token type of the last matched token */ |
| abstract JavaToken token(); |
| |
| abstract Token getNextToken(); |
| |
| //// |
| |
| /* The problems encountered while parsing */ |
| List<Problem> problems = new ArrayList<>(); |
| /* Configuration flag whether we store tokens and tokenranges */ |
| boolean storeTokens; |
| |
| /* Resets the parser for reuse, gaining a little performance */ |
| void reset(Provider provider) { |
| ReInit(provider); |
| problems = new ArrayList<>(); |
| getTokenSource().reset(); |
| } |
| |
| /** |
| * Return the list of JavaParser specific tokens that have been encountered while parsing code using this parser. |
| * |
| * @return a list of tokens |
| */ |
| public List<JavaToken> getTokens() { |
| return getTokenSource().getTokens(); |
| } |
| |
| /* The collection of comments encountered */ |
| CommentsCollection getCommentsCollection() { |
| return getTokenSource().getCommentsCollection(); |
| } |
| |
| /* Reports a problem to the user */ |
| void addProblem(String message) { |
| // TODO tokenRange only takes the final token. Need all the tokens. |
| problems.add(new Problem(message, tokenRange(), null)); |
| } |
| |
| /* Returns a tokenRange that spans the last matched token */ |
| TokenRange tokenRange() { |
| if (storeTokens) { |
| return new TokenRange(token(), token()); |
| } |
| return null; |
| } |
| |
| /** |
| * Return a TokenRange spanning from begin to end |
| */ |
| TokenRange range(JavaToken begin, JavaToken end) { |
| if (storeTokens) { |
| return new TokenRange(begin, end); |
| } |
| return null; |
| } |
| |
| /** |
| * Return a TokenRange spanning from begin to end |
| */ |
| TokenRange range(Node begin, JavaToken end) { |
| if (storeTokens) { |
| return new TokenRange(begin.getTokenRange().get().getBegin(), end); |
| } |
| return null; |
| } |
| |
| /** |
| * Return a TokenRange spanning from begin to end |
| */ |
| TokenRange range(JavaToken begin, Node end) { |
| if (storeTokens) { |
| return new TokenRange(begin, end.getTokenRange().get().getEnd()); |
| } |
| return null; |
| } |
| |
| /** |
| * Return a TokenRange spanning from begin to end |
| */ |
| TokenRange range(Node begin, Node end) { |
| if (storeTokens) { |
| return new TokenRange(begin.getTokenRange().get().getBegin(), end.getTokenRange().get().getEnd()); |
| } |
| return null; |
| } |
| |
| /** |
| * @return secondChoice if firstChoice is JavaToken.UNKNOWN, otherwise firstChoice |
| */ |
| JavaToken orIfInvalid(JavaToken firstChoice, JavaToken secondChoice) { |
| if (storeTokens) { |
| assertNotNull(firstChoice); |
| assertNotNull(secondChoice); |
| if (firstChoice.valid() || secondChoice.invalid()) { |
| return firstChoice; |
| } |
| return secondChoice; |
| } |
| return null; |
| } |
| |
| /** |
| * @return the begin-token secondChoice if firstChoice is JavaToken.UNKNOWN, otherwise firstChoice |
| */ |
| JavaToken orIfInvalid(JavaToken firstChoice, Node secondChoice) { |
| if (storeTokens) { |
| return orIfInvalid(firstChoice, secondChoice.getTokenRange().get().getBegin()); |
| } |
| return null; |
| } |
| |
| /** |
| * Get the token that starts the NodeList l |
| */ |
| JavaToken nodeListBegin(NodeList<?> l) { |
| if (!storeTokens || l.isEmpty()) { |
| return JavaToken.INVALID; |
| } |
| return l.get(0).getTokenRange().get().getBegin(); |
| } |
| |
| /* Sets the kind of the last matched token to newKind */ |
| void setTokenKind(int newKind) { |
| token().setKind(newKind); |
| } |
| |
| /* Makes the parser keep a list of tokens */ |
| void setStoreTokens(boolean storeTokens) { |
| this.storeTokens = storeTokens; |
| getTokenSource().setStoreTokens(storeTokens); |
| } |
| |
| /* Called from within a catch block to skip forward to a known token, |
| and report the occurred exception as a problem. */ |
| TokenRange recover(int recoveryTokenType, ParseException p) { |
| JavaToken begin = null; |
| if (p.currentToken != null) { |
| begin = token(); |
| } |
| Token t; |
| do { |
| t = getNextToken(); |
| } while (t.kind != recoveryTokenType && t.kind != EOF); |
| |
| JavaToken end = token(); |
| |
| TokenRange tokenRange = null; |
| if (begin != null && end != null) { |
| tokenRange = range(begin, end); |
| } |
| |
| problems.add(new Problem(makeMessageForParseException(p), tokenRange, p)); |
| return tokenRange; |
| } |
| |
| /** |
| * Quickly create a new NodeList |
| */ |
| <T extends Node> NodeList<T> emptyList() { |
| return new NodeList<>(); |
| } |
| |
| /** |
| * Add obj to list and return it. Create a new list if list is null |
| */ |
| <T extends Node> NodeList<T> add(NodeList<T> list, T obj) { |
| if (list == null) { |
| list = new NodeList<>(); |
| } |
| list.add(obj); |
| return list; |
| } |
| |
| /** |
| * Add obj to list only when list is not null |
| */ |
| <T extends Node> NodeList<T> addWhenNotNull(NodeList<T> list, T obj) { |
| if (obj == null) { |
| return list; |
| } |
| return add(list, obj); |
| } |
| |
| /** |
| * Add obj to list at position pos |
| */ |
| <T extends Node> NodeList<T> prepend(NodeList<T> list, T obj) { |
| if (list == null) { |
| list = new NodeList<>(); |
| } |
| list.addFirst(obj); |
| return list; |
| } |
| |
| /** |
| * Add obj to list |
| */ |
| <T> List<T> add(List<T> list, T obj) { |
| if (list == null) { |
| list = new LinkedList<>(); |
| } |
| list.add(obj); |
| return list; |
| } |
| |
| /** |
| * Add modifier mod to modifiers |
| */ |
| void addModifier(EnumSet<Modifier> modifiers, Modifier mod) { |
| if (modifiers.contains(mod)) { |
| addProblem("Duplicated modifier"); |
| } |
| modifiers.add(mod); |
| } |
| |
| /** |
| * Propagate expansion of the range on the right to the parent. This is necessary when the right border of the child |
| * is determining the right border of the parent (i.e., the child is the last element of the parent). In this case |
| * when we "enlarge" the child we should enlarge also the parent. |
| */ |
| private void propagateRangeGrowthOnRight(Node node, Node endNode) { |
| if (storeTokens) { |
| node.getParentNode().ifPresent(nodeParent -> { |
| boolean isChildOnTheRightBorderOfParent = node.getTokenRange().get().getEnd().equals(nodeParent.getTokenRange().get().getEnd()); |
| if (isChildOnTheRightBorderOfParent) { |
| propagateRangeGrowthOnRight(nodeParent, endNode); |
| } |
| }); |
| node.setTokenRange(range(node, endNode)); |
| } |
| } |
| |
| /** |
| * Workaround for rather complex ambiguity that lambda's create |
| */ |
| Expression generateLambda(Expression ret, Statement lambdaBody) { |
| if (ret instanceof EnclosedExpr) { |
| Expression inner = ((EnclosedExpr) ret).getInner(); |
| SimpleName id = ((NameExpr) inner).getName(); |
| NodeList<Parameter> params = add(new NodeList<>(), new Parameter(ret.getTokenRange().orElse(null), EnumSet.noneOf(Modifier.class), new NodeList<>(), new UnknownType(), false, new NodeList<>(), id)); |
| ret = new LambdaExpr(range(ret, lambdaBody), params, lambdaBody, true); |
| } else if (ret instanceof NameExpr) { |
| SimpleName id = ((NameExpr) ret).getName(); |
| NodeList<Parameter> params = add(new NodeList<>(), new Parameter(ret.getTokenRange().orElse(null), EnumSet.noneOf(Modifier.class), new NodeList<>(), new UnknownType(), false, new NodeList<>(), id)); |
| ret = new LambdaExpr(range(ret, lambdaBody), params, lambdaBody, false); |
| } else if (ret instanceof LambdaExpr) { |
| ((LambdaExpr) ret).setBody(lambdaBody); |
| propagateRangeGrowthOnRight(ret, lambdaBody); |
| } else if (ret instanceof CastExpr) { |
| CastExpr castExpr = (CastExpr) ret; |
| Expression inner = generateLambda(castExpr.getExpression(), lambdaBody); |
| castExpr.setExpression(inner); |
| } else { |
| addProblem("Failed to parse lambda expression! Please create an issue at https://github.com/javaparser/javaparser/issues"); |
| } |
| return ret; |
| } |
| |
| /** |
| * Throws together an ArrayCreationExpr from a lot of pieces |
| */ |
| ArrayCreationExpr juggleArrayCreation(TokenRange range, List<TokenRange> levelRanges, Type type, NodeList<Expression> dimensions, List<NodeList<AnnotationExpr>> arrayAnnotations, ArrayInitializerExpr arrayInitializerExpr) { |
| NodeList<ArrayCreationLevel> levels = new NodeList<>(); |
| |
| for (int i = 0; i < arrayAnnotations.size(); i++) { |
| levels.add(new ArrayCreationLevel(levelRanges.get(i), dimensions.get(i), arrayAnnotations.get(i))); |
| } |
| return new ArrayCreationExpr(range, type, levels, arrayInitializerExpr); |
| } |
| |
| /** |
| * Throws together a Type, taking care of all the array brackets |
| */ |
| Type juggleArrayType(Type partialType, List<ArrayType.ArrayBracketPair> additionalBrackets) { |
| Pair<Type, List<ArrayType.ArrayBracketPair>> partialParts = unwrapArrayTypes(partialType); |
| Type elementType = partialParts.a; |
| List<ArrayType.ArrayBracketPair> leftMostBrackets = partialParts.b; |
| return wrapInArrayTypes(elementType, leftMostBrackets, additionalBrackets).clone(); |
| } |
| |
| /** |
| * This is the code from ParseException.initialise, modified to be more horizontal. |
| */ |
| private String makeMessageForParseException(ParseException exception) { |
| final StringBuilder sb = new StringBuilder("Parse error. Found "); |
| final StringBuilder expected = new StringBuilder(); |
| |
| int maxExpectedTokenSequenceLength = 0; |
| TreeSet<String> sortedOptions = new TreeSet<>(); |
| for (int i = 0; i < exception.expectedTokenSequences.length; i++) { |
| if (maxExpectedTokenSequenceLength < exception.expectedTokenSequences[i].length) { |
| maxExpectedTokenSequenceLength = exception.expectedTokenSequences[i].length; |
| } |
| for (int j = 0; j < exception.expectedTokenSequences[i].length; j++) { |
| sortedOptions.add(exception.tokenImage[exception.expectedTokenSequences[i][j]]); |
| } |
| } |
| |
| for (String option : sortedOptions) { |
| expected.append(" ").append(option); |
| } |
| |
| sb.append(""); |
| |
| Token token = exception.currentToken.next; |
| for (int i = 0; i < maxExpectedTokenSequenceLength; i++) { |
| String tokenText = token.image; |
| String escapedTokenText = ParseException.add_escapes(tokenText); |
| if (i != 0) { |
| sb.append(" "); |
| } |
| if (token.kind == 0) { |
| sb.append(exception.tokenImage[0]); |
| break; |
| } |
| escapedTokenText = "\"" + escapedTokenText + "\""; |
| String image = exception.tokenImage[token.kind]; |
| if (image.equals(escapedTokenText)) { |
| sb.append(image); |
| } else { |
| sb.append(" ") |
| .append(escapedTokenText) |
| .append(" ") |
| .append(image); |
| } |
| token = token.next; |
| } |
| |
| if (exception.expectedTokenSequences.length != 0) { |
| int numExpectedTokens = exception.expectedTokenSequences.length; |
| sb.append(", expected") |
| .append(numExpectedTokens == 1 ? "" : " one of ") |
| .append(expected.toString()); |
| } |
| return sb.toString(); |
| } |
| } |