diff --git a/comparing_references_inspection/comparing_references_inspection.iml b/comparing_references_inspection/comparing_references_inspection.iml new file mode 100644 index 000000000..3b8cfcf82 --- /dev/null +++ b/comparing_references_inspection/comparing_references_inspection.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/comparing_references_inspection/source/META-INF/plugin.xml b/comparing_references_inspection/source/META-INF/plugin.xml new file mode 100644 index 000000000..36bf3d61f --- /dev/null +++ b/comparing_references_inspection/source/META-INF/plugin.xml @@ -0,0 +1,14 @@ + + Comparing References Inspection + Inspection for (probably) inappropriate use of equality relation operation. + 1.0 + JetBrains + + + + + + + diff --git a/comparing_references_inspection/source/com/intellij/codeInspection/ComparingReferencesInspection.java b/comparing_references_inspection/source/com/intellij/codeInspection/ComparingReferencesInspection.java new file mode 100644 index 000000000..941fc702c --- /dev/null +++ b/comparing_references_inspection/source/com/intellij/codeInspection/ComparingReferencesInspection.java @@ -0,0 +1,152 @@ +package com.intellij.codeInspection; + +import com.intellij.codeInsight.daemon.GroupNames; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.ui.DocumentAdapter; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.StringTokenizer; + +/** + * @author max + */ +public class ComparingReferencesInspection extends BaseJavaLocalInspectionTool { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection"); + + private final LocalQuickFix myQuickFix = new MyQuickFix(); + + @SuppressWarnings({"WeakerAccess"}) @NonNls public String CHECKED_CLASSES = "java.lang.String;java.util.Date"; + @NonNls private static final String DESCRIPTION_TEMPLATE = InspectionsBundle.message("inspection.comparing.references.problem.descriptor"); + + @NotNull + public String getDisplayName() { + + return "'==' or '!=' instead of 'equals()'"; + } + + @NotNull + public String getGroupDisplayName() { + return GroupNames.BUGS_GROUP_NAME; + } + + @NotNull + public String getShortName() { + return "ComparingReferences"; + } + + private boolean isCheckedType(PsiType type) { + if (!(type instanceof PsiClassType)) return false; + + StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";"); + while (tokenizer.hasMoreTokens()) { + String className = tokenizer.nextToken(); + if (type.equalsToText(className)) return true; + } + + return false; + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + + @Override + public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) { + } + + + @Override public void visitBinaryExpression(PsiBinaryExpression expression) { + super.visitBinaryExpression(expression); + IElementType opSign = expression.getOperationTokenType(); + if (opSign == JavaTokenType.EQEQ || opSign == JavaTokenType.NE) { + PsiExpression lOperand = expression.getLOperand(); + PsiExpression rOperand = expression.getROperand(); + if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) return; + + PsiType lType = lOperand.getType(); + PsiType rType = rOperand.getType(); + + if (isCheckedType(lType) || isCheckedType(rType)) { + holder.registerProblem(expression, + DESCRIPTION_TEMPLATE, myQuickFix); + } + } + } + }; + } + + private static boolean isNullLiteral(PsiExpression expr) { + return expr instanceof PsiLiteralExpression && "null".equals(expr.getText()); + } + + private static class MyQuickFix implements LocalQuickFix { + @NotNull + public String getName() { + // The test (see the TestThisPlugin class) uses this string to identify the quick fix action. + return InspectionsBundle.message("inspection.comparing.references.use.quickfix"); + } + + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + try { + PsiBinaryExpression binaryExpression = (PsiBinaryExpression)descriptor.getPsiElement(); + IElementType opSign = binaryExpression.getOperationTokenType(); + PsiExpression lExpr = binaryExpression.getLOperand(); + PsiExpression rExpr = binaryExpression.getROperand(); + if (rExpr == null) + return; + + PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + PsiMethodCallExpression equalsCall = (PsiMethodCallExpression)factory.createExpressionFromText("a.equals(b)", null); + + equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr); + equalsCall.getArgumentList().getExpressions()[0].replace(rExpr); + + PsiExpression result = (PsiExpression)binaryExpression.replace(equalsCall); + + if (opSign == JavaTokenType.NE) { + PsiPrefixExpression negation = (PsiPrefixExpression)factory.createExpressionFromText("!a", null); + negation.getOperand().replace(result); + result.replace(negation); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + @NotNull + public String getFamilyName() { + return getName(); + } + } + + public JComponent createOptionsPanel() { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + final JTextField checkedClasses = new JTextField(CHECKED_CLASSES); + checkedClasses.getDocument().addDocumentListener(new DocumentAdapter() { + public void textChanged(DocumentEvent event) { + CHECKED_CLASSES = checkedClasses.getText(); + } + }); + + panel.add(checkedClasses); + return panel; + } + + public boolean isEnabledByDefault() { + return true; + } +} diff --git a/comparing_references_inspection/source/com/intellij/codeInspection/ComparingReferencesProvider.java b/comparing_references_inspection/source/com/intellij/codeInspection/ComparingReferencesProvider.java new file mode 100644 index 000000000..37f091454 --- /dev/null +++ b/comparing_references_inspection/source/com/intellij/codeInspection/ComparingReferencesProvider.java @@ -0,0 +1,10 @@ +package com.intellij.codeInspection; + +/** + * @author max + */ +public class ComparingReferencesProvider implements InspectionToolProvider { + public Class[] getInspectionClasses() { + return new Class[] { ComparingReferencesInspection.class}; + } +} diff --git a/comparing_references_inspection/source/inspectionDescriptions/ComparingReferences.html b/comparing_references_inspection/source/inspectionDescriptions/ComparingReferences.html new file mode 100644 index 000000000..ca5f9d5ee --- /dev/null +++ b/comparing_references_inspection/source/inspectionDescriptions/ComparingReferences.html @@ -0,0 +1,7 @@ + + +This inspection reports when the '==' or '!=' operator was used between expressions of +reference types.
+In the text field below, specify the semicolon separated list of classes to be considered as suspicious. + + diff --git a/comparing_references_inspection/testData/before.after.java b/comparing_references_inspection/testData/before.after.java new file mode 100644 index 000000000..a3058effc --- /dev/null +++ b/comparing_references_inspection/testData/before.after.java @@ -0,0 +1,6 @@ +public class X { + public boolean compare2Strings(java.lang.String s1, java.lang.String s2) { + return (s1.equals(s2)); + } + +} \ No newline at end of file diff --git a/comparing_references_inspection/testData/before.java b/comparing_references_inspection/testData/before.java new file mode 100644 index 000000000..23095bcfa --- /dev/null +++ b/comparing_references_inspection/testData/before.java @@ -0,0 +1,6 @@ +public class X { + public boolean compare2Strings(java.lang.String s1, java.lang.String s2) { + return (s1 == s2); + } + +} \ No newline at end of file diff --git a/comparing_references_inspection/testData/before1.after.java b/comparing_references_inspection/testData/before1.after.java new file mode 100644 index 000000000..acedf90e0 --- /dev/null +++ b/comparing_references_inspection/testData/before1.after.java @@ -0,0 +1,6 @@ +public class X { +public boolean compare2Dates(java.util.Date dt1, java.util.Date dt2){ + return (!dt1.equals(dt2)); + } + +} \ No newline at end of file diff --git a/comparing_references_inspection/testData/before1.java b/comparing_references_inspection/testData/before1.java new file mode 100644 index 000000000..325c4ebf5 --- /dev/null +++ b/comparing_references_inspection/testData/before1.java @@ -0,0 +1,6 @@ +public class X { +public boolean compare2Dates(java.util.Date dt1, java.util.Date dt2){ + return (dt1 != dt2); + } + +} \ No newline at end of file diff --git a/comparing_references_inspection/testSource/testPlugin/TestThisPlugin.java b/comparing_references_inspection/testSource/testPlugin/TestThisPlugin.java new file mode 100644 index 000000000..bf3769c2a --- /dev/null +++ b/comparing_references_inspection/testSource/testPlugin/TestThisPlugin.java @@ -0,0 +1,67 @@ +package testPlugin; + + +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.ComparingReferencesInspection; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; +import com.intellij.testFramework.fixtures.*; +import junit.framework.Assert; + +import java.util.List; + +/** + * @see JavaCodeInsightFixtureTestCase + * @see LightCodeInsightFixtureTestCase + */ +public class TestThisPlugin extends UsefulTestCase { + + protected CodeInsightTestFixture myFixture; + // Specify path to your test data directory + // e.g. final String dataPath = "c:\\users\\john.doe\\idea\\community\\samples\\ComparingReferences/testData"; + final String dataPath = "c:\\users\\John.Doe\\idea\\community\\samples\\comparingReferences/testData"; + + + public void setUp() throws Exception { + + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + final TestFixtureBuilder testFixtureBuilder = fixtureFactory.createFixtureBuilder(getName()); + myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(testFixtureBuilder.getFixture()); + myFixture.setTestDataPath(dataPath); + final JavaModuleFixtureBuilder builder = testFixtureBuilder.addModule(JavaModuleFixtureBuilder.class); + + builder.addContentRoot(myFixture.getTempDirPath()).addSourceRoot(""); + builder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15); + myFixture.setUp(); + } + + public void tearDown() throws Exception { + myFixture.tearDown(); + myFixture = null; + } + + protected void doTest(String testName, String hint) throws Throwable { + myFixture.configureByFile(testName + ".java"); + myFixture.enableInspections(ComparingReferencesInspection.class); + List highlightInfos = myFixture.doHighlighting(); + Assert.assertTrue(!highlightInfos.isEmpty()); + + final IntentionAction action = myFixture.findSingleIntention(hint); + + Assert.assertNotNull(action); + myFixture.launchAction(action); + myFixture.checkResultByFile(testName + ".after.java"); + } + + // Test the "==" case + public void test() throws Throwable { + doTest("before", "Use equals()"); + } + + // Test the "!=" case + public void test1() throws Throwable { + doTest("before1", "Use equals()"); + } + +} diff --git a/conditional_operator_intention/META-INF/plugin.xml b/conditional_operator_intention/META-INF/plugin.xml new file mode 100644 index 000000000..17c55b92a --- /dev/null +++ b/conditional_operator_intention/META-INF/plugin.xml @@ -0,0 +1,27 @@ + + Conditional Operator Converter + ConditionalOperatorConverter + Intention action that suggests to convert a conditional operator into + 'if' block. + + 1.3 + JetBrains + + + + + com.intellij.codeInsight.intention.ConditionalOperatorConvertor + Conditional Operator + ConditionalOperatorConvertor + + + + + + + com.intellij.codeInsight.intention.ConditionalOperatorConvertor + + + diff --git a/conditional_operator_intention/conditional_operator_intention.iml b/conditional_operator_intention/conditional_operator_intention.iml new file mode 100644 index 000000000..3b8cfcf82 --- /dev/null +++ b/conditional_operator_intention/conditional_operator_intention.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/conditional_operator_intention/source/META-INF/plugin.xml b/conditional_operator_intention/source/META-INF/plugin.xml new file mode 100644 index 000000000..68ae2627b --- /dev/null +++ b/conditional_operator_intention/source/META-INF/plugin.xml @@ -0,0 +1,35 @@ + + com.your.company.unique.plugin.id + Plugin display name here + 1.0 + YourCompany + + + most HTML tags may be used + ]]> + + + most HTML tags may be used + ]]> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/conditional_operator_intention/source/com/intellij/codeInsight/intention/ConditionalOperatorConvertor.java b/conditional_operator_intention/source/com/intellij/codeInsight/intention/ConditionalOperatorConvertor.java new file mode 100644 index 000000000..ffee0ad54 --- /dev/null +++ b/conditional_operator_intention/source/com/intellij/codeInsight/intention/ConditionalOperatorConvertor.java @@ -0,0 +1,110 @@ +package com.intellij.codeInsight.intention; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author dsl + */ +@NonNls public class ConditionalOperatorConvertor extends PsiElementBaseIntentionAction implements IntentionAction { + + @NotNull + public String getText() { + return "Convert ternary operator to if statement"; + } + + @NotNull + public String getFamilyName() { + return getText(); + } + + public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable PsiElement element) { + if (element == null) return false; + if (!element.isWritable()) return false; + + if (element instanceof PsiJavaToken) { + final PsiJavaToken token = (PsiJavaToken)element; + if (token.getTokenType() != JavaTokenType.QUEST) return false; + if (token.getParent() instanceof PsiConditionalExpression) { + final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)token.getParent(); + if (conditionalExpression.getThenExpression() == null + || conditionalExpression.getElseExpression() == null) { + return false; + } + return true; + } + return false; + } + return false; + } + + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final int offset = editor.getCaretModel().getOffset(); + final PsiElement element = file.findElementAt(offset); + PsiConditionalExpression conditionalExpression = PsiTreeUtil.getParentOfType(element, + PsiConditionalExpression.class, false); + if (conditionalExpression == null) return; + if (conditionalExpression.getThenExpression() == null || conditionalExpression.getElseExpression() == null) return; + + final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + + PsiElement originalStatement = PsiTreeUtil.getParentOfType(conditionalExpression, PsiStatement.class, false); + while (originalStatement instanceof PsiForStatement) { + originalStatement = PsiTreeUtil.getParentOfType(originalStatement, PsiStatement.class, true); + } + if (originalStatement == null) return; + + // Maintain declrations + if (originalStatement instanceof PsiDeclarationStatement) { + final PsiDeclarationStatement declaration = (PsiDeclarationStatement)originalStatement; + final PsiElement[] declaredElements = declaration.getDeclaredElements(); + PsiLocalVariable variable = null; + for (PsiElement declaredElement : declaredElements) { + if (declaredElement instanceof PsiLocalVariable && PsiTreeUtil.isAncestor(declaredElement, conditionalExpression, true)) { + variable = (PsiLocalVariable)declaredElement; + break; + } + } + if (variable == null) return; + variable.normalizeDeclaration(); + final Object marker = new Object(); + PsiTreeUtil.mark(conditionalExpression, marker); + PsiExpressionStatement statement = + (PsiExpressionStatement)factory.createStatementFromText(variable.getName() + " = 0;", null); + statement = (PsiExpressionStatement)CodeStyleManager.getInstance(project).reformat(statement); + ((PsiAssignmentExpression)statement.getExpression()).getRExpression().replace(variable.getInitializer()); + variable.getInitializer().delete(); + final PsiElement variableParent = variable.getParent(); + originalStatement = variableParent.getParent().addAfter(statement, variableParent); + conditionalExpression = (PsiConditionalExpression)PsiTreeUtil.releaseMark(originalStatement, marker); + } + + // create then and else branches + final PsiElement[] originalElements = new PsiElement[]{originalStatement, conditionalExpression}; + final PsiExpression condition = (PsiExpression)conditionalExpression.getCondition().copy(); + final PsiElement[] thenElements = PsiTreeUtil.copyElements(originalElements); + final PsiElement[] elseElements = PsiTreeUtil.copyElements(originalElements); + thenElements[1].replace(conditionalExpression.getThenExpression()); + elseElements[1].replace(conditionalExpression.getElseExpression()); + + PsiIfStatement statement = (PsiIfStatement)factory.createStatementFromText("if (true) { a = b } else { c = d }", + null); + statement = (PsiIfStatement)CodeStyleManager.getInstance(project).reformat(statement); + statement.getCondition().replace(condition); + statement = (PsiIfStatement)originalStatement.replace(statement); + + ((PsiBlockStatement)statement.getThenBranch()).getCodeBlock().getStatements()[0].replace(thenElements[0]); + ((PsiBlockStatement)statement.getElseBranch()).getCodeBlock().getStatements()[0].replace(elseElements[0]); + } + + public boolean startInWriteAction() { + return true; + } +} diff --git a/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/after.java.template b/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/after.java.template new file mode 100644 index 000000000..9cf0cf2d2 --- /dev/null +++ b/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/after.java.template @@ -0,0 +1,11 @@ +public class X { + void f(boolean isMale) { + String title; + if (isMale) { + title = "Mr."; + } else { + title = "Ms."; + } + System.out.println("title = " + title); + } +} diff --git a/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/before.java.template b/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/before.java.template new file mode 100644 index 000000000..ef611ed1c --- /dev/null +++ b/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/before.java.template @@ -0,0 +1,6 @@ +public class X { + void f(boolean isMale) { + String title = isMale ? "Mr." : "Ms."; + System.out.println("title = " + title); + } +} \ No newline at end of file diff --git a/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/description.html b/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/description.html new file mode 100644 index 000000000..3d6efdd8d --- /dev/null +++ b/conditional_operator_intention/source/intentionDescriptions/ConditionalOperatorConvertor/description.html @@ -0,0 +1,5 @@ + + + This intention converts a ternary operator to a corresponding if statement.
+ + \ No newline at end of file diff --git a/conditional_operator_intention/testData/before.template.after.java b/conditional_operator_intention/testData/before.template.after.java new file mode 100644 index 000000000..6682628f7 --- /dev/null +++ b/conditional_operator_intention/testData/before.template.after.java @@ -0,0 +1,11 @@ +public class X { + void f(boolean isMale) { + String title; + if (isMale) { + title = "Mr."; + } else { + title = "Ms."; + } + System.out.println("title = " + title); + } +} \ No newline at end of file diff --git a/conditional_operator_intention/testData/before.template.java b/conditional_operator_intention/testData/before.template.java new file mode 100644 index 000000000..74107ea2d --- /dev/null +++ b/conditional_operator_intention/testData/before.template.java @@ -0,0 +1,6 @@ +public class X { + void f(boolean isMale) { + String title = isMale ? "Mr." : "Ms."; + System.out.println("title = " + title); + } +} \ No newline at end of file diff --git a/conditional_operator_intention/testSource/testPlugin/YourTest.java b/conditional_operator_intention/testSource/testPlugin/YourTest.java new file mode 100644 index 000000000..1b2114b71 --- /dev/null +++ b/conditional_operator_intention/testSource/testPlugin/YourTest.java @@ -0,0 +1,62 @@ +package testPlugin; + +import com.intellij.codeInsight.intention.IntentionAction; + +import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; +import com.intellij.testFramework.fixtures.*; +import junit.framework.Assert; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Created by IntelliJ IDEA. + * User: Alexey.Chursin + * Date: Sep 13, 2010 + * Time: 9:35:50 PM + * To change this template use File | Settings | File Templates. + */ + +public class YourTest { + protected CodeInsightTestFixture myFixture; + // Specify path to your test data + // e.g. final String dataPath = "c:\\users\\john.doe\\idea\\community\\samples\\conditionalOperatorConvertor/testData"; + final String dataPath = "c:\\users\\John.Doe\\idea\\community\\samples\\conditionalOperatorConvertor/testData"; + + @Before + + public void setUp() throws Exception { + + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + final TestFixtureBuilder testFixtureBuilder = fixtureFactory.createFixtureBuilder(); + myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(testFixtureBuilder.getFixture()); + myFixture.setTestDataPath(dataPath); + final JavaModuleFixtureBuilder builder = testFixtureBuilder.addModule(JavaModuleFixtureBuilder.class); + + builder.addContentRoot(myFixture.getTempDirPath()).addSourceRoot(""); + builder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15); + myFixture.setUp(); + + } + + @After + public void tearDown() throws Exception { + myFixture.tearDown(); + myFixture = null; + } + + protected void doTest(String testName, String hint) throws Throwable { + // Messages.showInfoMessage("Test started", "Info"); + myFixture.configureByFile(testName + ".java"); + final IntentionAction action = myFixture.findSingleIntention(hint); + Assert.assertNotNull(action); + myFixture.launchAction(action); + myFixture.checkResultByFile(testName + ".after.java"); + } + + @Test + public void test() throws Throwable { + doTest("before.template", "Convert ternary operator to if statement"); + } + +} diff --git a/MaxOpenedProjects/MaxOpenedProjects.iml b/max_opened_projects/max_opened_projects.iml similarity index 100% rename from MaxOpenedProjects/MaxOpenedProjects.iml rename to max_opened_projects/max_opened_projects.iml diff --git a/MaxOpenedProjects/resources/META-INF/plugin.xml b/max_opened_projects/resources/META-INF/plugin.xml similarity index 100% rename from MaxOpenedProjects/resources/META-INF/plugin.xml rename to max_opened_projects/resources/META-INF/plugin.xml diff --git a/MaxOpenedProjects/src/MyPackage/MaxProject.java b/max_opened_projects/src/MyPackage/MaxProject.java similarity index 100% rename from MaxOpenedProjects/src/MyPackage/MaxProject.java rename to max_opened_projects/src/MyPackage/MaxProject.java diff --git a/MaxOpenedProjects/src/MyPackage/MyCounter.java b/max_opened_projects/src/MyPackage/MyCounter.java similarity index 100% rename from MaxOpenedProjects/src/MyPackage/MyCounter.java rename to max_opened_projects/src/MyPackage/MyCounter.java