Home | History | Annotate | Download | only in javaparser
      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