diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiMatcher.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiMatcher.java new file mode 100644 index 00000000000..5a7b8e04af8 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiMatcher.java @@ -0,0 +1,246 @@ +/* + * 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.lang.psi; + +import java.util.List; + +public class JetPsiMatcher { + private JetPsiMatcher() { + } + + private static boolean checkTypeReferenceMatch(JetTypeReference t1, JetTypeReference t2) { + return (t1 == t2) || (t1 != null && t2 != null && t1.getText().equals(t2.getText())); + } + + private static boolean checkStringTemplateEntryMatch(JetStringTemplateEntry e1, JetStringTemplateEntry e2) { + if (e1 == e2) return true; + if (e1 == null || e2 == null) return false; + + return e1.getClass() == e2.getClass() && checkExpressionMatch(e1.getExpression(), e2.getExpression()); + } + + private static boolean checkWhenConditionMatch(JetWhenCondition cond1, JetWhenCondition cond2) { + if (cond1 == cond2) return true; + if (cond1 == null || cond2 == null) return false; + + if (cond1.getClass() != cond2.getClass()) return false; + + if (cond1 instanceof JetWhenConditionInRange) { + JetWhenConditionInRange inRange1 = (JetWhenConditionInRange) cond1; + JetWhenConditionInRange inRange2 = (JetWhenConditionInRange) cond2; + + return inRange1.isNegated() == inRange2.isNegated() && + checkExpressionMatch(inRange1.getRangeExpression(), inRange2.getRangeExpression()) && + checkExpressionMatch(inRange1.getOperationReference(), inRange2.getOperationReference()); + } + + if (cond1 instanceof JetWhenConditionIsPattern) { + JetWhenConditionIsPattern pattern1 = (JetWhenConditionIsPattern) cond1; + JetWhenConditionIsPattern pattern2 = (JetWhenConditionIsPattern) cond2; + + return pattern1.isNegated() == pattern2.isNegated() && + checkTypeReferenceMatch(pattern1.getTypeRef(), pattern2.getTypeRef()); + } + + if (cond1 instanceof JetWhenConditionWithExpression) { + return checkExpressionMatch(((JetWhenConditionWithExpression) cond1).getExpression(), ((JetWhenConditionWithExpression) cond2).getExpression()); + } + + return false; + } + + private static boolean checkWhenEntryMatch(JetWhenEntry e1, JetWhenEntry e2) { + if (e1 == e2) return true; + if (e1 == null || e2 == null) return false; + + if (!(e1.isElse() == e2.isElse() && checkExpressionMatch(e1.getExpression(), e2.getExpression()))) return false; + + JetWhenCondition[] conditions1 = e1.getConditions(); + JetWhenCondition[] conditions2 = e2.getConditions(); + + int n = conditions1.length; + if (conditions2.length != n) return false; + + for (int i = 0; i < n; i++) { + if (!checkWhenConditionMatch(conditions1[i], conditions2[i])) return false; + } + + return true; + } + + public static boolean checkExpressionMatch(JetExpression e1, JetExpression e2) { + if (e1 == e2) return true; + if (e1 == null || e2 == null) return false; + + e1 = JetPsiUtil.deparenthesizeWithNoTypeResolution(e1); + e2 = JetPsiUtil.deparenthesizeWithNoTypeResolution(e2); + + assert e1 != null && e2 != null; + + if (e1.getClass() != e2.getClass()) return false; + + if (e1 instanceof JetArrayAccessExpression) { + JetArrayAccessExpression aae1 = (JetArrayAccessExpression) e1; + JetArrayAccessExpression aae2 = (JetArrayAccessExpression) e2; + + if (!checkExpressionMatch(aae1.getArrayExpression(), aae2.getArrayExpression())) return false; + + List indexes1 = aae1.getIndexExpressions(); + List indexes2 = aae2.getIndexExpressions(); + + int n = indexes1.size(); + if (indexes2.size() != n) return false; + + for (int i = 0; i < n; i++) { + if (!checkExpressionMatch(indexes1.get(i), indexes2.get(i))) return false; + } + + return true; + } + + if (e1 instanceof JetBinaryExpression) { + JetBinaryExpression be1 = (JetBinaryExpression) e1; + JetBinaryExpression be2 = (JetBinaryExpression) e2; + + return be1.getOperationToken() == be2.getOperationToken() && + checkExpressionMatch(be1.getLeft(), be2.getLeft()) && + checkExpressionMatch(be1.getRight(), be2.getRight()); + } + + if (e1 instanceof JetBinaryExpressionWithTypeRHS) { + JetBinaryExpressionWithTypeRHS bet1 = (JetBinaryExpressionWithTypeRHS) e1; + JetBinaryExpressionWithTypeRHS bet2 = (JetBinaryExpressionWithTypeRHS) e2; + + return checkExpressionMatch(bet1.getLeft(), bet2.getLeft()) && + checkTypeReferenceMatch(bet1.getRight(), bet2.getRight()); + } + + if (e1 instanceof JetCallExpression) { + JetCallExpression call1 = (JetCallExpression) e1; + JetCallExpression call2 = (JetCallExpression) e2; + + if (!checkExpressionMatch(call1.getCalleeExpression(), call2.getCalleeExpression())) return false; + + List args1 = call1.getValueArguments(); + List args2 = call2.getValueArguments(); + + int argCount = args1.size(); + if (args2.size() != argCount) return false; + + for (int i = 0; i < argCount; i++) { + if (!checkExpressionMatch(args1.get(i).getArgumentExpression(), args2.get(i).getArgumentExpression())) return false; + } + + List funLiterals1 = call1.getFunctionLiteralArguments(); + List funLiterals2 = call2.getFunctionLiteralArguments(); + + int funLiteralCount = funLiterals1.size(); + if (funLiterals2.size() != funLiteralCount) return false; + + for (int i = 0; i < argCount; i++) { + if (!checkExpressionMatch(funLiterals1.get(i), funLiterals2.get(i))) return false; + } + + return true; + } + + if (e1 instanceof JetConstantExpression || e1 instanceof JetSimpleNameExpression) { + return e1.getText().equals(e2.getText()); + } + + if (e1 instanceof JetConstructorCalleeExpression) { + return checkTypeReferenceMatch(((JetConstructorCalleeExpression) e1).getTypeReference(), ((JetConstructorCalleeExpression) e2).getTypeReference()); + } + + if (e1 instanceof JetQualifiedExpression) { + return ((JetQualifiedExpression) e1).getOperationSign() == ((JetQualifiedExpression) e2).getOperationSign() && + checkExpressionMatch(((JetQualifiedExpression) e1).getReceiverExpression(), ((JetQualifiedExpression) e2).getReceiverExpression()) && + checkExpressionMatch(((JetQualifiedExpression) e1).getSelectorExpression(), ((JetQualifiedExpression) e2).getSelectorExpression()); + } + + if (e1 instanceof JetIfExpression) { + return checkExpressionMatch(((JetIfExpression) e1).getCondition(), ((JetIfExpression) e2).getCondition()) && + checkExpressionMatch(((JetIfExpression) e1).getThen(), ((JetIfExpression) e2).getThen()) && + checkExpressionMatch(((JetIfExpression) e1).getElse(), ((JetIfExpression) e2).getElse()); + } + + if (e1 instanceof JetIsExpression) { + return checkExpressionMatch(((JetIsExpression) e1).getLeftHandSide(), ((JetIsExpression) e2).getLeftHandSide()) && + checkTypeReferenceMatch(((JetIsExpression) e1).getTypeRef(), ((JetIsExpression) e2).getTypeRef()) && + ((JetIsExpression) e1).isNegated() == ((JetIsExpression) e2).isNegated(); + } + + if (e1 instanceof JetStringTemplateExpression) { + JetStringTemplateExpression str1 = (JetStringTemplateExpression) e1; + JetStringTemplateExpression str2 = (JetStringTemplateExpression) e2; + + JetStringTemplateEntry[] entries1 = str1.getEntries(); + JetStringTemplateEntry[] entries2 = str2.getEntries(); + + int n = entries1.length; + if (entries2.length != n) return false; + + for (int i = 0; i < n; i++) { + if (!checkStringTemplateEntryMatch(entries1[i], entries2[i])) return false; + } + + return true; + } + + if (e1 instanceof JetThrowExpression) { + return checkExpressionMatch(((JetThrowExpression) e1).getThrownExpression(), ((JetThrowExpression) e2).getThrownExpression()); + } + + if (e1 instanceof JetUnaryExpression) { + JetUnaryExpression ue1 = (JetUnaryExpression) e1; + JetUnaryExpression ue2 = (JetUnaryExpression) e2; + + return checkExpressionMatch(ue1.getBaseExpression(), ue2.getBaseExpression()) && + checkExpressionMatch(ue1.getOperationReference(), ue2.getOperationReference()); + } + + if (e1 instanceof JetWhenExpression) { + JetWhenExpression when1 = (JetWhenExpression) e1; + JetWhenExpression when2 = (JetWhenExpression) e2; + + if (checkExpressionMatch(when1.getSubjectExpression(), when2.getSubjectExpression())) return false; + + List entries1 = when1.getEntries(); + List entries2 = when2.getEntries(); + + int n = entries1.size(); + if (entries2.size() != n) return false; + + for (int i = 0; i < n; i++) { + if (!checkWhenEntryMatch(entries1.get(i), entries2.get(i))) return false; + } + } + + if (e1 instanceof JetThisExpression) return true; + + if (e1 instanceof JetSuperExpression) { + JetSuperExpression super1 = (JetSuperExpression) e1; + JetSuperExpression super2 = (JetSuperExpression) e2; + + return checkTypeReferenceMatch(super1.getSuperTypeQualifier(), super2.getSuperTypeQualifier()) && + checkExpressionMatch(super1.getInstanceReference(), super2.getInstanceReference()) && + checkExpressionMatch(super1.getTargetLabel(), super2.getTargetLabel()); + } + + return false; + } +} diff --git a/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/after.kt.template b/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/after.kt.template new file mode 100644 index 00000000000..a3b53a936d0 --- /dev/null +++ b/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/after.kt.template @@ -0,0 +1,11 @@ +when { + n == 1 -> { + res = "one" + } + n == 2 -> { + res = "two" + } + else -> { + res = "???" + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/before.kt.template b/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/before.kt.template new file mode 100644 index 00000000000..949cf0ab13d --- /dev/null +++ b/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/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/EliminateWhenSubjectIntention/description.html b/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/description.html new file mode 100644 index 00000000000..88a7f9b48da --- /dev/null +++ b/idea/resources/intentionDescriptions/EliminateWhenSubjectIntention/description.html @@ -0,0 +1,5 @@ + + +This intention eliminates argument of 'when' expression transforming its pattern-matching conditions into boolean expressions + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/FlattenWhenIntention/after.kt.template b/idea/resources/intentionDescriptions/FlattenWhenIntention/after.kt.template new file mode 100644 index 00000000000..42ac1855796 --- /dev/null +++ b/idea/resources/intentionDescriptions/FlattenWhenIntention/after.kt.template @@ -0,0 +1,17 @@ +when (n) { + 1 -> { + res = "one" + } + 2 -> { + res = "two" + } + 3 -> { + res = "three" + } + 4 -> { + res = "four" + } + else -> { + res = "???" + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/FlattenWhenIntention/before.kt.template b/idea/resources/intentionDescriptions/FlattenWhenIntention/before.kt.template new file mode 100644 index 00000000000..3bbce3cf227 --- /dev/null +++ b/idea/resources/intentionDescriptions/FlattenWhenIntention/before.kt.template @@ -0,0 +1,19 @@ +when (n) { + 1 -> { + res = "one" + } + 2 -> { + res = "two" + } + else -> when (n) { + 3 -> { + res = "thress" + } + 4 -> { + res = "four" + } + else -> { + res = "???" + } + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/FlattenWhenIntention/description.html b/idea/resources/intentionDescriptions/FlattenWhenIntention/description.html new file mode 100644 index 00000000000..a82e6bccc82 --- /dev/null +++ b/idea/resources/intentionDescriptions/FlattenWhenIntention/description.html @@ -0,0 +1,5 @@ + + +This intention merges branches of nested 'when' expression into enclosing one + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/after.kt.template b/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/after.kt.template new file mode 100644 index 00000000000..949cf0ab13d --- /dev/null +++ b/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/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/IntroduceWhenSubjectIntention/before.kt.template b/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/before.kt.template new file mode 100644 index 00000000000..a3b53a936d0 --- /dev/null +++ b/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/before.kt.template @@ -0,0 +1,11 @@ +when { + n == 1 -> { + res = "one" + } + n == 2 -> { + res = "two" + } + else -> { + res = "???" + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/description.html b/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/description.html new file mode 100644 index 00000000000..09ef51ee060 --- /dev/null +++ b/idea/resources/intentionDescriptions/IntroduceWhenSubjectIntention/description.html @@ -0,0 +1,5 @@ + + +This intention introduces argument into 'when' expression simplifying conditions of its branches + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/GuardedExpression.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/GuardedExpression.java new file mode 100644 index 00000000000..73a94d72bc5 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/GuardedExpression.java @@ -0,0 +1,27 @@ +package org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetExpression; + +public class GuardedExpression { + @NotNull + private final JetExpression condition; + + @NotNull + private final JetExpression baseExpression; + + public GuardedExpression(@NotNull JetExpression condition, @NotNull JetExpression baseExpression) { + this.condition = condition; + this.baseExpression = baseExpression; + } + + @NotNull + public JetExpression getCondition() { + return condition; + } + + @NotNull + public JetExpression getBaseExpression() { + return baseExpression; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/MultiGuardedExpression.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/MultiGuardedExpression.java new file mode 100644 index 00000000000..7e7c312cbfd --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/MultiGuardedExpression.java @@ -0,0 +1,29 @@ +package org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetExpression; + +import java.util.List; + +public class MultiGuardedExpression { + @NotNull + private final List conditions; + + @NotNull + private final JetExpression baseExpression; + + public MultiGuardedExpression(@NotNull List conditions, @NotNull JetExpression baseExpression) { + this.conditions = conditions; + this.baseExpression = baseExpression; + } + + @NotNull + public List getConditions() { + return conditions; + } + + @NotNull + public JetExpression getBaseExpression() { + return baseExpression; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/WhenUtils.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/WhenUtils.java new file mode 100644 index 00000000000..5429eeef779 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/WhenUtils.java @@ -0,0 +1,320 @@ +package org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lexer.JetTokens; + +import java.util.List; + +public class WhenUtils { + private WhenUtils() { + } + + public static final String TRANSFORM_WITHOUT_CHECK = + "Expression must be checked before applying transformation"; + + private static JetExpression getWhenConditionSubjectCandidate(JetExpression condition) { + if (condition instanceof JetIsExpression) { + return ((JetIsExpression) condition).getLeftHandSide(); + } + + if (condition instanceof JetBinaryExpression) { + JetBinaryExpression binaryExpression = (JetBinaryExpression) condition; + IElementType op = binaryExpression.getOperationToken(); + if (op == JetTokens.EQEQ || op == JetTokens.IN_KEYWORD || op == JetTokens.NOT_IN) { + return ((JetBinaryExpression) condition).getLeft(); + } + } + + return null; + } + + private static JetExpression getWhenSubjectCandidate(@NotNull JetWhenExpression whenExpression) { + if (whenExpression.getSubjectExpression() != null) return null; + + JetExpression lastCandidate = null; + for (JetWhenEntry entry : whenExpression.getEntries()) { + JetWhenCondition[] conditions = entry.getConditions(); + + if (!entry.isElse() && conditions.length == 0) return null; + + for (JetWhenCondition condition : conditions) { + if (!(condition instanceof JetWhenConditionWithExpression)) return null; + + JetExpression currCandidate = getWhenConditionSubjectCandidate(((JetWhenConditionWithExpression) condition).getExpression()); + + if (!(currCandidate instanceof JetSimpleNameExpression)) return null; + + if (lastCandidate == null) { + lastCandidate = currCandidate; + } + else if (!JetPsiMatcher.checkExpressionMatch(lastCandidate, currCandidate)) return null; + } + } + + return lastCandidate; + } + + public static boolean checkFlattenWhen(@NotNull JetWhenExpression whenExpression) { + JetExpression subject = whenExpression.getSubjectExpression(); + + if (subject != null && !(subject instanceof JetSimpleNameExpression)) return false; + + if (!JetPsiUtil.checkWhenExpressionHasSingleElse(whenExpression)) return false; + + JetExpression elseBranch = JetPsiUtil.getWhenElseBranch(whenExpression); + if (!(elseBranch instanceof JetWhenExpression)) return false; + + JetWhenExpression nestedWhenExpression = (JetWhenExpression) elseBranch; + + return JetPsiUtil.checkWhenExpressionHasSingleElse(nestedWhenExpression) && + JetPsiMatcher.checkExpressionMatch(subject, nestedWhenExpression.getSubjectExpression()); + } + + public static boolean checkIntroduceWhenSubject(@NotNull JetWhenExpression whenExpression) { + return getWhenSubjectCandidate(whenExpression) != null; + } + + public static boolean checkEliminateWhenSubject(@NotNull JetWhenExpression whenExpression) { + return whenExpression.getSubjectExpression() instanceof JetSimpleNameExpression; + } + + public static void flattenWhen(@NotNull JetWhenExpression whenExpression) { + JetExpression subjectExpression = whenExpression.getSubjectExpression(); + boolean hasSubject = subjectExpression != null; + + JetExpression elseBranch = JetPsiUtil.getWhenElseBranch(whenExpression); + assert elseBranch instanceof JetWhenExpression : TRANSFORM_WITHOUT_CHECK; + + JetWhenExpression nestedWhenExpression = (JetWhenExpression) elseBranch; + + List outerEntries = whenExpression.getEntries(); + List innerEntries = nestedWhenExpression.getEntries(); + + JetWhenExpression newWhenExpression = new JetPsiFactory.WhenTemplateBuilder(hasSubject) + .addBranchesWithSingleCondition(outerEntries.size() + innerEntries.size() - 2) + .toExpression(whenExpression.getProject()); + + if (hasSubject) { + JetExpression dummySubjectExpression = newWhenExpression.getSubjectExpression(); + assert dummySubjectExpression != null : TRANSFORM_WITHOUT_CHECK; + + dummySubjectExpression.replace(subjectExpression); + } + + List newEntries = newWhenExpression.getEntries(); + + int i = 0; + for (JetWhenEntry entry : outerEntries) { + if (!entry.isElse()) { + newEntries.get(i++).replace(entry); + } + } + for (JetWhenEntry entry : innerEntries) { + newEntries.get(i++).replace(entry); + } + + whenExpression.replace(newWhenExpression); + } + + private static JetWhenExpression createWhenTemplateWithSubject(@NotNull JetWhenExpression whenExpression) { + JetPsiFactory.WhenTemplateBuilder builder = new JetPsiFactory.WhenTemplateBuilder(true); + + for (JetWhenEntry entry : whenExpression.getEntries()) { + if (entry.isElse()) continue; + + for (JetWhenCondition condition : entry.getConditions()) { + assert condition instanceof JetWhenConditionWithExpression : TRANSFORM_WITHOUT_CHECK; + + JetExpression conditionExpression = ((JetWhenConditionWithExpression) condition).getExpression(); + + if (conditionExpression instanceof JetIsExpression) { + builder.addIsCondition(((JetIsExpression) conditionExpression).isNegated()); + } + else if (conditionExpression instanceof JetBinaryExpression) { + JetBinaryExpression binaryExpression = (JetBinaryExpression) conditionExpression; + + IElementType op = binaryExpression.getOperationToken(); + if (op == JetTokens.IN_KEYWORD) { + builder.addInCondition(false); + } + else if (op == JetTokens.NOT_IN) { + builder.addInCondition(true); + } + else if (op == JetTokens.EQEQ) { + builder.addExpressionCondition(); + } + else assert false : TRANSFORM_WITHOUT_CHECK; + } + else assert false : TRANSFORM_WITHOUT_CHECK; + } + + builder.finishBranch(); + } + + return builder.toExpression(whenExpression.getProject()); + } + + public static void introduceWhenSubject(@NotNull JetWhenExpression whenExpression) { + JetExpression subject = getWhenSubjectCandidate(whenExpression); + assert subject != null : TRANSFORM_WITHOUT_CHECK; + + JetWhenExpression newWhenExpression = createWhenTemplateWithSubject(whenExpression); + + JetExpression newSubject = newWhenExpression.getSubjectExpression(); + assert newSubject != null : TRANSFORM_WITHOUT_CHECK; + + newSubject.replace(subject); + + int i = 0; + List entries = whenExpression.getEntries(); + List newEntries = newWhenExpression.getEntries(); + for (JetWhenEntry newEntry : newEntries) { + JetWhenEntry entry = entries.get(i++); + + JetExpression branchExpression = entry.getExpression(); + assert branchExpression != null : TRANSFORM_WITHOUT_CHECK; + + JetExpression newBranchExpression = newEntry.getExpression(); + assert newBranchExpression != null : TRANSFORM_WITHOUT_CHECK; + + newBranchExpression.replace(branchExpression); + + int j = 0; + JetWhenCondition[] conditions = entry.getConditions(); + JetWhenCondition[] newConditions = newEntry.getConditions(); + + for (JetWhenCondition newCondition : newConditions) { + JetWhenCondition condition = conditions[j++]; + + assert condition instanceof JetWhenConditionWithExpression : TRANSFORM_WITHOUT_CHECK; + + JetExpression conditionExpression = ((JetWhenConditionWithExpression) condition).getExpression(); + + if (conditionExpression instanceof JetIsExpression) { + assert newCondition instanceof JetWhenConditionIsPattern : TRANSFORM_WITHOUT_CHECK; + + JetTypeReference typeReference = ((JetIsExpression) conditionExpression).getTypeRef(); + assert typeReference != null : TRANSFORM_WITHOUT_CHECK; + + JetTypeReference newTypeReference = ((JetWhenConditionIsPattern) newCondition).getTypeRef(); + assert newTypeReference != null : TRANSFORM_WITHOUT_CHECK; + + newTypeReference.replace(typeReference); + } + else if (conditionExpression instanceof JetBinaryExpression) { + JetBinaryExpression binaryExpression = (JetBinaryExpression) conditionExpression; + + JetExpression rhs = binaryExpression.getRight(); + assert rhs != null : TRANSFORM_WITHOUT_CHECK; + + IElementType op = binaryExpression.getOperationToken(); + if (op == JetTokens.IN_KEYWORD || op == JetTokens.NOT_IN) { + assert newCondition instanceof JetWhenConditionInRange : TRANSFORM_WITHOUT_CHECK; + + JetExpression newRangeExpression = ((JetWhenConditionInRange) newCondition).getRangeExpression(); + assert newRangeExpression != null : TRANSFORM_WITHOUT_CHECK; + + newRangeExpression.replace(rhs); + } + else if (op == JetTokens.EQEQ) { + assert newCondition instanceof JetWhenConditionWithExpression : TRANSFORM_WITHOUT_CHECK; + + JetExpression newConditionExpression = ((JetWhenConditionWithExpression) newCondition).getExpression(); + assert newConditionExpression != null : TRANSFORM_WITHOUT_CHECK; + + newConditionExpression.replace(rhs); + } + else assert false : TRANSFORM_WITHOUT_CHECK; + } + else assert false : TRANSFORM_WITHOUT_CHECK; + } + } + + whenExpression.replace(newWhenExpression); + } + + private static JetWhenExpression createWhenTemplateWithoutSubject(@NotNull JetWhenExpression whenExpression) { + JetPsiFactory.WhenTemplateBuilder builder = new JetPsiFactory.WhenTemplateBuilder(false); + + for (JetWhenEntry entry : whenExpression.getEntries()) { + if (!entry.isElse()) { + builder.addBranchWithMultiCondition(entry.getConditions().length); + } + } + + return builder.toExpression(whenExpression.getProject()); + } + + static JetExpression whenConditionToExpression(@NotNull JetWhenCondition condition, JetExpression subject) { + Project project = condition.getProject(); + + if (condition instanceof JetWhenConditionIsPattern) { + JetWhenConditionIsPattern patternCondition = (JetWhenConditionIsPattern) condition; + + JetTypeReference typeReference = patternCondition.getTypeRef(); + assert typeReference != null : TRANSFORM_WITHOUT_CHECK; + + assert subject != null : TRANSFORM_WITHOUT_CHECK; + + return JetPsiFactory.createIsExpression(project, subject, typeReference, patternCondition.isNegated()); + } + + if (condition instanceof JetWhenConditionInRange) { + JetWhenConditionInRange rangeCondition = (JetWhenConditionInRange) condition; + + JetExpression rangeExpression = rangeCondition.getRangeExpression(); + assert rangeExpression != null : TRANSFORM_WITHOUT_CHECK; + + assert subject != null : TRANSFORM_WITHOUT_CHECK; + + return JetPsiFactory.createBinaryExpression(project, subject, rangeCondition.getOperationReference().getText(), rangeExpression); + } + + assert condition instanceof JetWhenConditionWithExpression : TRANSFORM_WITHOUT_CHECK; + + JetExpression conditionExpression = ((JetWhenConditionWithExpression) condition).getExpression(); + assert conditionExpression != null : TRANSFORM_WITHOUT_CHECK; + + return subject != null ? JetPsiFactory.createBinaryExpression(project, subject, "==", conditionExpression) : conditionExpression; + } + + public static void eliminateWhenSubject(@NotNull JetWhenExpression whenExpression) { + JetExpression subject = whenExpression.getSubjectExpression(); + assert subject != null : TRANSFORM_WITHOUT_CHECK; + + JetWhenExpression newWhenExpression = createWhenTemplateWithoutSubject(whenExpression); + + int i = 0; + List entries = whenExpression.getEntries(); + List newEntries = newWhenExpression.getEntries(); + for (JetWhenEntry newEntry : newEntries) { + JetWhenEntry entry = entries.get(i++); + + JetExpression branchExpression = entry.getExpression(); + assert branchExpression != null : TRANSFORM_WITHOUT_CHECK; + + JetExpression newBranchExpression = newEntry.getExpression(); + assert newBranchExpression != null : TRANSFORM_WITHOUT_CHECK; + + newBranchExpression.replace(branchExpression); + + int j = 0; + JetWhenCondition[] conditions = entry.getConditions(); + JetWhenCondition[] newConditions = newEntry.getConditions(); + + for (JetWhenCondition newCondition : newConditions) { + JetWhenCondition condition = conditions[j++]; + + JetExpression newConditionExpression = ((JetWhenConditionWithExpression) newCondition).getExpression(); + assert newConditionExpression != null : TRANSFORM_WITHOUT_CHECK; + + newConditionExpression.replace(whenConditionToExpression(condition, subject)); + } + } + + whenExpression.replace(newWhenExpression); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/EliminateWhenSubjectIntention.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/EliminateWhenSubjectIntention.java new file mode 100644 index 00000000000..3ca7101fcf1 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/EliminateWhenSubjectIntention.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.JetWhenExpression; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.AbstractCodeTransformationIntention; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.WhenUtils; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.core.Transformer; + +public class EliminateWhenSubjectIntention extends AbstractCodeTransformationIntention { + private static final Transformer TRANSFORMER = new Transformer() { + @NotNull + @Override + public String getKey() { + return "eliminate.when.subject"; + } + + @Override + public void transform(@NotNull PsiElement element) { + WhenUtils.eliminateWhenSubject((JetWhenExpression) element); + } + }; + + private static final Predicate IS_APPLICABLE = new Predicate() { + @Override + public boolean apply(@Nullable PsiElement input) { + return input instanceof JetWhenExpression && WhenUtils.checkEliminateWhenSubject((JetWhenExpression) input); + } + }; + + public EliminateWhenSubjectIntention() { + super(TRANSFORMER, IS_APPLICABLE); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/FlattenWhenIntention.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/FlattenWhenIntention.java new file mode 100644 index 00000000000..c7df460ee97 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/FlattenWhenIntention.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.JetWhenExpression; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.AbstractCodeTransformationIntention; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.WhenUtils; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.core.Transformer; + +public class FlattenWhenIntention extends AbstractCodeTransformationIntention { + private static final Transformer TRANSFORMER = new Transformer() { + @NotNull + @Override + public String getKey() { + return "flatten.when"; + } + + @Override + public void transform(@NotNull PsiElement element) { + WhenUtils.flattenWhen((JetWhenExpression) element); + } + }; + + private static final Predicate IS_APPLICABLE = new Predicate() { + @Override + public boolean apply(@Nullable PsiElement input) { + return input instanceof JetWhenExpression && WhenUtils.checkFlattenWhen((JetWhenExpression) input); + } + }; + + public FlattenWhenIntention() { + super(TRANSFORMER, IS_APPLICABLE); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IntroduceWhenSubjectIntention.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IntroduceWhenSubjectIntention.java new file mode 100644 index 00000000000..b82dc453ed0 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/codeTransformations/branchedTransformations/intentions/IntroduceWhenSubjectIntention.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.JetWhenExpression; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.AbstractCodeTransformationIntention; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.WhenUtils; +import org.jetbrains.jet.plugin.codeInsight.codeTransformations.branchedTransformations.core.Transformer; + +public class IntroduceWhenSubjectIntention extends AbstractCodeTransformationIntention { + private static final Transformer TRANSFORMER = new Transformer() { + @NotNull + @Override + public String getKey() { + return "introduce.when.subject"; + } + + @Override + public void transform(@NotNull PsiElement element) { + WhenUtils.introduceWhenSubject((JetWhenExpression) element); + } + }; + + private static final Predicate IS_APPLICABLE = new Predicate() { + @Override + public boolean apply(@Nullable PsiElement input) { + return input instanceof JetWhenExpression && WhenUtils.checkIntroduceWhenSubject((JetWhenExpression) input); + } + }; + + public IntroduceWhenSubjectIntention() { + super(TRANSFORMER, IS_APPLICABLE); + } +}