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 extends Artifact> 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