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..dff5e22f402b --- /dev/null +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java @@ -0,0 +1,61 @@ +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 + * @author Pavel.Dolgov + */ +public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTest { + + public void testArtifactPresenceFieldToProperty() throws Exception { + configureByFiles(null, getTestName(false) + ".java"); + final IntentionAction intentionAction = getIntentionAction(); + // no artifact, no fxml, no javafx.* imports: the intention shouldn'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(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(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 new file mode 100644 index 000000000000..01b3d90df00b --- /dev/null +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java @@ -0,0 +1,109 @@ +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.openapi.roots.LanguageLevelProjectExtension; +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 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; + +import java.util.List; + +/** + * @author Pavel.Dolgov + */ +public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase { + private static final String actionName = JavaFxFieldToPropertyIntention.FAMILY_NAME; + + @Override + protected void setUpModule() { + super.setUpModule(); + AbstractJavaFXTestCase.addJavaFxJarAsLibrary(getModule()); + if (isArtifactNeeded()) { + 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 testFxmlPresenceFieldToProperty() throws Exception { + doTest(getTestName(false) + ".java", "sample.fxml"); + } + + public void testArtifactPresenceFieldToProperty() throws Exception { + doTest(); + } + + 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() throws Exception { + doTest(getTestName(false) + ".java"); + } + + private void doTest(String... fileNames) throws Exception { + LanguageLevelProjectExtension.getInstance(myProject).setLanguageLevel(LanguageLevel.JDK_1_7); + + configureByFiles(null, fileNames); + final IntentionAction intentionAction = getIntentionAction(); + assertNotNull(intentionAction); + + Editor editor = getEditor(); + PsiFile file = getFile(); + + assertTrue(ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, intentionAction,actionName)); + checkResultByFile(getTestName(false) + "_after.java"); + } + + protected IntentionAction getIntentionAction() throws Exception { + final List infos = doHighlighting(); + final Editor editor = getEditor(); + final PsiFile file = getFile(); + return findIntentionAction(infos, actionName, editor, file); + } + + protected boolean isArtifactNeeded() { + return true; + } + + @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..797c9c14efa0 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java @@ -0,0 +1,457 @@ +package org.jetbrains.plugins.javaFX.codeInsight; + +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.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; +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.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.util.IncorrectOperationException; +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; +import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; +import org.jetbrains.plugins.javaFX.fxml.JavaFxModuleUtil; +import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; + +import java.util.Collection; +import java.util.Set; + +/** + * @author Pavel.Dolgov + */ +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 FAMILY_NAME; + } + + @NotNull + @Override + public String getText() { + //noinspection DialogTitleCapitalization + return FAMILY_NAME; + } + + @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"); + new SearchUsagesTask(project, property).queue(); + } + + private static class SearchUsagesTask extends Task.Modal { + private final PropertyInfo myProperty; + private Collection myReferences; + private Set myFiles; + + public SearchUsagesTask(@NotNull Project project, + @NotNull PropertyInfo property) { + super(project, "Searching for usages of '" + property.myFieldName + "'", true); + myProperty = property; + } + + @Override + public void run(@NotNull ProgressIndicator indicator) { + ReadAction.run(() -> { + myReferences = ReferencesSearch.search(myProperty.myField).findAll(); + + 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; + }); + }); + WriteCommandAction + .runWriteCommandAction(myProject, "Convert '" + myProperty.myFieldName + "' to JavaFX property", null, + this::replaceOccurrences, myFiles.toArray(PsiFile.EMPTY_ARRAY)); + } + + private void replaceOccurrences() { + LOG.assertTrue(myProject != null, "myProject"); + final PsiField field = myProperty.myField; + 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); + if (conversion != null) { + TypeMigrationReplacementUtil.replaceExpression(expression, myProject, conversion, new TypeEvaluator(null, null)); + } + } + } + myProperty.convertField(); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + + @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(); + } + 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); + } + } + return null; + } + + TypeConversionDescriptor findDirectConversion(@NotNull PsiElement context, + @NotNull PsiType to, + @NotNull PsiType from) { + final PsiClass toTypeClass = PsiUtil.resolveClassInType(to); + LOG.assertTrue(toTypeClass != null); + + final PsiElement parent = context.getParent(); + 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() { + 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) { + 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..9fabe685bb6b 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 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..4bcdea9ef55e --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java @@ -0,0 +1,156 @@ +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.startup.StartupActivity; +import com.intellij.openapi.startup.StartupManager; +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; +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, FxmlPresenceListener.getModificationTracker(project)); + }); + return value; + } + + @NotNull + private static boolean hasJavaFxArtifacts(@NotNull Project project) { + return CachedValuesManager.getManager(project).getCachedValue( + project, () -> { + final ArtifactManager artifactManager = ArtifactManager.getInstance(project); + final Collection artifacts = artifactManager.getArtifactsByType(JavaFxApplicationArtifactType.getInstance()); + return CachedValueProvider.Result.create(!artifacts.isEmpty(), artifactManager.getModificationTracker()); + }); + } + + /** + * Avoids freeze on first use of Java intentions + */ + 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; + } + 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); + } + } + } + + 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(); + } + } + } +} 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/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 diff --git a/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty.java new file mode 100644 index 000000000000..eebcae5b9359 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty.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/ArtifactPresenceFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty_after.java new file mode 100644 index 000000000000..486bc7189cce --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty_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/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/FxmlPresenceFieldToProperty.java b/plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty.java new file mode 100644 index 000000000000..0f141c3df988 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty.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/FxmlPresenceFieldToProperty_after.java b/plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty_after.java new file mode 100644 index 000000000000..a7565645a1f3 --- /dev/null +++ b/plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty_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/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/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/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 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