Merge remote-tracking branch 'remotes/origin/pdolgov/javafxFieldToProperty'
authorPavel Dolgov <pavel.dolgov@jetbrains.com>
Wed, 9 Nov 2016 12:20:19 +0000 (15:20 +0300)
committerPavel Dolgov <pavel.dolgov@jetbrains.com>
Wed, 9 Nov 2016 12:20:19 +0000 (15:20 +0300)
31 files changed:
plugins/javaFX/javaFX-CE/javaFX-CE.iml
plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyNoArtifactTest.java [new file with mode: 0644]
plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/JavaFxFieldToPropertyTest.java [new file with mode: 0644]
plugins/javaFX/javaFX.iml
plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/after.java.template [new file with mode: 0644]
plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/before.java.template [new file with mode: 0644]
plugins/javaFX/resources/intentionDescriptions/JavaFxFieldToPropertyIntention/description.html [new file with mode: 0644]
plugins/javaFX/src/META-INF/common-javaFX-plugin.xml
plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxFieldToPropertyIntention.java [new file with mode: 0644]
plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxCommonNames.java
plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxFileTypeFactory.java
plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxModuleUtil.java [new file with mode: 0644]
plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java
plugins/javaFX/testData/intentions/fieldToProperty/AddRemoveFxmlFile.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/ArtifactPresenceFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/BoxedFloatFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/FxmlPresenceFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/IntFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/ListFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/StringFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToPropertySecondFile_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/TwoFilesFieldToProperty_after.java [new file with mode: 0644]
plugins/javaFX/testData/intentions/fieldToProperty/sample.fxml [new file with mode: 0644]

index 7eedf1ef09d108eec1f42ac72fc0a9549ea3e09b..c09b130976807259897ad1b32521a14b4456d5aa 100644 (file)
@@ -15,5 +15,7 @@
     <orderEntry type="module" module-name="jetgroovy" scope="TEST" />
     <orderEntry type="module" module-name="common-javaFX-plugin" scope="TEST" />
     <orderEntry type="module" module-name="java-tests" scope="TEST" />
+    <orderEntry type="module" module-name="structuralsearch-groovy" scope="TEST" />
+    <orderEntry type="module" module-name="compiler-openapi" scope="TEST" />
   </component>
 </module>
\ 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 (file)
index 0000000..dff5e22
--- /dev/null
@@ -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<VirtualFile> fileRef = new Ref<>();
+    ApplicationManager.getApplication().runWriteAction(() -> {
+      try {
+        VirtualFile file = sourceRootDir.createChildData(this, "sample.fxml");
+        VfsUtil.saveText(file, "<?import javafx.scene.layout.VBox?>\n<VBox/>");
+        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 (file)
index 0000000..01b3d90
--- /dev/null
@@ -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<HighlightInfo> 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/";
+  }
+}
index 0936ae44f5a0d15a1e20cbc07ca7cb5e3bedf1e6..ad4ea7ec5e0ee39e445a8d8e42214ed9db0ec0ed 100644 (file)
@@ -27,5 +27,6 @@
     <orderEntry type="module" module-name="properties" />
     <orderEntry type="library" name="SceneBuilderKit" level="project" />
     <orderEntry type="module" module-name="testFramework-java" scope="TEST" />
+    <orderEntry type="module" module-name="typeMigration" />
   </component>
 </module>
\ 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 (file)
index 0000000..1eff96d
--- /dev/null
@@ -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 (file)
index 0000000..557a869
--- /dev/null
@@ -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 (file)
index 0000000..1e2bf5b
--- /dev/null
@@ -0,0 +1,5 @@
+<html>
+<body>
+This intention converts a field into JavaFX property.
+</body>
+</html>
\ No newline at end of file
index 59b5666860ccd597519896a18abbc46d93eb7fe4..65515493b6db073961f3fc8fcbc304c41533d669 100644 (file)
       <className>org.jetbrains.plugins.javaFX.fxml.codeInsight.intentions.JavaFxExpandAttributeIntention</className>
       <category>JavaFX</category>
     </intentionAction>
+    <intentionAction>
+      <className>org.jetbrains.plugins.javaFX.codeInsight.JavaFxFieldToPropertyIntention</className>
+      <category>JavaFX</category>
+    </intentionAction>
+
     <lang.importOptimizer language="XML" implementationClass="org.jetbrains.plugins.javaFX.fxml.codeInsight.JavaFxImportsOptimizer" order="before XML"/>
     <psi.referenceContributor implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxReferencesContributor"/>
     <getterSetterProvider implementation="org.jetbrains.plugins.javaFX.codeInsight.JavaFxGetterSetterPrototypeProvider"/>
@@ -65,6 +70,7 @@
     <deadCode implementation="org.jetbrains.plugins.javaFX.JavaFxEntryPoint"/>
     <projectSdkSetupValidator implementation="org.jetbrains.plugins.javaFX.JavaFxProjectSdkSetupValidator" order="after javaSdk"/>
     <predefinedMigrationMapProvider implementation="org.jetbrains.plugins.javaFX.refactoring.migration.JavaFx9Migration"/>
+    <postStartupActivity implementation="org.jetbrains.plugins.javaFX.fxml.JavaFxModuleUtil$JavaFxDetectionStartupActivity"/>
   </extensions>
 
   <actions>
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 (file)
index 0000000..797c9c1
--- /dev/null
@@ -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<PsiReference> myReferences;
+    private Set<PsiFile> 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<PsiElement> 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<VirtualFile> 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;
+    }
+  }
+}
index 0c7bb16b7a7b4fbfce597f528c3dab3b085b328d..7a88db9b091c37328ad4ffd0f42e3b9e9cb87693 100644 (file)
@@ -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<PsiPrimitiveType, String> 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");
+  }
 }
