From 3c691062adb1e4952c4a80bf823591a5c1084c4c Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 26 Apr 2013 14:10:00 +0400 Subject: [PATCH] Implement if-to-when and when-to-if transformations --- .../IfToWhenIntention/after.kt.template | 11 + .../IfToWhenIntention/before.kt.template | 7 + .../IfToWhenIntention/description.html | 5 + .../WhenToIfIntention/after.kt.template | 7 + .../WhenToIfIntention/before.kt.template | 11 + .../WhenToIfIntention/description.html | 5 + .../branchedTransformations/IfWhenUtils.java | 206 ++++++++++++++++++ .../intentions/IfToWhenIntention.java | 36 +++ .../intentions/WhenToIfIntention.java | 52 +++++ 9 files changed, 340 insertions(+) create mode 100644 idea/resources/intentionDescriptions/IfToWhenIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/IfToWhenIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/IfToWhenIntention/description.html create mode 100644 idea/resources/intentionDescriptions/WhenToIfIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/WhenToIfIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/WhenToIfIntention/description.html create mode 100644 idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/IfWhenUtils.java create mode 100644 idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IfToWhenIntention.java create mode 100644 idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/WhenToIfIntention.java diff --git a/idea/resources/intentionDescriptions/IfToWhenIntention/after.kt.template b/idea/resources/intentionDescriptions/IfToWhenIntention/after.kt.template new file mode 100644 index 00000000000..949cf0ab13d --- /dev/null +++ b/idea/resources/intentionDescriptions/IfToWhenIntention/after.kt.template @@ -0,0 +1,11 @@ +when (n) { + 1 -> { + res = "one" + } + 2 -> { + res = "two" + } + else -> { + res = "???" + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/IfToWhenIntention/before.kt.template b/idea/resources/intentionDescriptions/IfToWhenIntention/before.kt.template new file mode 100644 index 00000000000..0fd94490980 --- /dev/null +++ b/idea/resources/intentionDescriptions/IfToWhenIntention/before.kt.template @@ -0,0 +1,7 @@ +if (n == 1) { + res = "one" +} else if (n == 2) { + res = "two" +} else { + res = "???" +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/IfToWhenIntention/description.html b/idea/resources/intentionDescriptions/IfToWhenIntention/description.html new file mode 100644 index 00000000000..170bacebb07 --- /dev/null +++ b/idea/resources/intentionDescriptions/IfToWhenIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts 'if' expression to equivalent 'when' expression + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/WhenToIfIntention/after.kt.template b/idea/resources/intentionDescriptions/WhenToIfIntention/after.kt.template new file mode 100644 index 00000000000..0fd94490980 --- /dev/null +++ b/idea/resources/intentionDescriptions/WhenToIfIntention/after.kt.template @@ -0,0 +1,7 @@ +if (n == 1) { + res = "one" +} else if (n == 2) { + res = "two" +} else { + res = "???" +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/WhenToIfIntention/before.kt.template b/idea/resources/intentionDescriptions/WhenToIfIntention/before.kt.template new file mode 100644 index 00000000000..949cf0ab13d --- /dev/null +++ b/idea/resources/intentionDescriptions/WhenToIfIntention/before.kt.template @@ -0,0 +1,11 @@ +when (n) { + 1 -> { + res = "one" + } + 2 -> { + res = "two" + } + else -> { + res = "???" + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/WhenToIfIntention/description.html b/idea/resources/intentionDescriptions/WhenToIfIntention/description.html new file mode 100644 index 00000000000..e81fa8a7a38 --- /dev/null +++ b/idea/resources/intentionDescriptions/WhenToIfIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts 'when' expression to one or more 'if' expressions + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/IfWhenUtils.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/IfWhenUtils.java new file mode 100644 index 00000000000..08e220ee152 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/IfWhenUtils.java @@ -0,0 +1,206 @@ +package org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lexer.JetTokens; + +import java.util.ArrayList; +import java.util.List; + +public class IfWhenUtils { + + public static final String TRANSFORM_WITHOUT_CHECK = + "Expression must be checked before applying transformation"; + + private IfWhenUtils() { + } + + public static boolean checkIfToWhen(@NotNull JetIfExpression ifExpression) { + return ifExpression.getCondition() != null && ifExpression.getThen() != null && ifExpression.getElse() != null; + } + + public static boolean checkWhenToIf(@NotNull JetWhenExpression whenExpression) { + return !whenExpression.getEntries().isEmpty() && JetPsiUtil.checkWhenExpressionHasSingleElse(whenExpression); + } + + private static void assertNotNull(JetExpression expression) { + assert expression != null : TRANSFORM_WITHOUT_CHECK; + } + + private static List splitExpressionToOrBranches(JetExpression expression) { + final List branches = new ArrayList(); + + expression.accept( + new JetVisitorVoid() { + @Override + public void visitBinaryExpression(JetBinaryExpression expression) { + if (expression.getOperationToken() == JetTokens.OROR) { + JetExpression left = expression.getLeft(); + JetExpression right = expression.getRight(); + + if (left != null) { + left.accept(this); + } + + if (right != null) { + right.accept(this); + } + } else { + visitExpression(expression); + } + } + + @Override + public void visitParenthesizedExpression(JetParenthesizedExpression expression) { + JetExpression baseExpression = expression.getExpression(); + + if (baseExpression != null) { + baseExpression.accept(this); + } + } + + @Override + public void visitExpression(JetExpression expression) { + branches.add(expression); + } + } + ); + + return branches; + } + + public static void transformIfToWhen(@NotNull JetIfExpression ifExpression) { + List positiveBranches = new ArrayList(); + JetExpression elseExpression = null; + + JetIfExpression currIfExpression = ifExpression; + do { + JetExpression condition = currIfExpression.getCondition(); + JetExpression thenBranch = currIfExpression.getThen(); + JetExpression elseBranch = currIfExpression.getElse(); + + assertNotNull(condition); + assertNotNull(thenBranch); + assertNotNull(elseBranch); + + //noinspection ConstantConditions + positiveBranches.add(new MultiGuardedExpression(splitExpressionToOrBranches(condition), thenBranch)); + + if (elseBranch instanceof JetIfExpression) { + currIfExpression = (JetIfExpression) elseBranch; + } + else { + currIfExpression = null; + elseExpression = elseBranch; + } + } while (currIfExpression != null); + + JetPsiFactory.WhenTemplateBuilder builder = new JetPsiFactory.WhenTemplateBuilder(false); + for (MultiGuardedExpression positiveBranch : positiveBranches) { + builder.addBranchWithMultiCondition(positiveBranch.getConditions().size()); + } + + JetWhenExpression whenExpression = builder.toExpression(ifExpression.getProject()); + + int i = 0; + List entries = whenExpression.getEntries(); + for (JetWhenEntry entry : entries) { + if (entry.isElse()) { + //noinspection ConstantConditions + entry.getExpression().replace(elseExpression); + break; + } + + MultiGuardedExpression branch = positiveBranches.get(i++); + + //noinspection ConstantConditions + entry.getExpression().replace(branch.getBaseExpression()); + + int j = 0; + JetWhenCondition[] conditions = entry.getConditions(); + for (JetWhenCondition condition : conditions) { + assert condition instanceof JetWhenConditionWithExpression : TRANSFORM_WITHOUT_CHECK; + + JetExpression conditionExpression = ((JetWhenConditionWithExpression) condition).getExpression(); + assertNotNull(conditionExpression); + + //noinspection ConstantConditions + conditionExpression.replace(branch.getConditions().get(j++)); + } + } + + ifExpression.replace(whenExpression); + } + + @SuppressWarnings("ConstantConditions") + private static JetExpression combineWhenConditions(Project project, JetWhenCondition[] conditions, JetExpression subject) { + int n = conditions.length; + assert n > 0 : TRANSFORM_WITHOUT_CHECK; + + JetWhenCondition condition = conditions[n - 1]; + assert condition != null : TRANSFORM_WITHOUT_CHECK; + + JetExpression resultExpr = WhenUtils.whenConditionToExpression(condition, subject); + if (n > 1) { + resultExpr = JetPsiFactory.createParenthesizedExpressionIfNeeded(project, resultExpr); + } + + for (int i = n - 2; i >= 0; i--) { + JetWhenCondition currCondition = conditions[i]; + + assert currCondition != null : TRANSFORM_WITHOUT_CHECK; + + resultExpr = JetPsiFactory.createBinaryExpression( + project, + JetPsiFactory.createParenthesizedExpressionIfNeeded(project, WhenUtils.whenConditionToExpression(currCondition, subject)), + "||", + resultExpr); + } + + return resultExpr; + } + + public static void transformWhenToIf(@NotNull JetWhenExpression whenExpression) { + Project project = whenExpression.getProject(); + + JetExpression elseExpression = null; + List positiveBranches = new ArrayList(); + + List entries = whenExpression.getEntries(); + for (JetWhenEntry entry : entries) { + JetExpression branch = entry.getExpression(); + + assertNotNull(branch); + + if (entry.isElse()) { + elseExpression = branch; + } else { + JetExpression branchCondition = combineWhenConditions(project, entry.getConditions(), whenExpression.getSubjectExpression()); + JetExpression branchExpression = entry.getExpression(); + + assertNotNull(branchExpression); + + //noinspection ConstantConditions + positiveBranches.add(new GuardedExpression(branchCondition, branchExpression)); + } + } + + assertNotNull(elseExpression); + assert !positiveBranches.isEmpty() : TRANSFORM_WITHOUT_CHECK; + + JetExpression outerExpression = elseExpression; + + for (int i = positiveBranches.size() - 1; i >= 0; i--) { + GuardedExpression branch = positiveBranches.get(i); + + outerExpression = JetPsiFactory.createIf( + project, + branch.getCondition(), branch.getBaseExpression(), outerExpression, + !(branch.getBaseExpression() instanceof JetBlockExpression), !(outerExpression instanceof JetBlockExpression)); + } + + //noinspection ConstantConditions + whenExpression.replace(outerExpression); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IfToWhenIntention.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IfToWhenIntention.java new file mode 100644 index 00000000000..3d80248f2c3 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IfToWhenIntention.java @@ -0,0 +1,36 @@ +package org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.intentions; + +import com.google.common.base.Predicate; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetIfExpression; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.AbstractCodeTransformationIntention; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.IfWhenUtils; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.core.Transformer; + +public class IfToWhenIntention extends AbstractCodeTransformationIntention { + private static final Transformer TRANSFORMER = new Transformer() { + @NotNull + @Override + public String getKey() { + return "if.to.when"; + } + + @Override + public void transform(@NotNull PsiElement element) { + IfWhenUtils.transformIfToWhen((JetIfExpression) element); + } + }; + + private static final Predicate IS_APPLICABLE = new Predicate() { + @Override + public boolean apply(@Nullable PsiElement input) { + return (input instanceof JetIfExpression) && IfWhenUtils.checkIfToWhen((JetIfExpression) input); + } + }; + + public IfToWhenIntention() { + super(TRANSFORMER, IS_APPLICABLE); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/WhenToIfIntention.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/WhenToIfIntention.java new file mode 100644 index 00000000000..42bfe16da12 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/WhenToIfIntention.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2013 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.jet.plugin.codeInsight.codeTransformations.branchedTransformations.intentions; + +import com.google.common.base.Predicate; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetWhenExpression; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.AbstractCodeTransformationIntention; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.IfWhenUtils; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.core.Transformer; + +public class WhenToIfIntention extends AbstractCodeTransformationIntention { + private static final Transformer TRANSFORMER = new Transformer() { + @NotNull + @Override + public String getKey() { + return "when.to.if"; + } + + @Override + public void transform(@NotNull PsiElement element) { + IfWhenUtils.transformWhenToIf((JetWhenExpression) element); + } + }; + + private static final Predicate IS_APPLICABLE = new Predicate() { + @Override + public boolean apply(@Nullable PsiElement input) { + return (input instanceof JetWhenExpression) && IfWhenUtils.checkWhenToIf((JetWhenExpression) input); + } + }; + + public WhenToIfIntention() { + super(TRANSFORMER, IS_APPLICABLE); + } +}