| /* |
| * 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(); |
| } |
| } |