blob: 49eadf90514244cdfd5ce017137adfc5f2c90e4a [file] [log] [blame]
/*
* 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;
}
}