| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.jetbrains.plugins.groovy.lang.parser.parsing.statements.typeDefinitions; |
| |
| import com.intellij.codeInsight.completion.CompletionUtilCore; |
| import com.intellij.lang.PsiBuilder; |
| import com.intellij.psi.tree.IElementType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.GroovyBundle; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; |
| import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; |
| import org.jetbrains.plugins.groovy.lang.parser.parsing.types.TypeArguments; |
| import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils; |
| import org.jetbrains.plugins.groovy.lang.psi.stubs.elements.GrReferenceListElementType; |
| |
| /** |
| * @author: Dmitry.Krasilschikov |
| * @date: 20.03.2007 |
| */ |
| |
| public class ReferenceElement { |
| public static final String DUMMY_IDENTIFIER = CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED; //inserted by completion |
| |
| @NotNull |
| public static IElementType parseReferenceList(@NotNull PsiBuilder builder, |
| @NotNull final IElementType startElement, |
| @NotNull final GrReferenceListElementType<?> clauseType, |
| @NotNull ClassType type) { |
| PsiBuilder.Marker isMarker = builder.mark(); |
| |
| if (!ParserUtils.getToken(builder, startElement)) { |
| if (clauseType == GroovyElementTypes.IMPLEMENTS_CLAUSE && (type == ClassType.INTERFACE || type == ClassType.ANNOTATION) || |
| clauseType == GroovyElementTypes.EXTENDS_CLAUSE && type == ClassType.ENUM || |
| type == ClassType.ANNOTATION) { |
| isMarker.rollbackTo(); |
| return GroovyElementTypes.NONE; |
| } |
| |
| return finish(builder, clauseType, isMarker, null, null); |
| } |
| |
| PsiBuilder.Marker space = builder.mark(); |
| ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); |
| |
| if (parseReferenceElement(builder) == ReferenceElementResult.FAIL) { |
| return finish(builder, clauseType, isMarker, space, GroovyBundle.message("identifier.expected")); |
| } |
| else { |
| space.drop(); |
| } |
| |
| while (ParserUtils.getToken(builder, GroovyTokenTypes.mCOMMA)) { |
| space = builder.mark(); |
| ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); |
| |
| if (parseReferenceElement(builder) == ReferenceElementResult.FAIL) { |
| return finish(builder, clauseType, isMarker, space, GroovyBundle.message("identifier.expected")); |
| } |
| else { |
| space.drop(); |
| } |
| } |
| |
| return finish(builder, clauseType, isMarker, null, null); |
| } |
| |
| @NotNull |
| private static GrReferenceListElementType<?> finish(@NotNull PsiBuilder builder, |
| @NotNull GrReferenceListElementType<?> clauseType, |
| @NotNull PsiBuilder.Marker isMarker, |
| @Nullable PsiBuilder.Marker space, |
| @Nullable String error) { |
| if (space != null) space.rollbackTo(); |
| if (error != null) builder.error(error); |
| isMarker.done(clauseType); |
| return clauseType; |
| } |
| |
| public enum ReferenceElementResult { |
| IDENTIFIER, PATH_REF, REF_WITH_TYPE_PARAMS, FAIL |
| } |
| |
| public static ReferenceElementResult parseForImport(@NotNull PsiBuilder builder) { |
| return parse(builder, false, false, true, false, false); |
| } |
| |
| public static ReferenceElementResult parseForPackage(@NotNull PsiBuilder builder) { |
| return parse(builder, false, false, true, false, false); |
| } |
| |
| //it doesn't important first letter of identifier of ThrowClause, of Annotation, of new Expression, of implements, extends, superclass clauses |
| public static ReferenceElementResult parseReferenceElement(@NotNull PsiBuilder builder) { |
| return parseReferenceElement(builder, false, true); |
| } |
| |
| public static ReferenceElementResult parseReferenceElement(@NotNull PsiBuilder builder, boolean isUpperCase, final boolean expressionPossible) { |
| return parse(builder, isUpperCase, true, false, false, expressionPossible); |
| } |
| |
| public static ReferenceElementResult parse(@NotNull PsiBuilder builder, |
| boolean checkUpperCase, |
| boolean parseTypeArgs, |
| boolean lineFeedAllowed, |
| boolean allowDiamond, |
| boolean expressionPossible) { |
| PsiBuilder.Marker internalTypeMarker = builder.mark(); |
| |
| String lastIdentifier = builder.getTokenText(); |
| |
| if (!ParserUtils.getToken(builder, TokenSets.CODE_REFERENCE_ELEMENT_NAME_TOKENS)) { |
| internalTypeMarker.rollbackTo(); |
| return ReferenceElementResult.FAIL; |
| } |
| |
| boolean hasTypeArguments = false; |
| if (parseTypeArgs && TypeArguments.parseTypeArguments(builder, expressionPossible, allowDiamond)) { |
| hasTypeArguments = true; |
| } |
| |
| internalTypeMarker.done(GroovyElementTypes.REFERENCE_ELEMENT); |
| internalTypeMarker = internalTypeMarker.precede(); |
| |
| boolean hasDots = builder.getTokenType() == GroovyTokenTypes.mDOT; |
| |
| while (builder.getTokenType() == GroovyTokenTypes.mDOT) { |
| |
| if ((ParserUtils.lookAhead(builder, GroovyTokenTypes.mDOT, GroovyTokenTypes.mSTAR) || ParserUtils.lookAhead(builder, |
| GroovyTokenTypes.mDOT, |
| GroovyTokenTypes.mNLS, |
| GroovyTokenTypes.mSTAR)) && lineFeedAllowed) { |
| internalTypeMarker.drop(); |
| return ReferenceElementResult.PATH_REF; |
| } |
| |
| ParserUtils.getToken(builder, GroovyTokenTypes.mDOT); |
| |
| if (lineFeedAllowed) { |
| ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); |
| } |
| |
| lastIdentifier = builder.getTokenText(); |
| |
| if (!ParserUtils.getToken(builder, TokenSets.CODE_REFERENCE_ELEMENT_NAME_TOKENS)) { |
| if (TokenSets.REFERENCE_NAME_PREFIXES.contains(builder.getTokenType())) { |
| internalTypeMarker.rollbackTo(); |
| return ReferenceElementResult.FAIL; |
| } |
| builder.error(GroovyBundle.message("identifier.expected")); |
| internalTypeMarker.done(GroovyElementTypes.REFERENCE_ELEMENT); |
| return ReferenceElementResult.PATH_REF; |
| } |
| |
| if (parseTypeArgs && TypeArguments.parseTypeArguments(builder, expressionPossible, allowDiamond)) { |
| hasTypeArguments = true; |
| } |
| |
| internalTypeMarker.done(GroovyElementTypes.REFERENCE_ELEMENT); |
| internalTypeMarker = internalTypeMarker.precede(); |
| } |
| |
| if (lastIdentifier == null) { |
| //eof |
| return ReferenceElementResult.FAIL; |
| } |
| |
| char firstChar = lastIdentifier.charAt(0); |
| if (checkUpperCase) { |
| if (!Character.isUpperCase(firstChar) || DUMMY_IDENTIFIER.equals(lastIdentifier)) { //hack to make completion work |
| internalTypeMarker.rollbackTo(); |
| return ReferenceElementResult.FAIL; |
| } |
| } |
| |
| internalTypeMarker.drop(); |
| |
| return hasTypeArguments ? ReferenceElementResult.REF_WITH_TYPE_PARAMS : |
| hasDots ? ReferenceElementResult.PATH_REF : |
| ReferenceElementResult.IDENTIFIER; |
| } |
| |
| } |