From 5706a80f780da22880cd884204012c15cfcf51db Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Mon, 17 Oct 2016 14:34:02 +0300 Subject: [PATCH 1/7] Javafx: "Wrap field with JavaFx property" intention (IDEA-102430) --- plugins/javaFX/javaFX-CE/javaFX-CE.iml | 2 + .../JavaFxFieldToPropertyNoArtifactTest.java | 34 ++ .../javaFX/JavaFxFieldToPropertyTest.java | 89 ++++ plugins/javaFX/javaFX.iml | 1 + .../after.java.template | 11 + .../before.java.template | 8 + .../description.html | 5 + .../src/META-INF/common-javaFX-plugin.xml | 6 + .../JavaFxFieldToPropertyIntention.java | 422 ++++++++++++++++++ .../javaFX/fxml/JavaFxCommonNames.java | 13 + .../javaFX/fxml/JavaFxFileTypeFactory.java | 13 +- .../plugins/javaFX/fxml/JavaFxModuleUtil.java | 100 +++++ .../plugins/javaFX/fxml/JavaFxPsiUtil.java | 22 + .../BigDecimalFieldToProperty.java | 17 + .../BigDecimalFieldToProperty_after.java | 19 + .../BoxedFloatFieldToProperty.java | 49 ++ .../BoxedFloatFieldToProperty_after.java | 50 +++ .../fieldToProperty/IntFieldToProperty.java | 59 +++ .../IntFieldToProperty_after.java | 60 +++ .../fieldToProperty/ListFieldToProperty.java | 23 + .../ListFieldToProperty_after.java | 26 ++ .../fieldToProperty/LongFieldToProperty.java | 47 ++ .../LongFieldToProperty_after.java | 49 ++ .../StringFieldToProperty.java | 17 + .../StringFieldToProperty_after.java | 18 + .../intentions/fieldToProperty/sample.fxml | 6 + 26 files changed, 1164 insertions(+), 2 deletions(-) create mode 100644 plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java create mode 100644 plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java create mode 100644 plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/after.java.template create mode 100644 plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/before.java.template create mode 100644 plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/description.html create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/sample.fxml diff --git a/plugins/javaFX/javaFX-CE/javaFX-CE.iml b/plugins/javaFX/javaFX-CE/javaFX-CE.iml index 7eedf1ef09d1..c09b13097680 100644 --- a/plugins/javaFX/javaFX-CE/javaFX-CE.iml +++ b/plugins/javaFX/javaFX-CE/javaFX-CE.iml @@ -15,5 +15,7 @@ + + \ No newline at end of file diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java new file mode 100644 index 000000000000..f1fe6fb52ad3 --- /dev/null +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java @@ -0,0 +1,34 @@ +package org.jetbrains.plugins.javaFX; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase; + +/** + * Same as the base test, but detects the presence of JavaFX using imports and FXMLs, not artifacts + * @author Pavel.Dolgov + */ +public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTest { + public static final DefaultLightProjectDescriptor JAVA_FX_DESCRIPTOR_NO_ARTIFACT = new DefaultLightProjectDescriptor() { + @Override + public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + AbstractJavaFXTestCase.addJavaFxJarAsLibrary(module, model); + super.configureModule(module, model, contentEntry); + } + }; + + @Override + protected void setUpModule() { + super.setUpModule(); + } + + public void testLongFieldToProperty() throws Exception { + final IntentionAction intentionAction = getIntentionAction(false); + // no artifact, no fxml, no javafx.* imports: the intention shoudn't be available + assertNull(intentionAction); + } +} diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java new file mode 100644 index 000000000000..93474254c727 --- /dev/null +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java @@ -0,0 +1,89 @@ +package org.jetbrains.plugins.javaFX; + +import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase; +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler; +import com.intellij.openapi.application.PluginPathManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase; +import org.jetbrains.plugins.javaFX.packaging.JavaFxApplicationArtifactType; + +import java.util.List; + +/** + * @author Pavel.Dolgov + * Run this test with 'main_idea_tests' classpath + */ +public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { + private static final String actionName = "Convert to JavaFX property"; + + @Override + protected void setUpModule() { + super.setUpModule(); + AbstractJavaFXTestCase.addJavaFxJarAsLibrary(getModule()); + ArtifactManager.getInstance(getProject()).addArtifact("fake-javafx", JavaFxApplicationArtifactType.getInstance(), null); + } + + public void testIntFieldToProperty() throws Exception { + doTest(); + } + + public void testBoxedFloatFieldToProperty() throws Exception { + doTest(); + } + + public void testStringFieldToProperty() throws Exception { + doTest(); + } + + public void testListFieldToProperty() throws Exception { + doTest(); + } + + public void testBigDecimalFieldToProperty() throws Exception { + doTest(true); + } + + public void testLongFieldToProperty() throws Exception { + doTest(); + } + + private void doTest() throws Exception { + doTest(false); + } + + private void doTest(boolean withFxml) throws Exception { + final IntentionAction intentionAction = getIntentionAction(withFxml); + assertNotNull(intentionAction); + + Editor editor = getEditor(); + PsiFile file = getFile(); + + assertTrue(ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, intentionAction,actionName)); + checkResultByFile(getTestName(false) + "_after.java"); + } + + protected IntentionAction getIntentionAction(boolean withFxml) throws Exception { + if (withFxml) { + configureByFiles(null, getTestName(false) + ".java", "sample.fxml"); + } + else { + configureByFiles(null, getTestName(false) + ".java"); + } + final List infos = doHighlighting(); + final Editor editor = getEditor(); + final PsiFile file = getFile(); + return findIntentionAction(infos, actionName, editor, file); + } + + @NotNull + @Override + protected String getTestDataPath() { + return PluginPathManager.getPluginHomePath("javaFX") + "/testData/intentions/fieldToProperty/"; + } + +} diff --git a/plugins/javaFX/javaFX.iml b/plugins/javaFX/javaFX.iml index 0936ae44f5a0..ad4ea7ec5e0e 100644 --- a/plugins/javaFX/javaFX.iml +++ b/plugins/javaFX/javaFX.iml @@ -27,5 +27,6 @@ + \ No newline at end of file diff --git a/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/after.java.template b/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/after.java.template new file mode 100644 index 000000000000..1eff96d39f39 --- /dev/null +++ b/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/after.java.template @@ -0,0 +1,11 @@ +ObservableIntegerValue number; + +int getNumber() { + return number.get(); +} +void setNumber(int newNumber) { + number.set(newNumber); +} +ObservableIntegerValue number() { + return number; +} \ No newline at end of file diff --git a/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/before.java.template b/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/before.java.template new file mode 100644 index 000000000000..557a869c7fb4 --- /dev/null +++ b/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/before.java.template @@ -0,0 +1,8 @@ +int number; + +int getNumber() { + return number; +} +void setNumber(int newNumber) { + number = newNumber; +} \ No newline at end of file diff --git a/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/description.html b/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/description.html new file mode 100644 index 000000000000..1e2bf5ba849c --- /dev/null +++ b/plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a field into JavaFX property. + + \ No newline at end of file diff --git a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml index 59b5666860cc..65515493b6db 100644 --- a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml +++ b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml @@ -44,6 +44,11 @@ org.jetbrains.plugins.javaFX.fxml.codeInsight.intentions.JavaFxExpandAttributeIntention JavaFX + + org.jetbrains.plugins.javaFX.codeInsight.JavaFxFieldToPropertyIntention + JavaFX + + @@ -65,6 +70,7 @@ + diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java new file mode 100644 index 000000000000..fe3517d69d4c --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java @@ -0,0 +1,422 @@ +package org.jetbrains.plugins.javaFX.codeInsight; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.diagnostic.Logger; +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.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.impl.PsiDiamondTypeUtil; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.*; +import com.intellij.refactoring.typeMigration.*; +import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.Query; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; +import org.jetbrains.plugins.javaFX.fxml.JavaFxModuleUtil; +import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Pavel.Dolgov + */ +public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionAction implements LowPriorityAction { + private static final Logger LOG = Logger.getInstance(JavaFxFieldToPropertyIntention.class); + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Convert to JavaFX property"; + } + + @NotNull + @Override + public String getText() { + //noinspection DialogTitleCapitalization + return "Convert to JavaFX property"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + final PsiField field = getField(element); + if (field != null) { + final PsiFile file = field.getContainingFile(); + if (JavaFxModuleUtil.isInJavaFxProject(file) || JavaFxPsiUtil.isJavaFxPackageImported(file)) { + return PropertyInfo.createPropertyInfo(field, project) != null; + } + } + return false; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + final PsiField field = getField(element); + LOG.assertTrue(field != null, "field"); + final PropertyInfo property = PropertyInfo.createPropertyInfo(field, project); + LOG.assertTrue(property != null, "propertyInfo"); + + final Query refs = ReferencesSearch.search(field); + + final Set elements = new HashSet<>(); + elements.add(element); + for (PsiReference reference : refs) { + elements.add(reference.getElement()); + } + if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; + field.normalizeDeclaration(); + + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiType fromType = field.getType(); + final PsiType toType = elementFactory.createTypeFromText(property.myObservableType.myText, field); + try { + final TypeMigrationRules rules = new TypeMigrationRules(); + rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile())); + final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType); + final TypeMigrationUsageInfo[] migratedUsages = labeler.getMigratedUsages(false, field); + + for (PsiReference reference : refs) { + final PsiElement refElement = reference.getElement(); + if (refElement instanceof PsiExpression) { + final TypeConversionDescriptor conversion = + property.myObservableType.findDirectConversion(refElement, toType, fromType, labeler); + if (conversion != null) { + TypeMigrationReplacementUtil.replaceExpression((PsiExpression)refElement, project, conversion, new TypeEvaluator(null, null)); + } + } + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + property.convertField(); + } + + @Nullable + private static PsiField getField(@NotNull PsiElement element) { + if (!(element instanceof PsiIdentifier)) return null; + final PsiField field = PsiTreeUtil.getParentOfType(element, PsiField.class); + if (field == null) return null; + if (field.getLanguage() != JavaLanguage.INSTANCE) return null; + if (field.getTypeElement() == null) return null; + if (field.hasModifierProperty(PsiModifier.STATIC) || field.hasModifierProperty(PsiModifier.FINAL)) return null; + return field; + } + + private static class PropertyInfo { + final PsiField myField; + final PsiClass myContainingClass; + final PsiTypeElement myTypeElement; + final String myFieldName; + final ObservableType myObservableType; + + private PropertyInfo(@NotNull PsiField field, + @NotNull PsiClass containingClass, + @NotNull PsiTypeElement typeElement, + @NotNull String fieldName, + @NotNull ObservableType observableType) { + myField = field; + myContainingClass = containingClass; + myTypeElement = typeElement; + myFieldName = fieldName; + myObservableType = observableType; + } + + static PropertyInfo createPropertyInfo(@NotNull PsiField field, @NotNull Project project) { + final String fieldName = field.getName(); + final PsiClass containingClass = field.getContainingClass(); + final PsiTypeElement typeElement = field.getTypeElement(); + if (fieldName != null && containingClass != null && typeElement != null) { + final ObservableType observableType = ObservableType.createObservableType(field, project); + if (observableType != null) { + return new PropertyInfo(field, containingClass, typeElement, fieldName, observableType); + } + } + return null; + } + + private void convertField() { + final Project project = myContainingClass.getProject(); + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + + final PsiTypeElement newTypeElement = elementFactory.createTypeElementFromText(myObservableType.myText, myField); + myTypeElement.replace(newTypeElement); + + final PsiExpression initializer = myField.getInitializer(); + final String propertyName = JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(myFieldName, VariableKind.FIELD); + final String initializerArgs = "this,\"" + propertyName + "\"" + (initializer == null ? "" : "," + initializer.getText()); + + String initializerText = "new " + myObservableType.myText + "(" + initializerArgs + ")"; + final PsiNewExpression newInitializer = (PsiNewExpression)elementFactory.createExpressionFromText(initializerText, myField); + myField.setInitializer(newInitializer); + + final PsiType fieldType = myField.getType(); + if (PsiDiamondTypeUtil.canCollapseToDiamond(newInitializer, newInitializer, fieldType)) { + final PsiJavaCodeReferenceElement classReference = newInitializer.getClassOrAnonymousClassReference(); + if (classReference != null) { + PsiDiamondTypeUtil.replaceExplicitWithDiamond(classReference.getParameterList()); + } + } + myField.setInitializer(newInitializer); + + final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); + codeStyleManager.reformat(javaCodeStyleManager.shortenClassReferences(myField)); + } + } + + static class ObservableType { + final String myText; + + ObservableType(@NotNull String text) { + this.myText = text; + } + + @Nullable + static ObservableType createObservableType(@NotNull PsiField field, @NotNull Project project) { + final PsiType type = field.getType(); + if (type instanceof PsiPrimitiveType) { + final String text = JavaFxCommonNames.ourObservablePrimitiveWrappers.get(type); + return text != null ? new ObservablePrimitive(text, (PsiPrimitiveType)type) : null; + } + final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type); + if (unboxedType != null) { + final String text = JavaFxCommonNames.ourObservablePrimitiveWrappers.get(unboxedType); + return text != null ? new ObservablePrimitive(text, unboxedType) : null; + } + if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { + return new ObservableString(project); + } + if (type instanceof PsiClassType) { + if (InheritanceUtil.isInheritor(type, JavaFxCommonNames.JAVAFX_BEANS_OBSERVABLE)) { + return null; // already observable + } + if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_LIST)) { + return ObservableList.createObservableList(type, project); + } + else if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_COLLECTION) || + InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) { + return null; // TODO: support SimpleSetProperty, SimpleMapProperty + } + else { + return new ObservableObject(type, project); + } + } + return null; + } + + TypeConversionDescriptor findDirectConversion(@NotNull PsiElement context, + @NotNull PsiType to, + @NotNull PsiType from, + @NotNull TypeMigrationLabeler labeler) { + final PsiClass toTypeClass = PsiUtil.resolveClassInType(to); + LOG.assertTrue(toTypeClass != null); + + final PsiElement parent = context.getParent(); + final PsiElement statementOrLambda = RefactoringUtil.getParentStatement(context, false); + if (parent instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression expression = (PsiAssignmentExpression)parent; + final IElementType tokenType = expression.getOperationTokenType(); + if (tokenType == JavaTokenType.EQ) { + return findSimpleAssignmentConversion(expression); + } + final String sign = expression.getOperationSign().getText(); + final String binarySign = sign.substring(0, sign.length() - 1); + return findCompoundAssignmentConversion(from, expression, sign, binarySign); + } + else if (parent instanceof PsiPostfixExpression) { + final PsiPostfixExpression expression = (PsiPostfixExpression)parent; + final TypeConversionDescriptor conversion = getUpdateConversion(expression, expression.getOperationSign(), true); + if (conversion != null) return conversion; + } + else if (parent instanceof PsiPrefixExpression) { + final PsiPrefixExpression expression = (PsiPrefixExpression)parent; + final TypeConversionDescriptor conversion = getUpdateConversion(expression, expression.getOperationSign(), false); + if (conversion != null) return conversion; + } + else if (context instanceof PsiReferenceExpression) { + final PsiExpression qualifierExpression = ((PsiReferenceExpression)context).getQualifierExpression(); + final PsiExpression expression = context.getParent() instanceof PsiMethodCallExpression && qualifierExpression != null + ? qualifierExpression + : (PsiExpression)context; + return getReadConversion(expression); + } + + return null; + } + + @Nullable + TypeConversionDescriptor findSimpleAssignmentConversion(PsiAssignmentExpression expression) { + return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)", expression); + } + + @Nullable + TypeConversionDescriptor findCompoundAssignmentConversion(@NotNull PsiType from, + @NotNull PsiExpression expression, + @NotNull String sign, + @NotNull String binarySign) { + return null; + } + + @Nullable + TypeConversionDescriptor getUpdateConversion(@NotNull PsiExpression expression, @NotNull PsiJavaToken operationToken, boolean postfix) { + return null; + } + + + @NotNull + TypeConversionDescriptor getReadConversion(PsiExpression expression) { + return new TypeConversionDescriptor("$qualifier$", "$qualifier$.get()", expression); + } + } + + static class ObservablePrimitive extends ObservableType { + final PsiPrimitiveType myType; + + ObservablePrimitive(@NotNull String text, @NotNull PsiPrimitiveType type) { + super(text); + myType = type; + } + + @Nullable + @Override + TypeConversionDescriptor findCompoundAssignmentConversion(@NotNull PsiType from, + @NotNull PsiExpression expression, + @NotNull String sign, + @NotNull String binarySign) { + final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(from); + final String valueType = (unboxedType != null ? unboxedType : from).getCanonicalText(); + return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$", + "$qualifier$.set((" + valueType + ")($qualifier$.get() " + binarySign + " ($val$)))", + expression); + } + + @Nullable + @Override + TypeConversionDescriptor getUpdateConversion(@NotNull PsiExpression expression, @NotNull PsiJavaToken operationToken, boolean postfix) { + final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(expression); + if (parent instanceof PsiExpressionStatement) { + final IElementType tokenType = operationToken.getTokenType(); + if (tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS) { + final String sign = operationToken.getText(); + return new TypeConversionDescriptor(postfix ? ("$qualifier$" + sign) : (sign + "$qualifier$"), + "$qualifier$.set($qualifier$.get()" + sign.charAt(0) + "1)", expression); + } + } + return null; + } + } + + static class ObservableString extends ObservableType { + ObservableString(@NotNull Project project) { + super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_STRING_PROPERTY); + } + + @Nullable + @Override + TypeConversionDescriptor findCompoundAssignmentConversion(@NotNull PsiType from, + @NotNull PsiExpression expression, + @NotNull String sign, + @NotNull String binarySign) { + return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$", + "$qualifier$.set($qualifier$.get() " + binarySign + " ($val$))", + expression); + } + } + + static class ObservableList extends ObservableType { + final PsiType myOriginalType; + final PsiType myItemType; + + ObservableList(@NotNull PsiType originalType, @NotNull PsiType itemType) { + super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_LIST_PROPERTY + "<" + itemType.getCanonicalText() + ">"); + myOriginalType = originalType; + myItemType = itemType; + } + + @Nullable + private static ObservableType createObservableList(@NotNull PsiType type, @NotNull Project project) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(type); + final PsiClass fieldClass = resolveResult.getElement(); + if (fieldClass != null) { + final PsiClass listClass = JavaPsiFacade.getInstance(project) + .findClass(CommonClassNames.JAVA_UTIL_LIST, GlobalSearchScope.allScope(project)); + if (listClass != null) { + final PsiSubstitutor substitutor = + TypeConversionUtil.getClassSubstitutor(listClass, fieldClass, resolveResult.getSubstitutor()); + if (substitutor != null) { + final PsiType itemType = substitutor.substitute(listClass.getTypeParameters()[0]); + if (itemType != null) { + return new ObservableList(type, itemType); + } + } + } + } + return null; + } + + @Nullable + @Override + TypeConversionDescriptor findSimpleAssignmentConversion(PsiAssignmentExpression expression) { + return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.setAll($val$)", expression); + } + + @NotNull + @Override + TypeConversionDescriptor getReadConversion(PsiExpression expression) { + return new TypeConversionDescriptor("$qualifier$", "$qualifier$.get()", expression) { + @Override + public PsiExpression replace(PsiExpression expression, TypeEvaluator evaluator) { + final PsiExpression replaced = super.replace(expression, evaluator); + // Replace the getter's return type: List -> ObservableList + final PsiElement parent = replaced.getParent(); + if (parent instanceof PsiReturnStatement) { + final PsiReturnStatement returnStatement = (PsiReturnStatement)parent; + final PsiElement statementParent = returnStatement.getParent(); + if (statementParent instanceof PsiCodeBlock) { + final PsiCodeBlock codeBlock = (PsiCodeBlock)statementParent; + final PsiElement blockParent = codeBlock.getParent(); + if (blockParent instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)blockParent; + final PsiTypeElement returnTypeElement = method.getReturnTypeElement(); + if (returnTypeElement != null && myOriginalType.equals(method.getReturnType())) { + final Project project = expression.getProject(); + final String text = JavaFxCommonNames.JAVAFX_COLLECTIONS_OBSERVABLE_LIST + "<" + myItemType.getCanonicalText() + ">"; + final PsiTypeElement newReturnTypeElement = JavaPsiFacade.getInstance(project) + .getElementFactory().createTypeElementFromText(text, method); + final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project); + javaCodeStyleManager.shortenClassReferences(returnTypeElement.replace(newReturnTypeElement)); + } + } + } + } + return replaced; + } + }; + } + } + + static class ObservableObject extends ObservableType { + final PsiType myType; + + ObservableObject(@NotNull PsiType type, @NotNull Project project) { + super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_OBJECT_PROPERTY + "<" + type.getCanonicalText() + ">"); + myType = type; + } + } +} diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxCommonNames.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxCommonNames.java index 0c7bb16b7a7b..7a88db9b091c 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxCommonNames.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxCommonNames.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.javaFX.fxml; +import com.intellij.psi.PsiPrimitiveType; import com.intellij.psi.PsiType; import org.jetbrains.annotations.NonNls; @@ -50,6 +51,9 @@ public class JavaFxCommonNames { @NonNls public static final String JAVAFX_BEANS_VALUE_WRITABLE_VALUE = "javafx.beans.value.WritableValue"; @NonNls public static final String JAVAFX_SCENE_LAYOUT_PANE = "javafx.scene.layout.Pane"; @NonNls public static final String JAVAFX_BEANS_NAMED_ARG = "javafx.beans.NamedArg"; + @NonNls public static final String JAVAFX_BEANS_PROPERTY_SIMPLE_STRING_PROPERTY = "javafx.beans.property.SimpleStringProperty"; + @NonNls public static final String JAVAFX_BEANS_PROPERTY_SIMPLE_LIST_PROPERTY = "javafx.beans.property.SimpleListProperty"; + @NonNls public static final String JAVAFX_BEANS_PROPERTY_SIMPLE_OBJECT_PROPERTY = "javafx.beans.property.SimpleObjectProperty"; @NonNls public static final String PROPERTY_METHOD_SUFFIX = "Property"; @@ -74,4 +78,13 @@ public class JavaFxCommonNames { @NonNls public static final String JAVA_FX_PARENT = "javafx.scene.Parent"; @NonNls public static final String JAVA_FX_SCENE = "javafx.scene.Scene"; @NonNls public static final String JAVAFX_APPLICATION_APPLICATION = "javafx.application.Application"; + + public static final Map ourObservablePrimitiveWrappers = new HashMap<>(); + static { + ourObservablePrimitiveWrappers.put(PsiPrimitiveType.INT, "javafx.beans.property.SimpleIntegerProperty"); + ourObservablePrimitiveWrappers.put(PsiPrimitiveType.LONG, "javafx.beans.property.SimpleLongProperty"); + ourObservablePrimitiveWrappers.put(PsiPrimitiveType.FLOAT, "javafx.beans.property.SimpleFloatProperty"); + ourObservablePrimitiveWrappers.put(PsiPrimitiveType.DOUBLE, "javafx.beans.property.SimpleDoubleProperty"); + ourObservablePrimitiveWrappers.put(PsiPrimitiveType.BOOLEAN, "javafx.beans.property.SimpleBooleanProperty"); + } } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java index 27e0093fb04b..804d338f2dd3 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java @@ -6,6 +6,7 @@ import com.intellij.openapi.fileTypes.FileTypeFactory; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; /** @@ -13,7 +14,8 @@ import org.jetbrains.annotations.NotNull; * Date: 1/8/13 */ public class JavaFxFileTypeFactory extends FileTypeFactory { - public static final String FXML_EXTENSION = "fxml"; + @NonNls public static final String FXML_EXTENSION = "fxml"; + @NonNls private static final String DOT_FXML_EXTENSION = "." + FXML_EXTENSION; public static boolean isFxml(@NotNull PsiFile file) { final VirtualFile virtualFile = file.getViewProvider().getVirtualFile(); @@ -23,11 +25,18 @@ public class JavaFxFileTypeFactory extends FileTypeFactory { public static boolean isFxml(@NotNull VirtualFile virtualFile) { if (FXML_EXTENSION.equals(virtualFile.getExtension())) { final FileType fileType = virtualFile.getFileType(); - if (fileType == FileTypeManager.getInstance().getFileTypeByExtension(FXML_EXTENSION) && !fileType.isBinary()) return true; + if (fileType == getFileType() && !fileType.isBinary()) { + return virtualFile.getName().endsWith(DOT_FXML_EXTENSION); + } } return false; } + @NotNull + public static FileType getFileType() { + return FileTypeManager.getInstance().getFileTypeByExtension(FXML_EXTENSION); + } + @Override public void createFileTypes(@NotNull FileTypeConsumer consumer) { final FileType fileType = consumer.getStandardFileTypeByName("XML"); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java new file mode 100644 index 000000000000..834136fc7c2d --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java @@ -0,0 +1,100 @@ +package org.jetbrains.plugins.javaFX.fxml; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.startup.StartupActivity; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.search.FileTypeIndex; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.javaFX.packaging.JavaFxApplicationArtifactType; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author Pavel.Dolgov + */ +public class JavaFxModuleUtil { + public static boolean isInJavaFxProject(@NotNull PsiFile file) { + final Project project = file.getProject(); + if (hasJavaFxArtifacts(project)) { + return true; + } + return isInJavaFxModule(file); + } + + private static boolean isInJavaFxModule(@NotNull PsiFile file) { + final VirtualFile virtualFile = file.getVirtualFile(); + if (virtualFile != null) { + final Project project = file.getProject(); + final Module fileModule = ModuleUtil.findModuleForFile(virtualFile, project); + if (fileModule != null) { + return getCachedJavaFxModules(project).contains(fileModule); + } + } + return false; + } + + @NotNull + private static Set getCachedJavaFxModules(@NotNull Project project) { + Set value = CachedValuesManager.getManager(project).getCachedValue( + project, () -> { + final Collection files = + FileTypeIndex.getFiles(JavaFxFileTypeFactory.getFileType(), GlobalSearchScope.projectScope(project)); + + final Set modules = files.stream() + .filter(JavaFxFileTypeFactory::isFxml) + .map(file -> ModuleUtil.findModuleForFile(file, project)) + .collect(Collectors.toCollection(THashSet::new)); + + return CachedValueProvider.Result.create(modules, ProjectRootManager.getInstance(project)); + }); + return value; + } + + @NotNull + private static boolean hasJavaFxArtifacts(@NotNull Project project) { + return CachedValuesManager.getManager(project).getCachedValue( + project, () -> { + final Collection artifacts = + ArtifactManager.getInstance(project).getArtifactsByType(JavaFxApplicationArtifactType.getInstance()); + return CachedValueProvider.Result.create(!artifacts.isEmpty(), ProjectRootManager.getInstance(project)); + }); + } + + /** + * Avoids freeze on first use of Java intentions + */ + public static class JavaFxDetectionStartupActivity implements StartupActivity { + @Override + public void runActivity(@NotNull Project project) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return; + } + StartupManager.getInstance(project).runWhenProjectIsInitialized( + () -> ApplicationManager.getApplication().executeOnPooledThread( + () -> DumbService.getInstance(project).runReadActionInSmartMode( + () -> populateCachedJavaFxModules(project)))); + } + + private void populateCachedJavaFxModules(@NotNull Project project) { + if (!project.isDisposed() && project.isOpen()) { + hasJavaFxArtifacts(project); + getCachedJavaFxModules(project); + } + } + } +} diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java index 7029408deca3..25cec6a5dbfd 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java @@ -1236,4 +1236,26 @@ public class JavaFxPsiUtil { } return true; } + + public static boolean isJavaFxPackageImported(@NotNull PsiFile file) { + if (!(file instanceof PsiJavaFile)) return false; + final PsiJavaFile javaFile = (PsiJavaFile)file; + + return CachedValuesManager.getCachedValue( + javaFile, () -> { + final PsiImportList importList = javaFile.getImportList(); + if (importList != null) { + for (PsiImportStatementBase statementBase : importList.getAllImportStatements()) { + final PsiJavaCodeReferenceElement importReference = statementBase.getImportReference(); + if (importReference != null) { + final String qualifiedName = importReference.getQualifiedName(); + if (qualifiedName != null && qualifiedName.startsWith("javafx.")) { + return CachedValueProvider.Result.create(true, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); + } + } + } + } + return CachedValueProvider.Result.create(false, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); + }); + } } diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty.java new file mode 100644 index 000000000000..0f141c3df988 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty.java @@ -0,0 +1,17 @@ +import java.math.BigDecimal; + +class BigDecimalDemo { + private BigDecimal number = new BigDecimal(42); + + BigDecimalDemo(BigDecimal number) { + this.number = number; + } + + public BigDecimal get() { + return number; + } + + public BigDecimal get(int i) { + return number; + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty_after.java new file mode 100644 index 000000000000..a7565645a1f3 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty_after.java @@ -0,0 +1,19 @@ +import javafx.beans.property.SimpleObjectProperty; + +import java.math.BigDecimal; + +class BigDecimalDemo { + private SimpleObjectProperty number = new SimpleObjectProperty<>(this, "number", new BigDecimal(42)); + + BigDecimalDemo(BigDecimal number) { + this.number.set(number); + } + + public BigDecimal get() { + return number.get(); + } + + public BigDecimal get(int i) { + return number.get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty.java new file mode 100644 index 000000000000..5bd7f6939e1a --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty.java @@ -0,0 +1,49 @@ +import javafx.scene.Node; + +class BoxedFloatDemo { + private Float f; + + BoxedFloatDemo(Float f) { + this.f = f; + } + + public Float getF() { + return f; + } + + public void setF(Float f) { + this.f = f; + } + + public void preInc() { + ++f; + } + + public void postDec() { + f--; + } + + public void twice() { + f *= 2; + } + + public void half() { + f /= 1; + } + + public Float plusOne() { + return f + 1; + } + + public F lambda() { + return () -> f; + } + + public String toString() { + return "f=" + f; + } + + interface F { + Float get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty_after.java new file mode 100644 index 000000000000..669e4154377e --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty_after.java @@ -0,0 +1,50 @@ +import javafx.beans.property.SimpleFloatProperty; +import javafx.scene.Node; + +class BoxedFloatDemo { + private SimpleFloatProperty f = new SimpleFloatProperty(this, "f"); + + BoxedFloatDemo(Float f) { + this.f.set(f); + } + + public Float getF() { + return f.get(); + } + + public void setF(Float f) { + this.f.set(f); + } + + public void preInc() { + f.set(f.get() + 1); + } + + public void postDec() { + f.set(f.get() - 1); + } + + public void twice() { + f.set((float) (f.get() * (2))); + } + + public void half() { + f.set((float) (f.get() / (1))); + } + + public Float plusOne() { + return f.get() + 1; + } + + public F lambda() { + return () -> f.get(); + } + + public String toString() { + return "f=" + f.get(); + } + + interface F { + Float get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty.java new file mode 100644 index 000000000000..e4a0e2e10ade --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty.java @@ -0,0 +1,59 @@ +import javafx.scene.Node; + +class IntDemo { + int n; + + IntDemo(int n) { + this.n = n; + } + + public int getN() { + return n; + } + + public void setN(int n) { + this.n = n; + } + + public void preInc() { + ++n; + } + + public void postDec() { + n--; + } + + public int preIncVal() { + return ++n; + } + + public void twice() { + n *= 2; + } + + public void half() { + n >>= 1; + } + + public int plusOne() { + return n + 1; + } + + public void forLoop(int a) { + for (n = 0; n < a; n++) { + System.out.println(n); + } + } + + public I lambda() { + return () -> n; + } + + public String toString() { + return "n=" + n; + } + + interface I { + int get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty_after.java new file mode 100644 index 000000000000..75c84d966390 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty_after.java @@ -0,0 +1,60 @@ +import javafx.beans.property.SimpleIntegerProperty; +import javafx.scene.Node; + +class IntDemo { + SimpleIntegerProperty n = new SimpleIntegerProperty(this, "n"); + + IntDemo(int n) { + this.n.set(n); + } + + public int getN() { + return n.get(); + } + + public void setN(int n) { + this.n.set(n); + } + + public void preInc() { + n.set(n.get() + 1); + } + + public void postDec() { + n.set(n.get() - 1); + } + + public int preIncVal() { + return ++n; + } + + public void twice() { + n.set((int) (n.get() * (2))); + } + + public void half() { + n.set((int) (n.get() >> (1))); + } + + public int plusOne() { + return n.get() + 1; + } + + public void forLoop(int a) { + for (n.set(0); n.get() < a; n.set(n.get() + 1)) { + System.out.println(n.get()); + } + } + + public I lambda() { + return () -> n.get(); + } + + public String toString() { + return "n=" + n.get(); + } + + interface I { + int get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty.java new file mode 100644 index 000000000000..ffeff871dfee --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty.java @@ -0,0 +1,23 @@ +import java.net.URL; +import java.util.List; +import javafx.scene.Node; + +class ListDemo { + private List urls; + + ListDemo(List urls) { + this.urls = urls; + } + + public List getUrls() { + return urls; + } + + public void setUrls(List urls) { + this.urls = urls; + } + + public String toString() { + return "urls=" + urls; + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty_after.java new file mode 100644 index 000000000000..a1a8d516bcfd --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty_after.java @@ -0,0 +1,26 @@ +import java.net.URL; +import java.util.List; + +import javafx.beans.property.SimpleListProperty; +import javafx.collections.ObservableList; +import javafx.scene.Node; + +class ListDemo { + private SimpleListProperty urls = new SimpleListProperty<>(this, "urls"); + + ListDemo(List urls) { + this.urls.setAll(urls); + } + + public ObservableList getUrls() { + return urls.get(); + } + + public void setUrls(List urls) { + this.urls.setAll(urls); + } + + public String toString() { + return "urls=" + urls.get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty.java new file mode 100644 index 000000000000..eebcae5b9359 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty.java @@ -0,0 +1,47 @@ +class LongDemo { + private long n; + + public long getN() { + return n; + } + + public void setN(long n) { + this.n = n; + } + + public void preInc() { + ++n; + } + + public long preIncVal() { + return ++n; + } + + public void postDec() { + n--; + } + + public void twice() { + n *= 2; + } + + public void half() { + n >>= 1; + } + + public long plusOne() { + return n + 1; + } + + public L lambda() { + return () -> n; + } + + public String toString() { + return "n=" + n; + } + + interface L { + long get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty_after.java new file mode 100644 index 000000000000..486bc7189cce --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty_after.java @@ -0,0 +1,49 @@ +import javafx.beans.property.SimpleLongProperty; + +class LongDemo { + private SimpleLongProperty n = new SimpleLongProperty(this, "n"); + + public long getN() { + return n.get(); + } + + public void setN(long n) { + this.n.set(n); + } + + public void preInc() { + n.set(n.get() + 1); + } + + public long preIncVal() { + return ++n; + } + + public void postDec() { + n.set(n.get() - 1); + } + + public void twice() { + n.set((long) (n.get() * (2))); + } + + public void half() { + n.set((long) (n.get() >> (1))); + } + + public long plusOne() { + return n.get() + 1; + } + + public L lambda() { + return () -> n.get(); + } + + public String toString() { + return "n=" + n.get(); + } + + interface L { + long get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty.java new file mode 100644 index 000000000000..577873121502 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty.java @@ -0,0 +1,17 @@ +import javafx.scene.Node; + +class StringDemo { + String s = "Something"; + + StringDemo(String s) { + this.s = s; + } + + public void compound(String t) { + s += t; + } + + public String toString() { + return "s=" + s; + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty_after.java new file mode 100644 index 000000000000..3f467633026c --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty_after.java @@ -0,0 +1,18 @@ +import javafx.beans.property.SimpleStringProperty; +import javafx.scene.Node; + +class StringDemo { + SimpleStringProperty s = new SimpleStringProperty(this, "s", "Something"); + + StringDemo(String s) { + this.s.set(s); + } + + public void compound(String t) { + s.set(s.get() + (t)); + } + + public String toString() { + return "s=" + s.get(); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/sample.fxml b/plugins/javaFX/testData/intentions/fieldToProperty/sample.fxml new file mode 100644 index 000000000000..083f45de1589 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/sample.fxml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 15564307841f792f2e38ab8da653399a3c153f66 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Wed, 26 Oct 2016 18:25:53 +0300 Subject: [PATCH 2/7] Javafx: "Wrap field with JavaFx property" intention - use ProgressIndicator and TransactionGuard when performing the refactoring (IDEA-102430) --- .../JavaFxFieldToPropertyNoArtifactTest.java | 4 +- .../javaFX/JavaFxFieldToPropertyTest.java | 51 ++++++--- .../JavaFxFieldToPropertyIntention.java | 106 +++++++++++++----- ...a => ArtifactPresenceFieldToProperty.java} | 0 ...rtifactPresenceFieldToProperty_after.java} | 0 ....java => FxmlPresenceFieldToProperty.java} | 0 ...=> FxmlPresenceFieldToProperty_after.java} | 0 .../TwoFilesFieldToProperty.java | 13 +++ .../TwoFilesFieldToPropertySecondFile.java | 8 ++ ...oFilesFieldToPropertySecondFile_after.java | 8 ++ .../TwoFilesFieldToProperty_after.java | 14 +++ 11 files changed, 154 insertions(+), 50 deletions(-) rename plugins/javaFX/testData/intentions/fieldToProperty/{LongFieldToProperty.java => ArtifactPresenceFieldToProperty.java} (100%) rename plugins/javaFX/testData/intentions/fieldToProperty/{LongFieldToProperty_after.java => ArtifactPresenceFieldToProperty_after.java} (100%) rename plugins/javaFX/testData/intentions/fieldToProperty/{BigDecimalFieldToProperty.java => FxmlPresenceFieldToProperty.java} (100%) rename plugins/javaFX/testData/intentions/fieldToProperty/{BigDecimalFieldToProperty_after.java => FxmlPresenceFieldToProperty_after.java} (100%) create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile_after.java create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty_after.java diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java index f1fe6fb52ad3..0c22992ea1ec 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java @@ -26,8 +26,8 @@ public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTe super.setUpModule(); } - public void testLongFieldToProperty() throws Exception { - final IntentionAction intentionAction = getIntentionAction(false); + public void testArtifactPresenceFieldToProperty() throws Exception { + final IntentionAction intentionAction = getIntentionAction(getTestName(false) + ".java"); // no artifact, no fxml, no javafx.* imports: the intention shoudn't be available assertNull(intentionAction); } diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java index 93474254c727..089380e0f975 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java @@ -6,9 +6,20 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.testFramework.IdeaTestUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.javaFX.codeInsight.JavaFxFieldToPropertyIntention; import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase; import org.jetbrains.plugins.javaFX.packaging.JavaFxApplicationArtifactType; @@ -16,10 +27,9 @@ import java.util.List; /** * @author Pavel.Dolgov - * Run this test with 'main_idea_tests' classpath */ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { - private static final String actionName = "Convert to JavaFX property"; + private static final String actionName = JavaFxFieldToPropertyIntention.FAMILY_NAME; @Override protected void setUpModule() { @@ -44,20 +54,33 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { doTest(); } - public void testBigDecimalFieldToProperty() throws Exception { - doTest(true); + public void testFxmlPresenceFieldToProperty() throws Exception { + doTest(getTestName(false) + ".java", "sample.fxml"); } - public void testLongFieldToProperty() throws Exception { + public void testArtifactPresenceFieldToProperty() throws Exception { doTest(); } - private void doTest() throws Exception { - doTest(false); + public void testTwoFilesFieldToProperty() throws Exception { + final String testName = getTestName(false); + final String secondFileName = testName + "SecondFile.java"; + doTest(testName + ".java", secondFileName); + + final String expected = StringUtil.convertLineSeparators(VfsUtilCore.loadText(getVirtualFile(testName + "SecondFile_after.java"))); + final PsiClass secondFileClass = JavaPsiFacade.getInstance(myProject).findClass("DoubleDemo2", GlobalSearchScope.allScope(myProject)); + final String actual = secondFileClass.getContainingFile().getText(); + assertEquals("Text mismatch[" + secondFileName + "]", expected, actual); } - private void doTest(boolean withFxml) throws Exception { - final IntentionAction intentionAction = getIntentionAction(withFxml); + private void doTest() throws Exception { + doTest(getTestName(false) + ".java"); + } + + private void doTest(String... fileNames) throws Exception { + LanguageLevelProjectExtension.getInstance(myProject).setLanguageLevel(LanguageLevel.JDK_1_7); + + final IntentionAction intentionAction = getIntentionAction(fileNames); assertNotNull(intentionAction); Editor editor = getEditor(); @@ -67,13 +90,8 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { checkResultByFile(getTestName(false) + "_after.java"); } - protected IntentionAction getIntentionAction(boolean withFxml) throws Exception { - if (withFxml) { - configureByFiles(null, getTestName(false) + ".java", "sample.fxml"); - } - else { - configureByFiles(null, getTestName(false) + ".java"); - } + protected IntentionAction getIntentionAction(String... fileNames) throws Exception { + configureByFiles(null, fileNames); final List infos = doHighlighting(); final Editor editor = getEditor(); final PsiFile file = getFile(); @@ -85,5 +103,4 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { protected String getTestDataPath() { return PluginPathManager.getPluginHomePath("javaFX") + "/testData/intentions/fieldToProperty/"; } - } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java index fe3517d69d4c..c494415c0c6b 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java @@ -4,9 +4,15 @@ import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.TransactionGuard; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -15,13 +21,17 @@ import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.typeMigration.*; -import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; +import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; +import gnu.trove.THashSet; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,7 +39,7 @@ import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxModuleUtil; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; -import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -37,19 +47,20 @@ import java.util.Set; */ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionAction implements LowPriorityAction { private static final Logger LOG = Logger.getInstance(JavaFxFieldToPropertyIntention.class); + public static final String FAMILY_NAME = "Convert to JavaFX property"; @Nls @NotNull @Override public String getFamilyName() { - return "Convert to JavaFX property"; + return FAMILY_NAME; } @NotNull @Override public String getText() { //noinspection DialogTitleCapitalization - return "Convert to JavaFX property"; + return FAMILY_NAME; } @Override @@ -70,41 +81,74 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio LOG.assertTrue(field != null, "field"); final PropertyInfo property = PropertyInfo.createPropertyInfo(field, project); LOG.assertTrue(property != null, "propertyInfo"); + new SearchUsagesTask(project, property).queue(); + } - final Query refs = ReferencesSearch.search(field); + private static class SearchUsagesTask extends Task.Modal { + private final PropertyInfo myProperty; + private List myReferences; + private Set myFiles; - final Set elements = new HashSet<>(); - elements.add(element); - for (PsiReference reference : refs) { - elements.add(reference.getElement()); + public SearchUsagesTask(@NotNull Project project, + @NotNull PropertyInfo property) { + super(project, "Searching for usages of '" + property.myFieldName + "'", true); + myProperty = property; } - if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; - field.normalizeDeclaration(); - final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); - final PsiType fromType = field.getType(); - final PsiType toType = elementFactory.createTypeFromText(property.myObservableType.myText, field); - try { - final TypeMigrationRules rules = new TypeMigrationRules(); - rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile())); - final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType); - final TypeMigrationUsageInfo[] migratedUsages = labeler.getMigratedUsages(false, field); + @Override + public void run(@NotNull ProgressIndicator indicator) { + ReadAction.run(() -> { + final Query refs = ReferencesSearch.search(myProperty.myField); + myReferences = ContainerUtil.mapNotNull(refs, ref -> ref); - for (PsiReference reference : refs) { - final PsiElement refElement = reference.getElement(); - if (refElement instanceof PsiExpression) { - final TypeConversionDescriptor conversion = - property.myObservableType.findDirectConversion(refElement, toType, fromType, labeler); - if (conversion != null) { - TypeMigrationReplacementUtil.replaceExpression((PsiExpression)refElement, project, conversion, new TypeEvaluator(null, null)); + final Set occurrences = new THashSet<>(); + occurrences.add(myProperty.myField); + occurrences.addAll(ContainerUtil.mapNotNull(myReferences, PsiReference::getElement)); + + myFiles = ContainerUtil.map2SetNotNull(occurrences, element -> { + final PsiFile file = element.getContainingFile(); + return file != null && file.isPhysical() ? file : null; + }); + + TransactionGuard.submitTransaction(myProject, () -> + WriteCommandAction + .runWriteCommandAction(myProject, "Convert '" + myProperty.myFieldName + "' to JavaFX property", null, + this::replaceUsages, myFiles.toArray(PsiFile.EMPTY_ARRAY))); + }); + } + + private void replaceUsages() { + final PsiField field = myProperty.myField; + if (!FileModificationService.getInstance().preparePsiElementsForWrite(myFiles)) return; + field.normalizeDeclaration(); + + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); + final PsiType fromType = field.getType(); + final PsiType toType = elementFactory.createTypeFromText(myProperty.myObservableType.myText, field); + try { + final TypeMigrationRules rules = new TypeMigrationRules(); + final Set virtualFiles = ContainerUtil.map2SetNotNull(myFiles, PsiFile::getVirtualFile); + rules.setBoundScope(GlobalSearchScope.filesScope(myProject, virtualFiles)); + final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType); + labeler.getMigratedUsages(false, field); + + for (PsiReference reference : myReferences) { + final PsiElement refElement = reference.getElement(); + if (refElement instanceof PsiExpression) { + final PsiExpression expression = (PsiExpression)refElement; + final TypeConversionDescriptor conversion = + myProperty.myObservableType.findDirectConversion(expression, toType, fromType, labeler); + if (conversion != null) { + TypeMigrationReplacementUtil.replaceExpression(expression, myProject, conversion, new TypeEvaluator(null, null)); + } } } + myProperty.convertField(); + } + catch (IncorrectOperationException e) { + LOG.error(e); } } - catch (IncorrectOperationException e) { - LOG.error(e); - } - property.convertField(); } @Nullable diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty.java similarity index 100% rename from plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty.java rename to plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty.java diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty_after.java similarity index 100% rename from plugins/javaFX/testData/intentions/fieldToProperty/LongFieldToProperty_after.java rename to plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty_after.java diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty.java similarity index 100% rename from plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty.java rename to plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty.java diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty_after.java similarity index 100% rename from plugins/javaFX/testData/intentions/fieldToProperty/BigDecimalFieldToProperty_after.java rename to plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty_after.java diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty.java new file mode 100644 index 000000000000..79e4550e24b1 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty.java @@ -0,0 +1,13 @@ +import javafx.scene.Node; + +class DoubleDemo { + double data = 1.234; + + DoubleDemo(double d) { + this.data = d; + } + + public String toString() { + return "data=" + data; + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile.java b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile.java new file mode 100644 index 000000000000..d3641da4e8f2 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile.java @@ -0,0 +1,8 @@ +import javafx.scene.Node; + +class DoubleDemo2 { + static void foo(DoubleDemo dd) { + double d = dd.data; + dd.data = d + 2; + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile_after.java new file mode 100644 index 000000000000..b7310043afa4 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile_after.java @@ -0,0 +1,8 @@ +import javafx.scene.Node; + +class DoubleDemo2 { + static void foo(DoubleDemo dd) { + double d = dd.data.get(); + dd.data.set(d + 2); + } +} \ No newline at end of file diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty_after.java new file mode 100644 index 000000000000..03f73c02a388 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty_after.java @@ -0,0 +1,14 @@ +import javafx.beans.property.SimpleDoubleProperty; +import javafx.scene.Node; + +class DoubleDemo { + SimpleDoubleProperty data = new SimpleDoubleProperty(this, "data", 1.234); + + DoubleDemo(double d) { + this.data.set(d); + } + + public String toString() { + return "data=" + data.get(); + } +} \ No newline at end of file From 270ab5e85fa0d46e6d7069cf36d2be33c2bdb052 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Thu, 27 Oct 2016 13:22:35 +0300 Subject: [PATCH 3/7] Javafx: "Wrap field with JavaFx property" intention - don't use TransactionGuard, because it delays the write action which may cause inconsistencies when performing refactoring (IDEA-102430) --- .../codeInsight/JavaFxFieldToPropertyIntention.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java index c494415c0c6b..76b7e5c5a714 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java @@ -1,11 +1,9 @@ package org.jetbrains.plugins.javaFX.codeInsight; -import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; @@ -109,17 +107,14 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio final PsiFile file = element.getContainingFile(); return file != null && file.isPhysical() ? file : null; }); - - TransactionGuard.submitTransaction(myProject, () -> - WriteCommandAction - .runWriteCommandAction(myProject, "Convert '" + myProperty.myFieldName + "' to JavaFX property", null, - this::replaceUsages, myFiles.toArray(PsiFile.EMPTY_ARRAY))); }); + WriteCommandAction + .runWriteCommandAction(myProject, "Convert '" + myProperty.myFieldName + "' to JavaFX property", null, + this::replaceOccurrences, myFiles.toArray(PsiFile.EMPTY_ARRAY)); } - private void replaceUsages() { + private void replaceOccurrences() { final PsiField field = myProperty.myField; - if (!FileModificationService.getInstance().preparePsiElementsForWrite(myFiles)) return; field.normalizeDeclaration(); final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); From a2b040e170f7ea704feaf22193fd57f31fb34e88 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Thu, 27 Oct 2016 14:43:53 +0300 Subject: [PATCH 4/7] Javafx: "Wrap field with JavaFx property" intention - use ArtifactManager's modification tracker for caching presence of JavaFX artifacts (IDEA-102430) --- .../JavaFxFieldToPropertyNoArtifactTest.java | 23 ++++--------------- .../javaFX/JavaFxFieldToPropertyTest.java | 8 ++++++- .../plugins/javaFX/fxml/JavaFxModuleUtil.java | 6 ++--- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java index 0c22992ea1ec..ef4633d396a9 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java @@ -1,34 +1,21 @@ package org.jetbrains.plugins.javaFX; import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.roots.ContentEntry; -import com.intellij.openapi.roots.ModifiableRootModel; -import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase; /** * Same as the base test, but detects the presence of JavaFX using imports and FXMLs, not artifacts * @author Pavel.Dolgov */ public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTest { - public static final DefaultLightProjectDescriptor JAVA_FX_DESCRIPTOR_NO_ARTIFACT = new DefaultLightProjectDescriptor() { - @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { - AbstractJavaFXTestCase.addJavaFxJarAsLibrary(module, model); - super.configureModule(module, model, contentEntry); - } - }; - - @Override - protected void setUpModule() { - super.setUpModule(); - } public void testArtifactPresenceFieldToProperty() throws Exception { final IntentionAction intentionAction = getIntentionAction(getTestName(false) + ".java"); // no artifact, no fxml, no javafx.* imports: the intention shoudn't be available assertNull(intentionAction); } + + @Override + protected boolean isArtifactNeeded() { + return false; + } } diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java index 089380e0f975..f236692eba8f 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java @@ -35,7 +35,9 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { protected void setUpModule() { super.setUpModule(); AbstractJavaFXTestCase.addJavaFxJarAsLibrary(getModule()); - ArtifactManager.getInstance(getProject()).addArtifact("fake-javafx", JavaFxApplicationArtifactType.getInstance(), null); + if (isArtifactNeeded()) { + ArtifactManager.getInstance(getProject()).addArtifact("fake-javafx", JavaFxApplicationArtifactType.getInstance(), null); + } } public void testIntFieldToProperty() throws Exception { @@ -98,6 +100,10 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { return findIntentionAction(infos, actionName, editor, file); } + protected boolean isArtifactNeeded() { + return true; + } + @NotNull @Override protected String getTestDataPath() { diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java index 834136fc7c2d..dc9d40b66b0a 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java @@ -69,9 +69,9 @@ public class JavaFxModuleUtil { private static boolean hasJavaFxArtifacts(@NotNull Project project) { return CachedValuesManager.getManager(project).getCachedValue( project, () -> { - final Collection artifacts = - ArtifactManager.getInstance(project).getArtifactsByType(JavaFxApplicationArtifactType.getInstance()); - return CachedValueProvider.Result.create(!artifacts.isEmpty(), ProjectRootManager.getInstance(project)); + final ArtifactManager artifactManager = ArtifactManager.getInstance(project); + final Collection artifacts = artifactManager.getArtifactsByType(JavaFxApplicationArtifactType.getInstance()); + return CachedValueProvider.Result.create(!artifacts.isEmpty(), artifactManager.getModificationTracker()); }); } From d136f9d79e3d4ea20b1bc3c459c60d13966af35a Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Thu, 27 Oct 2016 20:35:45 +0300 Subject: [PATCH 5/7] Javafx: "Wrap field with JavaFx property" intention - listen for addition and removal of FXML files (IDEA-102430) --- .../javaFX/fxml/JavaFxFileTypeFactory.java | 2 +- .../plugins/javaFX/fxml/JavaFxModuleUtil.java | 62 ++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java index 804d338f2dd3..9fabe685bb6b 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; */ public class JavaFxFileTypeFactory extends FileTypeFactory { @NonNls public static final String FXML_EXTENSION = "fxml"; - @NonNls private static final String DOT_FXML_EXTENSION = "." + FXML_EXTENSION; + @NonNls static final String DOT_FXML_EXTENSION = "." + FXML_EXTENSION; public static boolean isFxml(@NotNull PsiFile file) { final VirtualFile virtualFile = file.getViewProvider().getVirtualFile(); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java index dc9d40b66b0a..4bcdea9ef55e 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java @@ -5,10 +5,12 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.startup.StartupActivity; import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.ModificationTracker; +import com.intellij.openapi.util.SimpleModificationTracker; +import com.intellij.openapi.vfs.*; import com.intellij.packaging.artifacts.Artifact; import com.intellij.packaging.artifacts.ArtifactManager; import com.intellij.psi.PsiFile; @@ -60,7 +62,7 @@ public class JavaFxModuleUtil { .map(file -> ModuleUtil.findModuleForFile(file, project)) .collect(Collectors.toCollection(THashSet::new)); - return CachedValueProvider.Result.create(modules, ProjectRootManager.getInstance(project)); + return CachedValueProvider.Result.create(modules, FxmlPresenceListener.getModificationTracker(project)); }); return value; } @@ -81,6 +83,8 @@ public class JavaFxModuleUtil { public static class JavaFxDetectionStartupActivity implements StartupActivity { @Override public void runActivity(@NotNull Project project) { + VirtualFileManager.getInstance().addVirtualFileListener(new FxmlPresenceListener(project), project); + if (ApplicationManager.getApplication().isUnitTestMode()) { return; } @@ -97,4 +101,56 @@ public class JavaFxModuleUtil { } } } + + private static class FxmlPresenceListener extends VirtualFileAdapter { + private static final Key KEY = Key.create("fxml.presence.modification.tracker"); + private final SimpleModificationTracker myModificationTracker; + + public FxmlPresenceListener(@NotNull Project project) { + myModificationTracker = new SimpleModificationTracker(); + project.putUserData(KEY, myModificationTracker); + } + + private static ModificationTracker getModificationTracker(@NotNull Project project) { + return project.getUserData(KEY); + } + + @Override + public void fileCreated(@NotNull VirtualFileEvent event) { + checkEvent(event); + } + + @Override + public void fileDeleted(@NotNull VirtualFileEvent event) { + checkEvent(event); + } + + @Override + public void fileMoved(@NotNull VirtualFileMoveEvent event) { + checkEvent(event); + } + + @Override + public void fileCopied(@NotNull VirtualFileCopyEvent event) { + checkEvent(event); + } + + @Override + public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { + if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) { + final String oldName = (String)event.getOldValue(); + final String newName = (String)event.getNewValue(); + if (oldName != null && newName != null && + oldName.endsWith(JavaFxFileTypeFactory.DOT_FXML_EXTENSION) != newName.endsWith(JavaFxFileTypeFactory.DOT_FXML_EXTENSION)) { + myModificationTracker.incModificationCount(); + } + } + } + + private void checkEvent(@NotNull VirtualFileEvent event) { + if (JavaFxFileTypeFactory.isFxml(event.getFile())) { + myModificationTracker.incModificationCount(); + } + } + } } From 74e42ee761ba644428bbd6b94dd0b66a7d46607f Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Fri, 28 Oct 2016 14:59:42 +0300 Subject: [PATCH 6/7] Javafx: "Wrap field with JavaFx property" intention - added test for adding/deleting FXML files (IDEA-102430) --- .../JavaFxFieldToPropertyNoArtifactTest.java | 42 ++++++++++++++++++- .../javaFX/JavaFxFieldToPropertyTest.java | 6 +-- .../fieldToProperty/AddRemoveFxmlFile.java | 3 ++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 plugins/javaFX/testData/intentions/fieldToProperty/AddRemoveFxmlFile.java diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java index ef4633d396a9..a23abc13b326 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java @@ -1,6 +1,12 @@ package org.jetbrains.plugins.javaFX; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.IOException; /** * Same as the base test, but detects the presence of JavaFX using imports and FXMLs, not artifacts @@ -9,11 +15,45 @@ import com.intellij.codeInsight.intention.IntentionAction; public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTest { public void testArtifactPresenceFieldToProperty() throws Exception { - final IntentionAction intentionAction = getIntentionAction(getTestName(false) + ".java"); + configureByFiles(null, getTestName(false) + ".java"); + final IntentionAction intentionAction = getIntentionAction(); // no artifact, no fxml, no javafx.* imports: the intention shoudn't be available assertNull(intentionAction); } + public void testAddRemoveFxmlFile() throws Exception { + final VirtualFile sourceRootDir = configureByFiles(null, getTestName(false) + ".java"); + IntentionAction intentionAction = getIntentionAction(); + assertNull(intentionAction); + + final Ref fileRef = new Ref<>(); + ApplicationManager.getApplication().runWriteAction(() -> { + try { + VirtualFile file = sourceRootDir.createChildData(JavaFxFieldToPropertyNoArtifactTest.this, "sample.fxml"); + VfsUtil.saveText(file, "\n"); + fileRef.set(file); + } + catch (IOException e) { + fail(e.toString()); + } + }); + + intentionAction = getIntentionAction(); + assertNotNull("when created", intentionAction); + + ApplicationManager.getApplication().runWriteAction(() -> { + try { + fileRef.get().delete(JavaFxFieldToPropertyNoArtifactTest.this); + } + catch (IOException e) { + fail(e.toString()); + } + }); + + intentionAction = getIntentionAction(); + assertNull("when deleted", intentionAction); + } + @Override protected boolean isArtifactNeeded() { return false; diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java index f236692eba8f..4136e70b1767 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java @@ -82,7 +82,8 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { private void doTest(String... fileNames) throws Exception { LanguageLevelProjectExtension.getInstance(myProject).setLanguageLevel(LanguageLevel.JDK_1_7); - final IntentionAction intentionAction = getIntentionAction(fileNames); + configureByFiles(null, fileNames); + final IntentionAction intentionAction = getIntentionAction(); assertNotNull(intentionAction); Editor editor = getEditor(); @@ -92,8 +93,7 @@ public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { checkResultByFile(getTestName(false) + "_after.java"); } - protected IntentionAction getIntentionAction(String... fileNames) throws Exception { - configureByFiles(null, fileNames); + protected IntentionAction getIntentionAction() throws Exception { final List infos = doHighlighting(); final Editor editor = getEditor(); final PsiFile file = getFile(); diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/AddRemoveFxmlFile.java b/plugins/javaFX/testData/intentions/fieldToProperty/AddRemoveFxmlFile.java new file mode 100644 index 000000000000..0c8060c9bed3 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/AddRemoveFxmlFile.java @@ -0,0 +1,3 @@ +class FloatDemo { + float f; +} \ No newline at end of file From 136f4400b2655a8df5a9322aede21310797e94e4 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Mon, 31 Oct 2016 17:18:27 +0300 Subject: [PATCH 7/7] Javafx: "Wrap field with JavaFx property" intention - code cleanup (IDEA-102430) --- .../JavaFxFieldToPropertyNoArtifactTest.java | 6 ++--- .../javaFX/JavaFxFieldToPropertyTest.java | 3 --- .../JavaFxFieldToPropertyIntention.java | 24 ++++++++----------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java index a23abc13b326..dff5e22f402b 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java @@ -17,7 +17,7 @@ public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTe public void testArtifactPresenceFieldToProperty() throws Exception { configureByFiles(null, getTestName(false) + ".java"); final IntentionAction intentionAction = getIntentionAction(); - // no artifact, no fxml, no javafx.* imports: the intention shoudn't be available + // no artifact, no fxml, no javafx.* imports: the intention shouldn't be available assertNull(intentionAction); } @@ -29,7 +29,7 @@ public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTe final Ref fileRef = new Ref<>(); ApplicationManager.getApplication().runWriteAction(() -> { try { - VirtualFile file = sourceRootDir.createChildData(JavaFxFieldToPropertyNoArtifactTest.this, "sample.fxml"); + VirtualFile file = sourceRootDir.createChildData(this, "sample.fxml"); VfsUtil.saveText(file, "\n"); fileRef.set(file); } @@ -43,7 +43,7 @@ public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTe ApplicationManager.getApplication().runWriteAction(() -> { try { - fileRef.get().delete(JavaFxFieldToPropertyNoArtifactTest.this); + fileRef.get().delete(this); } catch (IOException e) { fail(e.toString()); diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java index 4136e70b1767..01b3d90df00b 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java @@ -6,9 +6,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.LanguageLevelProjectExtension; -import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.packaging.artifacts.ArtifactManager; @@ -17,7 +15,6 @@ import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.testFramework.IdeaTestUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.javaFX.codeInsight.JavaFxFieldToPropertyIntention; import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase; diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java index 76b7e5c5a714..797c9c14efa0 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java @@ -24,9 +24,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.typeMigration.*; -import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.Query; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; import gnu.trove.THashSet; @@ -37,7 +35,7 @@ import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxModuleUtil; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; -import java.util.List; +import java.util.Collection; import java.util.Set; /** @@ -84,7 +82,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio private static class SearchUsagesTask extends Task.Modal { private final PropertyInfo myProperty; - private List myReferences; + private Collection myReferences; private Set myFiles; public SearchUsagesTask(@NotNull Project project, @@ -96,8 +94,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio @Override public void run(@NotNull ProgressIndicator indicator) { ReadAction.run(() -> { - final Query refs = ReferencesSearch.search(myProperty.myField); - myReferences = ContainerUtil.mapNotNull(refs, ref -> ref); + myReferences = ReferencesSearch.search(myProperty.myField).findAll(); final Set occurrences = new THashSet<>(); occurrences.add(myProperty.myField); @@ -114,6 +111,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio } private void replaceOccurrences() { + LOG.assertTrue(myProject != null, "myProject"); final PsiField field = myProperty.myField; field.normalizeDeclaration(); @@ -132,7 +130,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio if (refElement instanceof PsiExpression) { final PsiExpression expression = (PsiExpression)refElement; final TypeConversionDescriptor conversion = - myProperty.myObservableType.findDirectConversion(expression, toType, fromType, labeler); + myProperty.myObservableType.findDirectConversion(expression, toType, fromType); if (conversion != null) { TypeMigrationReplacementUtil.replaceExpression(expression, myProject, conversion, new TypeEvaluator(null, null)); } @@ -239,7 +237,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio return text != null ? new ObservablePrimitive(text, unboxedType) : null; } if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { - return new ObservableString(project); + return new ObservableString(); } if (type instanceof PsiClassType) { if (InheritanceUtil.isInheritor(type, JavaFxCommonNames.JAVAFX_BEANS_OBSERVABLE)) { @@ -253,7 +251,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio return null; // TODO: support SimpleSetProperty, SimpleMapProperty } else { - return new ObservableObject(type, project); + return new ObservableObject(type); } } return null; @@ -261,13 +259,11 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio TypeConversionDescriptor findDirectConversion(@NotNull PsiElement context, @NotNull PsiType to, - @NotNull PsiType from, - @NotNull TypeMigrationLabeler labeler) { + @NotNull PsiType from) { final PsiClass toTypeClass = PsiUtil.resolveClassInType(to); LOG.assertTrue(toTypeClass != null); final PsiElement parent = context.getParent(); - final PsiElement statementOrLambda = RefactoringUtil.getParentStatement(context, false); if (parent instanceof PsiAssignmentExpression) { final PsiAssignmentExpression expression = (PsiAssignmentExpression)parent; final IElementType tokenType = expression.getOperationTokenType(); @@ -362,7 +358,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio } static class ObservableString extends ObservableType { - ObservableString(@NotNull Project project) { + ObservableString() { super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_STRING_PROPERTY); } @@ -453,7 +449,7 @@ public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionActio static class ObservableObject extends ObservableType { final PsiType myType; - ObservableObject(@NotNull PsiType type, @NotNull Project project) { + ObservableObject(@NotNull PsiType type) { super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_OBJECT_PROPERTY + "<" + type.getCanonicalText() + ">"); myType = type; }