| /* |
| * 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.refactoring.convertToJava; |
| |
| import com.intellij.psi.PsiClass; |
| import com.intellij.psi.PsiType; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrSwitchStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrBreakStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseLabel; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator; |
| |
| /** |
| * @author Maxim.Medvedev |
| */ |
| public class SwitchStatementGenerator { |
| |
| private SwitchStatementGenerator() { } |
| |
| public static void generate(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @NotNull GrSwitchStatement switchStatement) { |
| final GrExpression condition = switchStatement.getCondition(); |
| final GrCaseSection[] caseSections = switchStatement.getCaseSections(); |
| |
| final PsiType type = condition == null ? null : TypesUtil.unboxPrimitiveTypeWrapper(condition.getType()); |
| if (type == null || isValidTypeForSwitchSelector(type)) { |
| generateSwitch(builder, context, condition, caseSections); |
| } |
| else { |
| generateIfs(builder, context, condition, caseSections); |
| } |
| } |
| |
| private static boolean isValidTypeForSwitchSelector(@NotNull PsiType type) { |
| if (TypeConversionUtil.getTypeRank(type) <= TypeConversionUtil.INT_RANK) { |
| return true; |
| } |
| |
| PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(type); |
| if (aClass != null && aClass.isEnum()) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static void generateIfs(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @NotNull GrExpression condition, |
| @NotNull GrCaseSection[] caseSections) { |
| final GrExpression ref; |
| if (condition instanceof GrReferenceExpression) { |
| ref = condition; |
| } |
| else { |
| final String varName = generateConditionVar(builder, context, condition); |
| ref = GroovyPsiElementFactory.getInstance(context.project).createExpressionFromText(varName); |
| } |
| final GrExpression[] args = {ref}; |
| generateIfFromCaseSection(builder, context, caseSections, 0, args); |
| } |
| |
| private static void generateIfFromCaseSection(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @NotNull final GrCaseSection[] caseSections, |
| final int i, |
| @NotNull final GrExpression[] args) { |
| |
| GenerationUtil.writeStatement(builder, context, null, new StatementWriter() { |
| @Override |
| public void writeStatement(StringBuilder builder, ExpressionContext context) { |
| if (caseSections.length == 1) { |
| final GrCaseLabel[] labels = caseSections[0].getCaseLabels(); |
| if (labels.length == 1 && labels[0].isDefault()) { |
| builder.append("if(true)"); |
| } |
| } |
| |
| GrCaseSection section = caseSections[i]; |
| final GrCaseLabel[] labels = section.getCaseLabels(); |
| final boolean isCase = labels.length > 1 || !labels[0].isDefault(); |
| |
| if (isCase) { |
| writeCondition(builder, context, section, labels, args); |
| } |
| writeCaseBody(builder, context, i, caseSections); |
| |
| if (isCase && i != caseSections.length - 1) { |
| builder.append("\nelse "); |
| StringBuilder elseBuilder = new StringBuilder(); |
| final ExpressionContext elseContext = context.extend(); |
| |
| generateIfFromCaseSection(elseBuilder, elseContext, caseSections, i + 1, args); |
| GenerationUtil.insertStatementFromContextBefore(builder, elseContext); |
| builder.append(elseBuilder); |
| } |
| if (!context.myStatements.isEmpty()) { |
| context.setInsertCurlyBrackets(); |
| } |
| } |
| }); |
| } |
| |
| private static void writeCaseBody(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| int i, |
| @NotNull GrCaseSection[] caseSections) { |
| builder.append("{\n"); |
| |
| final ExpressionContext extended = context.extend(); |
| CodeBlockGenerator generator = new CodeBlockGenerator(builder, extended); |
| |
| Outer: |
| for (int j = i; j < caseSections.length; j++) { |
| GrCaseSection curSection = caseSections[j]; |
| final GrStatement[] statements = curSection.getStatements(); |
| for (GrStatement statement : statements) { |
| if (statement instanceof GrBreakStatement && ((GrBreakStatement)statement).getLabelIdentifier() == null) { |
| break Outer; |
| } |
| statement.accept(generator); |
| builder.append("\n"); |
| } |
| if (brakesFlow(curSection)) break; |
| } |
| |
| builder.append('}'); |
| } |
| |
| private static boolean brakesFlow(GrCaseSection section) { |
| final GrStatement[] statements = section.getStatements(); |
| return statements.length > 0 && !ControlFlowUtils.statementMayCompleteNormally(ArrayUtil.getLastElement(statements)); |
| } |
| |
| private static void writeCondition(StringBuilder builder, |
| ExpressionContext context, |
| GrCaseSection section, |
| GrCaseLabel[] labels, |
| GrExpression[] args) { |
| builder.append("if ("); |
| for (GrCaseLabel label : labels) { |
| if (label.isDefault()) { |
| builder.append("true"); |
| } |
| else { |
| GenerationUtil.invokeMethodByName( |
| label.getValue(), |
| "isCase", |
| args, |
| GrNamedArgument.EMPTY_ARRAY, |
| GrClosableBlock.EMPTY_ARRAY, |
| new ExpressionGenerator(builder, context), |
| section |
| ); |
| } |
| builder.append("||"); |
| } |
| builder.delete(builder.length() - 2, builder.length()); |
| builder.append(") "); |
| } |
| |
| private static String generateConditionVar(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @NotNull GrExpression condition) { |
| StringBuilder conditionBuilder = new StringBuilder(); |
| final PsiType type = condition.getType(); |
| final String varName = GenerationUtil.validateName("switchArg", condition, context); |
| conditionBuilder.append("final "); |
| TypeWriter.writeType(conditionBuilder, type, condition); |
| conditionBuilder.append(' ').append(varName).append(" = "); |
| condition.accept(new ExpressionGenerator(conditionBuilder, context)); |
| conditionBuilder.append(";\n"); |
| GenerationUtil.insertStatementFromContextBefore(builder, context); |
| builder.append(conditionBuilder); |
| return varName; |
| } |
| |
| private static void generateSwitch(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @Nullable GrExpression condition, |
| @NotNull GrCaseSection[] caseSections) { |
| builder.append("switch ("); |
| if (condition != null) { |
| condition.accept(new ExpressionGenerator(builder, context)); |
| } |
| builder.append(") {\n"); |
| |
| final ExpressionContext innerContext = context.extend(); |
| for (GrCaseSection section : caseSections) { |
| generateCaseSection(builder, context, innerContext, section); |
| } |
| |
| builder.append('}'); |
| } |
| |
| private static void generateCaseSection(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @NotNull ExpressionContext innerContext, |
| @NotNull GrCaseSection section) { |
| for (GrCaseLabel label : section.getCaseLabels()) { |
| writeLabel(builder, context, label); |
| } |
| |
| final GrStatement[] statements = section.getStatements(); |
| CodeBlockGenerator generator = new CodeBlockGenerator(builder, innerContext); |
| for (GrStatement statement : statements) { |
| statement.accept(generator); |
| builder.append("\n"); |
| } |
| } |
| |
| private static void writeLabel(@NotNull StringBuilder builder, |
| @NotNull ExpressionContext context, |
| @NotNull GrCaseLabel label) { |
| if (label.isDefault()) { |
| builder.append("default"); |
| } |
| else { |
| builder.append("case "); |
| final GrExpression value = label.getValue(); |
| Object evaluated; |
| try { |
| evaluated = GroovyConstantExpressionEvaluator.evaluate(value); |
| } |
| catch (Throwable e) { |
| evaluated = null; |
| } |
| |
| if (evaluated != null) { |
| builder.append(evaluated); |
| } |
| else if (value != null) { |
| value.accept(new ExpressionGenerator(builder, context)); |
| } |
| } |
| |
| builder.append(":\n"); |
| } |
| } |