blob: 8449b47c83f5e26f76cc80dccd7fb859789e8f6c [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.expressions.primary;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.diagnostic.Logger;
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.GroovyElementType;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.parser.GroovyParser;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.blocks.OpenOrClosableBlock;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.PathExpression;
import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;
/**
* @author ilyas
*/
public class CompoundStringExpression {
private static final Logger LOG = Logger.getInstance(CompoundStringExpression.class);
private final PsiBuilder myBuilder;
private final GroovyParser myParser;
private final boolean myForRefExpr;
private final IElementType myBegin;
private final IElementType myContent;
private final IElementType myEnd;
private final IElementType mySimpleLiteral;
private final GroovyElementType myCompoundLiteral;
private final String myMessage;
private CompoundStringExpression(PsiBuilder builder,
GroovyParser parser,
boolean forRefExpr,
IElementType begin,
IElementType content,
IElementType end,
IElementType literal,
GroovyElementType compoundLiteral,
String message) {
myBuilder = builder;
myParser = parser;
myForRefExpr = forRefExpr;
myBegin = begin;
myContent = content;
myEnd = end;
mySimpleLiteral = literal;
myCompoundLiteral = compoundLiteral;
myMessage = message;
}
private boolean parse() {
PsiBuilder.Marker marker = myBuilder.mark();
final PsiBuilder.Marker marker2 = myBuilder.mark();
LOG.assertTrue(ParserUtils.getToken(myBuilder, myBegin));
if (mySimpleLiteral != null && myBuilder.getTokenType() == myEnd) {
myBuilder.advanceLexer();
finishSimpleLiteral(marker, marker2);
return true;
}
if (myBuilder.getTokenType() == myContent) {
final PsiBuilder.Marker contentMarker = myBuilder.mark();
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == GroovyTokenTypes.mDOLLAR || mySimpleLiteral == null) {
contentMarker.done(GroovyElementTypes.GSTRING_CONTENT);
}
else {
contentMarker.drop();
}
}
else {
processContent();
}
boolean hasInjection = myBuilder.getTokenType() == GroovyTokenTypes.mDOLLAR;
while (myBuilder.getTokenType() == GroovyTokenTypes.mDOLLAR) {
parseInjection();
processContent();
}
if (!ParserUtils.getToken(myBuilder, myEnd)) {
myBuilder.error(myMessage);
}
if (hasInjection || mySimpleLiteral == null) {
marker2.drop();
marker.done(myCompoundLiteral);
}
else {
finishSimpleLiteral(marker, marker2);
}
return hasInjection;
}
private void processContent() {
PsiBuilder.Marker marker = myBuilder.mark();
if (myBuilder.getTokenType() == myContent) {
myBuilder.advanceLexer();
}
else {
myBuilder.mark().done(myContent);
}
marker.done(GroovyElementTypes.GSTRING_CONTENT);
}
private void finishSimpleLiteral(PsiBuilder.Marker marker, PsiBuilder.Marker marker2) {
marker2.done(mySimpleLiteral);
if (myForRefExpr) {
marker.drop();
}
else {
marker.done(GroovyElementTypes.LITERAL);
}
}
/**
* Parses heredoc's content in GString
*
* @return nothing
*/
private boolean parseInjection() {
if (myBuilder.getTokenType() != GroovyTokenTypes.mDOLLAR) return false;
final PsiBuilder.Marker injection = myBuilder.mark();
ParserUtils.getToken(myBuilder, GroovyTokenTypes.mDOLLAR);
if (myBuilder.getTokenType() == GroovyTokenTypes.mIDENT || myBuilder.getTokenType() == GroovyTokenTypes.kTHIS) {
PathExpression.parse(myBuilder, myParser);
}
else if (myBuilder.getTokenType() == GroovyTokenTypes.mLCURLY) {
OpenOrClosableBlock.parseClosableBlock(myBuilder, myParser);
}
else {
ParserUtils.wrapError(myBuilder, GroovyBundle.message("identifier.or.block.expected"));
}
injection.done(GroovyElementTypes.GSTRING_INJECTION);
return true;
}
/**
* Groovy lexer does not smart enough to understand whether a regex contents injections or not. So the parser should do this job.
* We create additional marker2 for the case of absence of injections. In this case resulting tree is as follows:
*
* Regex
* mRegexLiteral (mDollarSlashRegexLiteral)
* mRegexBegin (........................)
* mRegexContent (........................)
* mRegexEnd (........................)
*
* This tree emulates tree of simple GrLiteralImpl structure so we can use regexes where simple strings are expected.
*
* @return true if there are any injections
*/
public static boolean parse(@NotNull PsiBuilder builder,
@NotNull GroovyParser parser,
boolean forRefExpr,
@NotNull IElementType begin,
@NotNull IElementType content,
@NotNull IElementType end,
@Nullable IElementType literal,
@NotNull GroovyElementType compoundLiteral,
@NotNull String message) {
return new CompoundStringExpression(builder, parser, forRefExpr, begin, content, end, literal, compoundLiteral, message).parse();
}
}