index 27e0093fb04b3503e16ba9ae92dc88145fbfb3a8..9fabe685bb6b1830a423eafb5e1eee76dfbf9696 100644 (file)
@@ -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 (file)
index 0000000..4bcdea9
--- /dev/null
@@ -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<Module> getCachedJavaFxModules(@NotNull Project project) {
+    Set<Module> value = CachedValuesManager.getManager(project).getCachedValue(
+      project, () -> {
+        final Collection<VirtualFile> files =
+          FileTypeIndex.getFiles(JavaFxFileTypeFactory.getFileType(), GlobalSearchScope.projectScope(project));
+
+        final Set<Module> 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<ModificationTracker> 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();
+      }
+    }
+  }
+}
index 7029408deca3cb71d275088989f6bc99b8d6bec8..25cec6a5dbfdf798a528919d773cf1188312dd33 100644 (file)
@@ -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 (file)
index 0000000..0c8060c
--- /dev/null
@@ -0,0 +1,3 @@
+class FloatDemo {
+    float <caret>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 (file)
index 0000000..eebcae5
--- /dev/null
@@ -0,0 +1,47 @@
+class LongDemo {
+    private long <caret>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 (file)
index 0000000..486bc71
--- /dev/null
@@ -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 (file)
index 0000000..5bd7f69
--- /dev/null
@@ -0,0 +1,49 @@
+import javafx.scene.Node;
+
+class BoxedFloatDemo {
+    private Float <caret>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 (file)
index 0000000..669e415
--- /dev/null
@@ -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 (file)
index 0000000..0f141c3
--- /dev/null
@@ -0,0 +1,17 @@
+import java.math.BigDecimal;
+
+class BigDecimalDemo {
+  private BigDecimal nu<caret>mber = 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 (file)
index 0000000..a756564
--- /dev/null
@@ -0,0 +1,19 @@
+import javafx.beans.property.SimpleObjectProperty;
+
+import java.math.BigDecimal;
+
+class BigDecimalDemo {
+    private SimpleObjectProperty<BigDecimal> 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 (file)
index 0000000..e4a0e2e
--- /dev/null
@@ -0,0 +1,59 @@
+import javafx.scene.Node;
+
+class IntDemo {
+    int <caret>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 (file)
index 0000000..75c84d9
--- /dev/null
@@ -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 (file)
index 0000000..ffeff87
--- /dev/null
@@ -0,0 +1,23 @@
+import java.net.URL;
+import java.util.List;
+import javafx.scene.Node;
+
+class ListDemo {
+    private List<URL> <caret>urls;
+
+    ListDemo(List<URL> urls) {
+        this.urls = urls;
+    }
+
+    public List<URL> getUrls() {
+        return urls;
+    }
+
+    public void setUrls(List<URL> 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 (file)
index 0000000..a1a8d51
--- /dev/null
@@ -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<URL> urls = new SimpleListProperty<>(this, "urls");
+
+    ListDemo(List<URL> urls) {
+        this.urls.setAll(urls);
+    }
+
+    public ObservableList<URL> getUrls() {
+        return urls.get();
+    }
+
+    public void setUrls(List<URL> 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 (file)
index 0000000..5778731
--- /dev/null
@@ -0,0 +1,17 @@
+import javafx.scene.Node;
+
+class StringDemo {
+    String <caret>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 (file)
index 0000000..3f46763
--- /dev/null
@@ -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 (file)
index 0000000..79e4550
--- /dev/null
@@ -0,0 +1,13 @@
+import javafx.scene.Node;
+
+class DoubleDemo {
+    double <caret>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 (file)
index 0000000..d3641da
--- /dev/null
@@ -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 (file)
index 0000000..b731004
--- /dev/null
@@ -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 (file)
index 0000000..03f73c0
--- /dev/null
@@ -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 (file)
index 0000000..083f45d
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.layout.GridPane?>
+
+<GridPane xmlns:fx="http://javafx.com/fxml/1">
+</GridPane>
\ No newline at end of file