From f80895f84af6872689a0ed27f16c749ae4f5abcd Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 18 Sep 2012 17:44:41 +0200 Subject: [PATCH] IDEA-91742 (correct conversion of extension methods) --- .../src/com/intellij/psi/util/PsiUtil.java | 11 ++++-- .../ConvertInterfaceToClassIntention.java | 19 ++++++++--- .../com/siyeh/ipp/interfaceToClass/Basic.java | 19 +++++++++++ .../ipp/interfaceToClass/Basic_after.java | 19 +++++++++++ .../interfaceToClass/ExtensionMethods.java | 21 ++++++++++++ .../ExtensionMethods_after.java | 21 ++++++++++++ .../ConvertInterfaceToClass.java | 6 ---- .../ConvertInterfaceToClassImpl.java | 14 -------- .../ConvertInterfaceToClassTest.java | 34 +++++++++++++++++++ 9 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods_after.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClass.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassImpl.java create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassTest.java diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 49134d57af49..4c613bb09245 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -966,10 +966,15 @@ public final class PsiUtil extends PsiUtilCore { } public static boolean isExtensionMethod(@Nullable final PsiMethod method) { - if (method == null) return false; + return findExtensionMethodMarker(method) != null; + } + + @Nullable + public static PsiJavaToken findExtensionMethodMarker(@Nullable final PsiMethod method) { + if (method == null) return null; final PsiCodeBlock body = method.getBody(); - if (body == null) return false; + if (body == null) return null; final PsiElement previous = PsiTreeUtil.skipSiblingsBackward(body, PsiComment.class, PsiWhiteSpace.class); - return isJavaToken(previous, JavaTokenType.DEFAULT_KEYWORD); + return previous instanceof PsiJavaToken && isJavaToken(previous, JavaTokenType.DEFAULT_KEYWORD) ? (PsiJavaToken)previous : null; } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassIntention.java index f77ec847e48a..435c472bfc5f 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassIntention.java @@ -19,6 +19,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; @@ -31,16 +32,14 @@ import java.util.Collection; public class ConvertInterfaceToClassIntention extends Intention { - private static void changeInterfaceToClass(PsiClass anInterface) - throws IncorrectOperationException { + private static void changeInterfaceToClass(PsiClass anInterface) throws IncorrectOperationException { final PsiIdentifier nameIdentifier = anInterface.getNameIdentifier(); assert nameIdentifier != null; final PsiElement whiteSpace = nameIdentifier.getPrevSibling(); assert whiteSpace != null; final PsiElement interfaceToken = whiteSpace.getPrevSibling(); assert interfaceToken != null; - final PsiKeyword interfaceKeyword = - (PsiKeyword)interfaceToken.getOriginalElement(); + final PsiKeyword interfaceKeyword = (PsiKeyword)interfaceToken.getOriginalElement(); final Project project = anInterface.getProject(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); final PsiElementFactory factory = psiFacade.getElementFactory(); @@ -52,16 +51,25 @@ public class ConvertInterfaceToClassIntention extends Intention { return; } classModifierList.setModifierProperty(PsiModifier.ABSTRACT, true); + final PsiElement parent = anInterface.getParent(); if (parent instanceof PsiClass) { classModifierList.setModifierProperty(PsiModifier.STATIC, true); } + final PsiMethod[] methods = anInterface.getMethods(); for (final PsiMethod method : methods) { + final PsiJavaToken marker = PsiUtil.findExtensionMethodMarker(method); final PsiModifierList modifierList = method.getModifierList(); modifierList.setModifierProperty(PsiModifier.PUBLIC, true); - modifierList.setModifierProperty(PsiModifier.ABSTRACT, true); + if (marker != null) { + marker.delete(); + } + else { + modifierList.setModifierProperty(PsiModifier.ABSTRACT, true); + } } + final PsiField[] fields = anInterface.getFields(); for (final PsiField field : fields) { final PsiModifierList modifierList = field.getModifierList(); @@ -71,6 +79,7 @@ public class ConvertInterfaceToClassIntention extends Intention { modifierList.setModifierProperty(PsiModifier.FINAL, true); } } + final PsiClass[] innerClasses = anInterface.getInnerClasses(); for (PsiClass innerClass : innerClasses) { final PsiModifierList modifierList = innerClass.getModifierList(); diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic.java new file mode 100644 index 000000000000..eb3c46c8fc51 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic.java @@ -0,0 +1,19 @@ +/* + * Copyright 2000-2012 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. + */ + +public interface I { + void m(); +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic_after.java new file mode 100644 index 000000000000..a41677eee58b --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/Basic_after.java @@ -0,0 +1,19 @@ +/* + * Copyright 2000-2012 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. + */ + +public abstract class I { + public abstract void m(); +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods.java new file mode 100644 index 000000000000..394e20bbf423 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2012 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. + */ + +public interface I { + void m() default { + System.out.println("Hi there."); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods_after.java new file mode 100644 index 000000000000..d616bc7ead37 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfaceToClass/ExtensionMethods_after.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2012 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. + */ + +public abstract class I { + public void m() { + System.out.println("Hi there."); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClass.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClass.java deleted file mode 100644 index f3dbfd4f550c..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClass.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.siyeh.ipp.interfacetoclass; - -public interface ConvertInterfaceToClass -{ - -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassImpl.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassImpl.java deleted file mode 100644 index 7b23da8500eb..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassImpl.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.siyeh.ipp.interfacetoclass; - -import javax.swing.AbstractAction; -import java.awt.event.ActionEvent; - -// make me readonly -public class ConvertInterfaceToClassImpl extends AbstractAction - implements ConvertInterfaceToClass -{ - - public void actionPerformed(ActionEvent e) - { - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassTest.java new file mode 100644 index 000000000000..001553b72131 --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/interfacetoclass/ConvertInterfaceToClassTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2012 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 com.siyeh.ipp.interfacetoclass; + +import com.siyeh.IntentionPowerPackBundle; +import com.siyeh.ipp.IPPTestCase; + +public class ConvertInterfaceToClassTest extends IPPTestCase { + public void testBasic() { doTest(); } + public void testExtensionMethods() { doTest(); } + + @Override + protected String getRelativePath() { + return "interfaceToClass"; + } + + @Override + protected String getIntentionName() { + return IntentionPowerPackBundle.message("convert.interface.to.class.intention.name"); + } +}