9ff9da60f09c655c6871435394f1505ae4c8b472
[idea/community.git] / java / java-psi-impl / src / com / intellij / lang / java / parser / DeclarationParser.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.lang.java.parser;
17
18 import com.intellij.codeInsight.daemon.JavaErrorMessages;
19 import com.intellij.lang.PsiBuilder;
20 import com.intellij.lang.WhitespacesBinders;
21 import com.intellij.openapi.util.Pair;
22 import com.intellij.psi.JavaTokenType;
23 import com.intellij.psi.impl.source.tree.ElementType;
24 import com.intellij.psi.impl.source.tree.JavaElementType;
25 import com.intellij.psi.tree.IElementType;
26 import com.intellij.psi.tree.ILazyParseableElementType;
27 import com.intellij.psi.tree.TokenSet;
28 import com.intellij.util.text.CharArrayUtil;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 import org.jetbrains.annotations.PropertyKey;
32
33 import static com.intellij.lang.PsiBuilderUtil.expect;
34 import static com.intellij.lang.java.parser.JavaParserUtil.*;
35
36 public class DeclarationParser {
37   public enum Context {
38     FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE
39   }
40
41   private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(
42     JavaElementType.FIELD, JavaElementType.METHOD);
43   private static final TokenSet BEFORE_LBRACE_ELEMENTS_SET = TokenSet.create(
44     JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.IMPLEMENTS_KEYWORD);
45   private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create(
46     JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD);
47   private static final TokenSet PARAM_LIST_STOPPERS = TokenSet.create(
48     JavaTokenType.RPARENTH, JavaTokenType.LBRACE, JavaTokenType.ARROW);
49   private static final TokenSet TYPE_START = TokenSet.orSet(
50     ElementType.PRIMITIVE_TYPE_BIT_SET, TokenSet.create(JavaTokenType.IDENTIFIER, JavaTokenType.AT));
51
52   private static final String WHITESPACES = "\n\r \t";
53   private static final String LINE_ENDS = "\n\r";
54
55   private final JavaParser myParser;
56
57   public DeclarationParser(@NotNull final JavaParser javaParser) {
58     myParser = javaParser;
59   }
60
61   public void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) {
62     assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType();
63     builder.advanceLexer();
64
65     final PsiBuilder builderWrapper = braceMatchingBuilder(builder);
66     if (isEnum) {
67       parseEnumConstants(builderWrapper);
68     }
69     parseClassBodyDeclarations(builderWrapper, isAnnotation);
70
71     expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace");
72   }
73
74   @Nullable
75   public PsiBuilder.Marker parseClassFromKeyword(final PsiBuilder builder, final PsiBuilder.Marker declaration,
76                                                         final boolean isAnnotation, final Context context) {
77     final IElementType keywordTokenType = builder.getTokenType();
78     assert ElementType.CLASS_KEYWORD_BIT_SET.contains(keywordTokenType) : keywordTokenType;
79     builder.advanceLexer();
80     final boolean isEnum = (keywordTokenType == JavaTokenType.ENUM_KEYWORD);
81
82     if (!expect(builder, JavaTokenType.IDENTIFIER)) {
83       error(builder, JavaErrorMessages.message("expected.identifier"));
84       declaration.drop();
85       return null;
86     }
87
88     final ReferenceParser refParser = myParser.getReferenceParser();
89     refParser.parseTypeParameters(builder);
90     refParser.parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_LIST, JavaTokenType.COMMA);
91     refParser.parseReferenceList(builder, JavaTokenType.IMPLEMENTS_KEYWORD, JavaElementType.IMPLEMENTS_LIST, JavaTokenType.COMMA);
92
93     if (builder.getTokenType() != JavaTokenType.LBRACE) {
94       final PsiBuilder.Marker error = builder.mark();
95       while (BEFORE_LBRACE_ELEMENTS_SET.contains(builder.getTokenType())) {
96         builder.advanceLexer();
97       }
98       error.error(JavaErrorMessages.message("expected.lbrace"));
99     }
100
101     if (builder.getTokenType() == JavaTokenType.LBRACE) {
102       parseClassBodyWithBraces(builder, isAnnotation, isEnum);
103     }
104
105     if (context == Context.FILE) {
106       boolean declarationsAfterEnd = false;
107
108       while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) {
109         final PsiBuilder.Marker position = builder.mark();
110         final PsiBuilder.Marker extra = parse(builder, Context.CLASS);
111         if (extra != null && AFTER_END_DECLARATION_SET.contains(exprType(extra))) {
112           if (!declarationsAfterEnd) {
113             error(builder, JavaErrorMessages.message("expected.class.or.interface"), extra);
114           }
115           declarationsAfterEnd = true;
116           position.drop();
117         }
118         else {
119           position.rollbackTo();
120           break;
121         }
122       }
123
124       if (declarationsAfterEnd) {
125         expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace");
126       }
127     }
128
129     done(declaration, JavaElementType.CLASS);
130     return declaration;
131   }
132
133   private void parseEnumConstants(final PsiBuilder builder) {
134     while (builder.getTokenType() != null) {
135       if (expect(builder, JavaTokenType.SEMICOLON)) {
136         return;
137       }
138
139       if (builder.getTokenType() == JavaTokenType.PRIVATE_KEYWORD || builder.getTokenType() == JavaTokenType.PROTECTED_KEYWORD) {
140         error(builder, JavaErrorMessages.message("expected.semicolon"));
141         return;
142       }
143
144       final PsiBuilder.Marker enumConstant = parseEnumConstant(builder);
145       if (enumConstant == null) {
146         error(builder, JavaErrorMessages.message("expected.identifier"));
147       }
148
149       if (!expect(builder, JavaTokenType.COMMA)) {
150         if (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.SEMICOLON) {
151           error(builder, JavaErrorMessages.message("expected.comma.or.semicolon"));
152           return;
153         }
154       }
155     }
156   }
157
158   @Nullable
159   public PsiBuilder.Marker parseEnumConstant(final PsiBuilder builder) {
160     final PsiBuilder.Marker constant = builder.mark();
161
162     parseModifierList(builder);
163
164     if (expect(builder, JavaTokenType.IDENTIFIER)) {
165       if (builder.getTokenType() == JavaTokenType.LPARENTH) {
166         myParser.getExpressionParser().parseArgumentList(builder);
167       }
168       else {
169         emptyElement(builder, JavaElementType.EXPRESSION_LIST);
170       }
171
172       if (builder.getTokenType() == JavaTokenType.LBRACE) {
173         final PsiBuilder.Marker constantInit = builder.mark();
174         parseClassBodyWithBraces(builder, false, false);
175         done(constantInit, JavaElementType.ENUM_CONSTANT_INITIALIZER);
176       }
177
178       done(constant, JavaElementType.ENUM_CONSTANT);
179       return constant;
180     }
181     else {
182       constant.rollbackTo();
183       return null;
184     }
185   }
186
187   public void parseClassBodyDeclarations(final PsiBuilder builder, final boolean isAnnotation) {
188     final Context context = isAnnotation ? Context.ANNOTATION_INTERFACE : Context.CLASS;
189
190     PsiBuilder.Marker invalidElements = null;
191     while (true) {
192       final IElementType tokenType = builder.getTokenType();
193       if (tokenType == null || tokenType == JavaTokenType.RBRACE) break;
194
195       if (tokenType == JavaTokenType.SEMICOLON) {
196         if (invalidElements != null) {
197           invalidElements.error(JavaErrorMessages.message("unexpected.token"));
198           invalidElements = null;
199         }
200         builder.advanceLexer();
201         continue;
202       }
203
204       final PsiBuilder.Marker declaration = parse(builder, context);
205       if (declaration != null) {
206         if (invalidElements != null) {
207           invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), declaration);
208           invalidElements = null;
209         }
210         continue;
211       }
212
213       if (invalidElements == null) {
214         invalidElements = builder.mark();
215       }
216
217       // adding a reference, not simple tokens allows "Browse ..." to work well
218       final PsiBuilder.Marker ref = myParser.getReferenceParser().parseJavaCodeReference(builder, true, true, false, false);
219       if (ref == null) {
220         builder.advanceLexer();
221       }
222     }
223
224     if (invalidElements != null) {
225       invalidElements.error(JavaErrorMessages.message("unexpected.token"));
226     }
227   }
228
229   @Nullable
230   public PsiBuilder.Marker parse(final PsiBuilder builder, final Context context) {
231     final IElementType tokenType = builder.getTokenType();
232     if (tokenType == null) return null;
233
234     if (tokenType == JavaTokenType.LBRACE) {
235       if (context == Context.FILE || context == Context.CODE_BLOCK) return null;
236     }
237     else if (tokenType == JavaTokenType.IDENTIFIER || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) {
238       if (context == Context.FILE) return null;
239     }
240     else if (tokenType instanceof ILazyParseableElementType) {
241       builder.advanceLexer();
242       return null;
243     }
244     else if (!ElementType.MODIFIER_BIT_SET.contains(tokenType) &&
245              !ElementType.CLASS_KEYWORD_BIT_SET.contains(tokenType) &&
246              tokenType != JavaTokenType.AT &&
247              (context == Context.CODE_BLOCK || tokenType != JavaTokenType.LT)) {
248       return null;
249     }
250
251     final PsiBuilder.Marker declaration = builder.mark();
252     final int declarationStart = builder.getCurrentOffset();
253
254     final Pair<PsiBuilder.Marker, Boolean> modListInfo = parseModifierList(builder);
255     final PsiBuilder.Marker modList = modListInfo.first;
256
257     if (expect(builder, JavaTokenType.AT)) {
258       if (builder.getTokenType() == JavaTokenType.INTERFACE_KEYWORD) {
259         final PsiBuilder.Marker result = parseClassFromKeyword(builder, declaration, true, context);
260         return result != null ? result : modList;
261       }
262       else {
263         declaration.rollbackTo();
264         return null;
265       }
266     }
267     else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) {
268       final PsiBuilder.Marker result = parseClassFromKeyword(builder, declaration, false, context);
269       return result != null ? result : modList;
270     }
271
272     PsiBuilder.Marker typeParams = null;
273     if (builder.getTokenType() == JavaTokenType.LT) {
274       typeParams = myParser.getReferenceParser().parseTypeParameters(builder);
275     }
276
277     if (context == Context.FILE) {
278       error(builder, JavaErrorMessages.message("expected.class.or.interface"), typeParams);
279       declaration.drop();
280       return modList;
281     }
282
283     if (builder.getTokenType() == JavaTokenType.LBRACE) {
284       if (context == Context.CODE_BLOCK) {
285         error(builder, JavaErrorMessages.message("expected.identifier.or.type"), typeParams);
286         declaration.drop();
287         return modList;
288       }
289
290       PsiBuilder.Marker codeBlock = myParser.getStatementParser().parseCodeBlock(builder);
291       assert codeBlock != null : builder.getOriginalText();
292
293       if (typeParams != null) {
294         PsiBuilder.Marker error = typeParams.precede();
295         error.errorBefore(JavaErrorMessages.message("unexpected.token"), codeBlock);
296       }
297
298       done(declaration, JavaElementType.CLASS_INITIALIZER);
299       return declaration;
300     }
301
302     PsiBuilder.Marker type = null;
303
304     if (TYPE_START.contains(builder.getTokenType())) {
305       PsiBuilder.Marker pos = builder.mark();
306
307       type = myParser.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD);
308
309       if (type == null) {
310         pos.rollbackTo();
311       }
312       else if (builder.getTokenType() == JavaTokenType.LPARENTH) {  // constructor
313         if (context == Context.CODE_BLOCK) {
314           declaration.rollbackTo();
315           return null;
316         }
317
318         pos.rollbackTo();
319
320         if (typeParams == null) {
321           emptyElement(builder, JavaElementType.TYPE_PARAMETER_LIST);
322         }
323         parseAnnotations(builder);
324         builder.advanceLexer();
325
326         if (builder.getTokenType() == JavaTokenType.LPARENTH) {
327           return parseMethodFromLeftParenth(builder, declaration, false, true);
328         }
329         else {
330           declaration.rollbackTo();
331           return null;
332         }
333       }
334       else {
335         pos.drop();
336       }
337     }
338
339     if (type == null) {
340       PsiBuilder.Marker error = typeParams != null ? typeParams.precede() : builder.mark();
341       error.error(JavaErrorMessages.message("expected.identifier.or.type"));
342       declaration.drop();
343       return modList;
344     }
345
346     if (!expect(builder, JavaTokenType.IDENTIFIER)) {
347       if ((context == Context.CODE_BLOCK) && Boolean.TRUE.equals(modListInfo.second)) {
348         declaration.rollbackTo();
349         return null;
350       }
351       else {
352         if (typeParams != null) {
353           typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type);
354         }
355         builder.error(JavaErrorMessages.message("expected.identifier"));
356         declaration.drop();
357         return modList;
358       }
359     }
360
361     if (builder.getTokenType() == JavaTokenType.LPARENTH) {
362       if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) {  // method
363         if (typeParams == null) {
364           emptyElement(type, JavaElementType.TYPE_PARAMETER_LIST);
365         }
366         return parseMethodFromLeftParenth(builder, declaration, (context == Context.ANNOTATION_INTERFACE), false);
367       }
368     }
369
370     if (typeParams != null) {
371       typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type);
372     }
373     return parseFieldOrLocalVariable(builder, declaration, declarationStart, context);
374   }
375
376   @NotNull
377   public Pair<PsiBuilder.Marker, Boolean> parseModifierList(final PsiBuilder builder) {
378     return parseModifierList(builder, ElementType.MODIFIER_BIT_SET);
379   }
380
381   @NotNull
382   public Pair<PsiBuilder.Marker, Boolean> parseModifierList(final PsiBuilder builder, final TokenSet modifiers) {
383     final PsiBuilder.Marker modList = builder.mark();
384     boolean isEmpty = true;
385
386     while (true) {
387       final IElementType tokenType = builder.getTokenType();
388       if (tokenType == null) break;
389       if (modifiers.contains(tokenType)) {
390         builder.advanceLexer();
391         isEmpty = false;
392       }
393       else if (tokenType == JavaTokenType.AT) {
394         if (ElementType.KEYWORD_BIT_SET.contains(builder.lookAhead(1))) {
395           break;
396         }
397         parseAnnotation(builder);
398         isEmpty = false;
399       }
400       else {
401         break;
402       }
403     }
404
405     done(modList, JavaElementType.MODIFIER_LIST);
406     return Pair.create(modList, isEmpty);
407   }
408
409   private PsiBuilder.Marker parseMethodFromLeftParenth(PsiBuilder builder, PsiBuilder.Marker declaration, boolean anno, boolean constructor) {
410     parseParameterList(builder);
411
412     eatBrackets(builder, constructor ? "expected.semicolon" : null);
413
414     myParser.getReferenceParser().parseReferenceList(builder, JavaTokenType.THROWS_KEYWORD, JavaElementType.THROWS_LIST, JavaTokenType.COMMA);
415
416     if (anno && expect(builder, JavaTokenType.DEFAULT_KEYWORD)) {
417       parseAnnotationValue(builder);
418     }
419
420     IElementType tokenType = builder.getTokenType();
421     if (tokenType != JavaTokenType.SEMICOLON && tokenType != JavaTokenType.LBRACE) {
422       PsiBuilder.Marker error = builder.mark();
423       // heuristic: going to next line obviously means method signature is over, starting new method (actually, another one completion hack)
424       CharSequence text = builder.getOriginalText();
425       Loop:
426       while (true) {
427         for (int i = builder.getCurrentOffset() - 1; i >= 0; i--) {
428           char ch = text.charAt(i);
429           if (ch == '\n') break Loop;
430           else if (ch != ' ' && ch != '\t') break;
431         }
432         if (!expect(builder, APPEND_TO_METHOD_SET)) break;
433       }
434       error.error(JavaErrorMessages.message("expected.lbrace.or.semicolon"));
435     }
436
437     if (!expect(builder, JavaTokenType.SEMICOLON)) {
438       if (builder.getTokenType() == JavaTokenType.LBRACE) {
439         myParser.getStatementParser().parseCodeBlock(builder);
440       }
441     }
442
443     done(declaration, anno ? JavaElementType.ANNOTATION_METHOD : JavaElementType.METHOD);
444     return declaration;
445   }
446
447   @NotNull
448   public PsiBuilder.Marker parseParameterList(PsiBuilder builder) {
449     return parseElementList(builder, ListType.METHOD);
450   }
451
452   @NotNull
453   public PsiBuilder.Marker parseResourceList(PsiBuilder builder) {
454     return parseElementList(builder, ListType.RESOURCE);
455   }
456
457   @NotNull
458   public PsiBuilder.Marker parseLambdaParameterList(PsiBuilder builder, boolean typed) {
459     return parseElementList(builder, typed ? ListType.LAMBDA_TYPED : ListType.LAMBDA_UNTYPED);
460   }
461
462   private enum ListType {METHOD, RESOURCE, LAMBDA_TYPED, LAMBDA_UNTYPED}
463
464   @NotNull
465   private PsiBuilder.Marker parseElementList(PsiBuilder builder, ListType type) {
466     final boolean lambda = (type == ListType.LAMBDA_TYPED || type == ListType.LAMBDA_UNTYPED);
467     final boolean resources = (type == ListType.RESOURCE);
468     final PsiBuilder.Marker elementList = builder.mark();
469     final boolean leftParenth = expect(builder, JavaTokenType.LPARENTH);
470     assert lambda || leftParenth : builder.getTokenType();
471
472     final IElementType delimiter = resources ? JavaTokenType.SEMICOLON : JavaTokenType.COMMA;
473     final String noDelimiterMsg = resources ? "expected.semicolon" : "expected.comma";
474     final String noElementMsg = resources ? "expected.resource" : "expected.parameter";
475
476     PsiBuilder.Marker invalidElements = null;
477     String errorMessage = null;
478     boolean delimiterExpected = false;
479     boolean noElements = true;
480     while (true) {
481       final IElementType tokenType = builder.getTokenType();
482       if (tokenType == null || PARAM_LIST_STOPPERS.contains(tokenType)) {
483         final boolean noLastElement = !delimiterExpected && (!noElements && !resources || noElements && resources);
484         if (noLastElement) {
485           final String key = lambda ? "expected.parameter" : "expected.identifier.or.type";
486           error(builder, JavaErrorMessages.message(key));
487         }
488         if (tokenType == JavaTokenType.RPARENTH) {
489           if (invalidElements != null) {
490             invalidElements.error(errorMessage);
491             invalidElements = null;
492           }
493           builder.advanceLexer();
494         }
495         else {
496           if (!noLastElement || resources) {
497             if (invalidElements != null) {
498               invalidElements.error(errorMessage);
499             }
500             invalidElements = null;
501             if (leftParenth) {
502               error(builder, JavaErrorMessages.message("expected.rparen"));
503             }
504           }
505         }
506         break;
507       }
508
509       if (delimiterExpected) {
510         if (builder.getTokenType() == delimiter) {
511           delimiterExpected = false;
512           if (invalidElements != null) {
513             invalidElements.error(errorMessage);
514             invalidElements = null;
515           }
516           builder.advanceLexer();
517           continue;
518         }
519       }
520       else {
521         final PsiBuilder.Marker listElement = resources ? parseResource(builder) :
522                                               lambda ? parseLambdaParameter(builder, type == ListType.LAMBDA_TYPED) :
523                                               parseParameter(builder, true, false);
524         if (listElement != null) {
525           delimiterExpected = true;
526           if (invalidElements != null) {
527             invalidElements.errorBefore(errorMessage, listElement);
528             invalidElements = null;
529           }
530           noElements= false;
531           continue;
532         }
533       }
534
535       if (invalidElements == null) {
536         if (builder.getTokenType() == delimiter) {
537           error(builder, JavaErrorMessages.message(noElementMsg));
538           builder.advanceLexer();
539           if (noElements && resources) {
540             noElements = false;
541           }
542           continue;
543         }
544         else {
545           invalidElements = builder.mark();
546           errorMessage = JavaErrorMessages.message(delimiterExpected ? noDelimiterMsg : noElementMsg);
547         }
548       }
549
550       // adding a reference, not simple tokens allows "Browse .." to work well
551       final PsiBuilder.Marker ref = myParser.getReferenceParser().parseJavaCodeReference(builder, true, true, false, false);
552       if (ref == null && builder.getTokenType() != null) {
553         builder.advanceLexer();
554       }
555     }
556
557     if (invalidElements != null) {
558       invalidElements.error(errorMessage);
559     }
560
561     done(elementList, resources ? JavaElementType.RESOURCE_LIST : JavaElementType.PARAMETER_LIST);
562     return elementList;
563   }
564
565   @Nullable
566   public PsiBuilder.Marker parseParameter(PsiBuilder builder, boolean ellipsis, boolean disjunctiveType) {
567     return parseListElement(builder, true, ellipsis, disjunctiveType, false);
568   }
569
570   @Nullable
571   public PsiBuilder.Marker parseResource(PsiBuilder builder) {
572     return parseListElement(builder, true, false, false, true);
573   }
574
575   @Nullable
576   public PsiBuilder.Marker parseLambdaParameter(PsiBuilder builder, boolean typed) {
577     return parseListElement(builder, typed, true, false, false);
578   }
579
580   @Nullable
581   private PsiBuilder.Marker parseListElement(PsiBuilder builder, boolean typed, boolean ellipsis, boolean disjunctiveType, boolean resource) {
582     PsiBuilder.Marker param = builder.mark();
583
584     Pair<PsiBuilder.Marker, Boolean> modListInfo = parseModifierList(builder);
585
586     ReferenceParser.TypeInfo typeInfo = null;
587     if (typed) {
588       int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD;
589       if (ellipsis) flags |= ReferenceParser.ELLIPSIS;
590       if (disjunctiveType) flags |= ReferenceParser.DISJUNCTIONS;
591       typeInfo = myParser.getReferenceParser().parseTypeInfo(builder, flags);
592
593       if (typeInfo == null) {
594         if (Boolean.TRUE.equals(modListInfo.second)) {
595           param.rollbackTo();
596           return null;
597         }
598         else {
599           error(builder, JavaErrorMessages.message("expected.type"));
600           emptyElement(builder, JavaElementType.TYPE);
601         }
602       }
603     }
604
605     if (expect(builder, JavaTokenType.IDENTIFIER)) {
606       if (!resource) {
607         eatBrackets(builder, typeInfo != null && typeInfo.isVarArg ? "expected.rparen" : null);
608         done(param, JavaElementType.PARAMETER);
609         return param;
610       }
611     }
612     else {
613       error(builder, JavaErrorMessages.message("expected.identifier"));
614       param.drop();
615       return modListInfo.first;
616     }
617
618     if (expectOrError(builder, JavaTokenType.EQ, "expected.eq")) {
619       if (myParser.getExpressionParser().parse(builder) == null) {
620         error(builder, JavaErrorMessages.message("expected.expression"));
621       }
622     }
623
624     done(param, JavaElementType.RESOURCE_VARIABLE);
625     return param;
626   }
627
628   @Nullable
629   private PsiBuilder.Marker parseFieldOrLocalVariable(PsiBuilder builder, PsiBuilder.Marker declaration, int declarationStart, Context context) {
630     final IElementType varType;
631     if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) {
632       varType = JavaElementType.FIELD;
633     }
634     else if (context == Context.CODE_BLOCK) {
635       varType = JavaElementType.LOCAL_VARIABLE;
636     }
637     else {
638       declaration.drop();
639       assert false : "Unexpected context: " + context;
640       return null;
641     }
642
643     PsiBuilder.Marker variable = declaration;
644     boolean unclosed = false;
645     boolean eatSemicolon = true;
646     boolean shouldRollback;
647     boolean openMarker = true;
648     while (true) {
649       shouldRollback = true;
650
651       if (!eatBrackets(builder, null)) {
652         unclosed = true;
653       }
654
655       if (expect(builder, JavaTokenType.EQ)) {
656         final PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
657         if (expr != null) {
658           shouldRollback = false;
659         }
660         else {
661           error(builder, JavaErrorMessages.message("expected.expression"));
662           unclosed = true;
663           break;
664         }
665       }
666
667       if (builder.getTokenType() != JavaTokenType.COMMA) break;
668       done(variable, varType);
669       builder.advanceLexer();
670
671       if (builder.getTokenType() != JavaTokenType.IDENTIFIER) {
672         error(builder, JavaErrorMessages.message("expected.identifier"));
673         unclosed = true;
674         eatSemicolon = false;
675         openMarker = false;
676         break;
677       }
678
679       variable = builder.mark();
680       builder.advanceLexer();
681     }
682
683     if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) {
684       builder.advanceLexer();
685     }
686     else {
687       // special treatment (see DeclarationParserTest.testMultiLineUnclosed())
688       if (!builder.eof() && shouldRollback) {
689         final CharSequence text = builder.getOriginalText();
690         final int spaceEnd = builder.getCurrentOffset();
691         final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES);
692         final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS);
693
694         if (declarationStart < lineStart && lineStart < spaceStart) {
695           final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES);
696           declaration.rollbackTo();
697           return parse(stoppingBuilder(builder, newBufferEnd), context);
698         }
699       }
700
701       if (!unclosed) {
702         error(builder, JavaErrorMessages.message("expected.semicolon"));
703       }
704     }
705
706     if (openMarker) {
707       done(variable, varType);
708     }
709
710     return declaration;
711   }
712
713   private boolean eatBrackets(PsiBuilder builder, @Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) {
714     IElementType tokenType = builder.getTokenType();
715     if (tokenType != JavaTokenType.LBRACKET && tokenType != JavaTokenType.AT) return true;
716
717     PsiBuilder.Marker marker = builder.mark();
718
719     int count = 0;
720     while (true) {
721       parseAnnotations(builder);
722       if (!expect(builder, JavaTokenType.LBRACKET)) {
723         break;
724       }
725       ++count;
726       if (!expect(builder, JavaTokenType.RBRACKET)) {
727         break;
728       }
729       ++count;
730     }
731
732     if (count == 0) {
733       // just annotation, most probably belongs to a next declaration
734       marker.rollbackTo();
735       return true;
736     }
737
738     if (errorKey != null) {
739       marker.error(JavaErrorMessages.message(errorKey));
740     }
741     else {
742       marker.drop();
743     }
744
745     boolean paired = count % 2 == 0;
746     if (!paired) {
747       error(builder, JavaErrorMessages.message("expected.rbracket"));
748     }
749     return paired;
750   }
751
752   @Nullable
753   public PsiBuilder.Marker parseAnnotations(final PsiBuilder builder) {
754     PsiBuilder.Marker firstAnno = null;
755
756     while (builder.getTokenType() == JavaTokenType.AT) {
757       final PsiBuilder.Marker anno = parseAnnotation(builder);
758       if (firstAnno == null) firstAnno = anno;
759     }
760
761     return firstAnno;
762   }
763
764   @NotNull
765   public PsiBuilder.Marker parseAnnotation(final PsiBuilder builder) {
766     assert builder.getTokenType() == JavaTokenType.AT : builder.getTokenType();
767     final PsiBuilder.Marker anno = builder.mark();
768     builder.advanceLexer();
769
770     PsiBuilder.Marker classRef = null;
771     if (builder.getTokenType() == JavaTokenType.IDENTIFIER) {
772       classRef = myParser.getReferenceParser().parseJavaCodeReference(builder, true, false, false, false);
773     }
774     if (classRef == null) {
775       error(builder, JavaErrorMessages.message("expected.class.reference"));
776     }
777
778     parseAnnotationParameterList(builder);
779
780     done(anno, JavaElementType.ANNOTATION);
781     return anno;
782   }
783
784   @NotNull
785   private PsiBuilder.Marker parseAnnotationParameterList(final PsiBuilder builder) {
786     PsiBuilder.Marker list = builder.mark();
787
788     if (!expect(builder, JavaTokenType.LPARENTH)) {
789       done(list, JavaElementType.ANNOTATION_PARAMETER_LIST);
790       return list;
791     }
792
793     if (expect(builder, JavaTokenType.RPARENTH)) {
794       done(list, JavaElementType.ANNOTATION_PARAMETER_LIST);
795       return list;
796     }
797
798     final boolean isFirstParamNamed = parseAnnotationParameter(builder, true);
799     boolean isFirstParamWarned = false;
800
801     boolean afterBad = false;
802     while (true) {
803       final IElementType tokenType = builder.getTokenType();
804       if (tokenType == null) {
805         error(builder, JavaErrorMessages.message("expected.parameter"));
806         break;
807       }
808       else if (expect(builder, JavaTokenType.RPARENTH)) {
809         break;
810       }
811       else if (tokenType == JavaTokenType.COMMA) {
812         final PsiBuilder.Marker errorStart = builder.mark();
813         final PsiBuilder.Marker errorEnd = builder.mark();
814         builder.advanceLexer();
815         final boolean hasParamName = parseAnnotationParameter(builder, false);
816         if (!isFirstParamNamed && hasParamName && !isFirstParamWarned) {
817           errorStart.errorBefore(JavaErrorMessages.message("annotation.name.is.missing"), errorEnd);
818           isFirstParamWarned = true;
819         }
820         else {
821           errorStart.drop();
822         }
823         errorEnd.drop();
824       }
825       else if (!afterBad) {
826         error(builder, JavaErrorMessages.message("expected.comma.or.rparen"));
827         builder.advanceLexer();
828         afterBad = true;
829       }
830       else {
831         afterBad = false;
832         parseAnnotationParameter(builder, false);
833       }
834     }
835
836     done(list, JavaElementType.ANNOTATION_PARAMETER_LIST);
837     return list;
838   }
839
840   private boolean parseAnnotationParameter(final PsiBuilder builder, final boolean mayBeSimple) {
841     PsiBuilder.Marker pair = builder.mark();
842
843     if (mayBeSimple) {
844       parseAnnotationValue(builder);
845       if (builder.getTokenType() != JavaTokenType.EQ) {
846         done(pair, JavaElementType.NAME_VALUE_PAIR);
847         return false;
848       }
849
850       pair.rollbackTo();
851       pair = builder.mark();
852     }
853
854     final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, "expected.identifier");
855
856     expectOrError(builder, JavaTokenType.EQ, "expected.eq");
857
858     parseAnnotationValue(builder);
859
860     done(pair, JavaElementType.NAME_VALUE_PAIR);
861
862     return hasName;
863   }
864
865   @NotNull
866   public PsiBuilder.Marker parseAnnotationValue(final PsiBuilder builder) {
867     PsiBuilder.Marker result;
868
869     final IElementType tokenType = builder.getTokenType();
870     if (tokenType == JavaTokenType.AT) {
871       result = parseAnnotation(builder);
872     }
873     else if (tokenType == JavaTokenType.LBRACE) {
874       result = parseAnnotationArrayInitializer(builder);
875     }
876     else {
877       result = myParser.getExpressionParser().parseConditional(builder);
878     }
879
880     if (result == null) {
881       result = builder.mark();
882       result.error(JavaErrorMessages.message("expected.value"));
883     }
884
885     return result;
886   }
887
888   @NotNull
889   private PsiBuilder.Marker parseAnnotationArrayInitializer(final PsiBuilder builder) {
890     assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType();
891     final PsiBuilder.Marker annoArray = builder.mark();
892     builder.advanceLexer();
893
894     if (expect(builder, JavaTokenType.RBRACE)) {
895       done(annoArray, JavaElementType.ANNOTATION_ARRAY_INITIALIZER);
896       return annoArray;
897     }
898
899     parseAnnotationValue(builder);
900
901     boolean unclosed = false;
902     while (true) {
903       if (expect(builder, JavaTokenType.RBRACE)) {
904         break;
905       }
906       else if (expect(builder, JavaTokenType.COMMA)) {
907         if (builder.getTokenType() != JavaTokenType.RBRACE) {
908           parseAnnotationValue(builder);
909         }
910       }
911       else {
912         error(builder, JavaErrorMessages.message("expected.rbrace"));
913         unclosed = true;
914         break;
915       }
916     }
917
918     done(annoArray, JavaElementType.ANNOTATION_ARRAY_INITIALIZER);
919     if (unclosed) {
920       annoArray.setCustomEdgeTokenBinders(null, WhitespacesBinders.GREEDY_RIGHT_BINDER);
921     }
922     return annoArray;
923   }
924 }