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