Javafx: "Wrap field with JavaFx property" intention (IDEA-102430)

This commit is contained in:
Pavel Dolgov
2016-10-17 20:13:40 +03:00
parent 6112e0cdae
commit 5706a80f78
26 changed files with 1164 additions and 2 deletions
+2
View 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>
@@ -0,0 +1,34 @@
package org.jetbrains.plugins.javaFX;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase;
/**
* Same as the base test, but detects the presence of JavaFX using imports and FXMLs, not artifacts
* @author Pavel.Dolgov
*/
public class JavaFxFieldToPropertyNoArtifactTest extends JavaFxFieldToPropertyTest {
public static final DefaultLightProjectDescriptor JAVA_FX_DESCRIPTOR_NO_ARTIFACT = new DefaultLightProjectDescriptor() {
@Override
public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) {
AbstractJavaFXTestCase.addJavaFxJarAsLibrary(module, model);
super.configureModule(module, model, contentEntry);
}
};
@Override
protected void setUpModule() {
super.setUpModule();
}
public void testLongFieldToProperty() throws Exception {
final IntentionAction intentionAction = getIntentionAction(false);
// no artifact, no fxml, no javafx.* imports: the intention shoudn't be available
assertNull(intentionAction);
}
}
@@ -0,0 +1,89 @@
package org.jetbrains.plugins.javaFX;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.packaging.artifacts.ArtifactManager;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.javaFX.fxml.AbstractJavaFXTestCase;
import org.jetbrains.plugins.javaFX.packaging.JavaFxApplicationArtifactType;
import java.util.List;
/**
* @author Pavel.Dolgov
* Run this test with 'main_idea_tests' classpath
*/
public class JavaFxFieldToPropertyTest extends DaemonAnalyzerTestCase {
private static final String actionName = "Convert to JavaFX property";
@Override
protected void setUpModule() {
super.setUpModule();
AbstractJavaFXTestCase.addJavaFxJarAsLibrary(getModule());
ArtifactManager.getInstance(getProject()).addArtifact("fake-javafx", JavaFxApplicationArtifactType.getInstance(), null);
}
public void testIntFieldToProperty() throws Exception {
doTest();
}
public void testBoxedFloatFieldToProperty() throws Exception {
doTest();
}
public void testStringFieldToProperty() throws Exception {
doTest();
}
public void testListFieldToProperty() throws Exception {
doTest();
}
public void testBigDecimalFieldToProperty() throws Exception {
doTest(true);
}
public void testLongFieldToProperty() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false);
}
private void doTest(boolean withFxml) throws Exception {
final IntentionAction intentionAction = getIntentionAction(withFxml);
assertNotNull(intentionAction);
Editor editor = getEditor();
PsiFile file = getFile();
assertTrue(ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, intentionAction,actionName));
checkResultByFile(getTestName(false) + "_after.java");
}
protected IntentionAction getIntentionAction(boolean withFxml) throws Exception {
if (withFxml) {
configureByFiles(null, getTestName(false) + ".java", "sample.fxml");
}
else {
configureByFiles(null, getTestName(false) + ".java");
}
final List<HighlightInfo> infos = doHighlighting();
final Editor editor = getEditor();
final PsiFile file = getFile();
return findIntentionAction(infos, actionName, editor, file);
}
@NotNull
@Override
protected String getTestDataPath() {
return PluginPathManager.getPluginHomePath("javaFX") + "/testData/intentions/fieldToProperty/";
}
}
+1
View 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>
@@ -0,0 +1,11 @@
ObservableIntegerValue number;
int getNumber() {
return number.get();
}
void setNumber(int newNumber) {
number.set(newNumber);
}
ObservableIntegerValue number() {
return number;
}
@@ -0,0 +1,8 @@
int number;
int getNumber() {
return number;
}
void setNumber(int newNumber) {
number = newNumber;
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention converts a field into JavaFX property.
</body>
</html>
@@ -44,6 +44,11 @@
<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>
@@ -0,0 +1,422 @@
package org.jetbrains.plugins.javaFX.codeInsight;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.LowPriorityAction;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.refactoring.typeMigration.*;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames;
import org.jetbrains.plugins.javaFX.fxml.JavaFxModuleUtil;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import java.util.HashSet;
import java.util.Set;
/**
* @author Pavel.Dolgov
*/
public class JavaFxFieldToPropertyIntention extends PsiElementBaseIntentionAction implements LowPriorityAction {
private static final Logger LOG = Logger.getInstance(JavaFxFieldToPropertyIntention.class);
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Convert to JavaFX property";
}
@NotNull
@Override
public String getText() {
//noinspection DialogTitleCapitalization
return "Convert to JavaFX property";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
final PsiField field = getField(element);
if (field != null) {
final PsiFile file = field.getContainingFile();
if (JavaFxModuleUtil.isInJavaFxProject(file) || JavaFxPsiUtil.isJavaFxPackageImported(file)) {
return PropertyInfo.createPropertyInfo(field, project) != null;
}
}
return false;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
final PsiField field = getField(element);
LOG.assertTrue(field != null, "field");
final PropertyInfo property = PropertyInfo.createPropertyInfo(field, project);
LOG.assertTrue(property != null, "propertyInfo");
final Query<PsiReference> refs = ReferencesSearch.search(field);
final Set<PsiElement> elements = new HashSet<>();
elements.add(element);
for (PsiReference reference : refs) {
elements.add(reference.getElement());
}
if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return;
field.normalizeDeclaration();
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
final PsiType fromType = field.getType();
final PsiType toType = elementFactory.createTypeFromText(property.myObservableType.myText, field);
try {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile()));
final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType);
final TypeMigrationUsageInfo[] migratedUsages = labeler.getMigratedUsages(false, field);
for (PsiReference reference : refs) {
final PsiElement refElement = reference.getElement();
if (refElement instanceof PsiExpression) {
final TypeConversionDescriptor conversion =
property.myObservableType.findDirectConversion(refElement, toType, fromType, labeler);
if (conversion != null) {
TypeMigrationReplacementUtil.replaceExpression((PsiExpression)refElement, project, conversion, new TypeEvaluator(null, null));
}
}
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
property.convertField();
}
@Nullable
private static PsiField getField(@NotNull PsiElement element) {
if (!(element instanceof PsiIdentifier)) return null;
final PsiField field = PsiTreeUtil.getParentOfType(element, PsiField.class);
if (field == null) return null;
if (field.getLanguage() != JavaLanguage.INSTANCE) return null;
if (field.getTypeElement() == null) return null;
if (field.hasModifierProperty(PsiModifier.STATIC) || field.hasModifierProperty(PsiModifier.FINAL)) return null;
return field;
}
private static class PropertyInfo {
final PsiField myField;
final PsiClass myContainingClass;
final PsiTypeElement myTypeElement;
final String myFieldName;
final ObservableType myObservableType;
private PropertyInfo(@NotNull PsiField field,
@NotNull PsiClass containingClass,
@NotNull PsiTypeElement typeElement,
@NotNull String fieldName,
@NotNull ObservableType observableType) {
myField = field;
myContainingClass = containingClass;
myTypeElement = typeElement;
myFieldName = fieldName;
myObservableType = observableType;
}
static PropertyInfo createPropertyInfo(@NotNull PsiField field, @NotNull Project project) {
final String fieldName = field.getName();
final PsiClass containingClass = field.getContainingClass();
final PsiTypeElement typeElement = field.getTypeElement();
if (fieldName != null && containingClass != null && typeElement != null) {
final ObservableType observableType = ObservableType.createObservableType(field, project);
if (observableType != null) {
return new PropertyInfo(field, containingClass, typeElement, fieldName, observableType);
}
}
return null;
}
private void convertField() {
final Project project = myContainingClass.getProject();
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
final PsiTypeElement newTypeElement = elementFactory.createTypeElementFromText(myObservableType.myText, myField);
myTypeElement.replace(newTypeElement);
final PsiExpression initializer = myField.getInitializer();
final String propertyName = JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(myFieldName, VariableKind.FIELD);
final String initializerArgs = "this,\"" + propertyName + "\"" + (initializer == null ? "" : "," + initializer.getText());
String initializerText = "new " + myObservableType.myText + "(" + initializerArgs + ")";
final PsiNewExpression newInitializer = (PsiNewExpression)elementFactory.createExpressionFromText(initializerText, myField);
myField.setInitializer(newInitializer);
final PsiType fieldType = myField.getType();
if (PsiDiamondTypeUtil.canCollapseToDiamond(newInitializer, newInitializer, fieldType)) {
final PsiJavaCodeReferenceElement classReference = newInitializer.getClassOrAnonymousClassReference();
if (classReference != null) {
PsiDiamondTypeUtil.replaceExplicitWithDiamond(classReference.getParameterList());
}
}
myField.setInitializer(newInitializer);
final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project);
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
codeStyleManager.reformat(javaCodeStyleManager.shortenClassReferences(myField));
}
}
static class ObservableType {
final String myText;
ObservableType(@NotNull String text) {
this.myText = text;
}
@Nullable
static ObservableType createObservableType(@NotNull PsiField field, @NotNull Project project) {
final PsiType type = field.getType();
if (type instanceof PsiPrimitiveType) {
final String text = JavaFxCommonNames.ourObservablePrimitiveWrappers.get(type);
return text != null ? new ObservablePrimitive(text, (PsiPrimitiveType)type) : null;
}
final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
if (unboxedType != null) {
final String text = JavaFxCommonNames.ourObservablePrimitiveWrappers.get(unboxedType);
return text != null ? new ObservablePrimitive(text, unboxedType) : null;
}
if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return new ObservableString(project);
}
if (type instanceof PsiClassType) {
if (InheritanceUtil.isInheritor(type, JavaFxCommonNames.JAVAFX_BEANS_OBSERVABLE)) {
return null; // already observable
}
if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_LIST)) {
return ObservableList.createObservableList(type, project);
}
else if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_COLLECTION) ||
InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
return null; // TODO: support SimpleSetProperty, SimpleMapProperty
}
else {
return new ObservableObject(type, project);
}
}
return null;
}
TypeConversionDescriptor findDirectConversion(@NotNull PsiElement context,
@NotNull PsiType to,
@NotNull PsiType from,
@NotNull TypeMigrationLabeler labeler) {
final PsiClass toTypeClass = PsiUtil.resolveClassInType(to);
LOG.assertTrue(toTypeClass != null);
final PsiElement parent = context.getParent();
final PsiElement statementOrLambda = RefactoringUtil.getParentStatement(context, false);
if (parent instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression expression = (PsiAssignmentExpression)parent;
final IElementType tokenType = expression.getOperationTokenType();
if (tokenType == JavaTokenType.EQ) {
return findSimpleAssignmentConversion(expression);
}
final String sign = expression.getOperationSign().getText();
final String binarySign = sign.substring(0, sign.length() - 1);
return findCompoundAssignmentConversion(from, expression, sign, binarySign);
}
else if (parent instanceof PsiPostfixExpression) {
final PsiPostfixExpression expression = (PsiPostfixExpression)parent;
final TypeConversionDescriptor conversion = getUpdateConversion(expression, expression.getOperationSign(), true);
if (conversion != null) return conversion;
}
else if (parent instanceof PsiPrefixExpression) {
final PsiPrefixExpression expression = (PsiPrefixExpression)parent;
final TypeConversionDescriptor conversion = getUpdateConversion(expression, expression.getOperationSign(), false);
if (conversion != null) return conversion;
}
else if (context instanceof PsiReferenceExpression) {
final PsiExpression qualifierExpression = ((PsiReferenceExpression)context).getQualifierExpression();
final PsiExpression expression = context.getParent() instanceof PsiMethodCallExpression && qualifierExpression != null
? qualifierExpression
: (PsiExpression)context;
return getReadConversion(expression);
}
return null;
}
@Nullable
TypeConversionDescriptor findSimpleAssignmentConversion(PsiAssignmentExpression expression) {
return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)", expression);
}
@Nullable
TypeConversionDescriptor findCompoundAssignmentConversion(@NotNull PsiType from,
@NotNull PsiExpression expression,
@NotNull String sign,
@NotNull String binarySign) {
return null;
}
@Nullable
TypeConversionDescriptor getUpdateConversion(@NotNull PsiExpression expression, @NotNull PsiJavaToken operationToken, boolean postfix) {
return null;
}
@NotNull
TypeConversionDescriptor getReadConversion(PsiExpression expression) {
return new TypeConversionDescriptor("$qualifier$", "$qualifier$.get()", expression);
}
}
static class ObservablePrimitive extends ObservableType {
final PsiPrimitiveType myType;
ObservablePrimitive(@NotNull String text, @NotNull PsiPrimitiveType type) {
super(text);
myType = type;
}
@Nullable
@Override
TypeConversionDescriptor findCompoundAssignmentConversion(@NotNull PsiType from,
@NotNull PsiExpression expression,
@NotNull String sign,
@NotNull String binarySign) {
final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(from);
final String valueType = (unboxedType != null ? unboxedType : from).getCanonicalText();
return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$",
"$qualifier$.set((" + valueType + ")($qualifier$.get() " + binarySign + " ($val$)))",
expression);
}
@Nullable
@Override
TypeConversionDescriptor getUpdateConversion(@NotNull PsiExpression expression, @NotNull PsiJavaToken operationToken, boolean postfix) {
final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(expression);
if (parent instanceof PsiExpressionStatement) {
final IElementType tokenType = operationToken.getTokenType();
if (tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS) {
final String sign = operationToken.getText();
return new TypeConversionDescriptor(postfix ? ("$qualifier$" + sign) : (sign + "$qualifier$"),
"$qualifier$.set($qualifier$.get()" + sign.charAt(0) + "1)", expression);
}
}
return null;
}
}
static class ObservableString extends ObservableType {
ObservableString(@NotNull Project project) {
super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_STRING_PROPERTY);
}
@Nullable
@Override
TypeConversionDescriptor findCompoundAssignmentConversion(@NotNull PsiType from,
@NotNull PsiExpression expression,
@NotNull String sign,
@NotNull String binarySign) {
return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$",
"$qualifier$.set($qualifier$.get() " + binarySign + " ($val$))",
expression);
}
}
static class ObservableList extends ObservableType {
final PsiType myOriginalType;
final PsiType myItemType;
ObservableList(@NotNull PsiType originalType, @NotNull PsiType itemType) {
super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_LIST_PROPERTY + "<" + itemType.getCanonicalText() + ">");
myOriginalType = originalType;
myItemType = itemType;
}
@Nullable
private static ObservableType createObservableList(@NotNull PsiType type, @NotNull Project project) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(type);
final PsiClass fieldClass = resolveResult.getElement();
if (fieldClass != null) {
final PsiClass listClass = JavaPsiFacade.getInstance(project)
.findClass(CommonClassNames.JAVA_UTIL_LIST, GlobalSearchScope.allScope(project));
if (listClass != null) {
final PsiSubstitutor substitutor =
TypeConversionUtil.getClassSubstitutor(listClass, fieldClass, resolveResult.getSubstitutor());
if (substitutor != null) {
final PsiType itemType = substitutor.substitute(listClass.getTypeParameters()[0]);
if (itemType != null) {
return new ObservableList(type, itemType);
}
}
}
}
return null;
}
@Nullable
@Override
TypeConversionDescriptor findSimpleAssignmentConversion(PsiAssignmentExpression expression) {
return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.setAll($val$)", expression);
}
@NotNull
@Override
TypeConversionDescriptor getReadConversion(PsiExpression expression) {
return new TypeConversionDescriptor("$qualifier$", "$qualifier$.get()", expression) {
@Override
public PsiExpression replace(PsiExpression expression, TypeEvaluator evaluator) {
final PsiExpression replaced = super.replace(expression, evaluator);
// Replace the getter's return type: List -> ObservableList
final PsiElement parent = replaced.getParent();
if (parent instanceof PsiReturnStatement) {
final PsiReturnStatement returnStatement = (PsiReturnStatement)parent;
final PsiElement statementParent = returnStatement.getParent();
if (statementParent instanceof PsiCodeBlock) {
final PsiCodeBlock codeBlock = (PsiCodeBlock)statementParent;
final PsiElement blockParent = codeBlock.getParent();
if (blockParent instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)blockParent;
final PsiTypeElement returnTypeElement = method.getReturnTypeElement();
if (returnTypeElement != null && myOriginalType.equals(method.getReturnType())) {
final Project project = expression.getProject();
final String text = JavaFxCommonNames.JAVAFX_COLLECTIONS_OBSERVABLE_LIST + "<" + myItemType.getCanonicalText() + ">";
final PsiTypeElement newReturnTypeElement = JavaPsiFacade.getInstance(project)
.getElementFactory().createTypeElementFromText(text, method);
final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project);
javaCodeStyleManager.shortenClassReferences(returnTypeElement.replace(newReturnTypeElement));
}
}
}
}
return replaced;
}
};
}
}
static class ObservableObject extends ObservableType {
final PsiType myType;
ObservableObject(@NotNull PsiType type, @NotNull Project project) {
super(JavaFxCommonNames.JAVAFX_BEANS_PROPERTY_SIMPLE_OBJECT_PROPERTY + "<" + type.getCanonicalText() + ">");
myType = type;
}
}
}
@@ -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");
}
}
@@ -6,6 +6,7 @@ import com.intellij.openapi.fileTypes.FileTypeFactory;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
@@ -13,7 +14,8 @@ import org.jetbrains.annotations.NotNull;
* Date: 1/8/13
*/
public class JavaFxFileTypeFactory extends FileTypeFactory {
public static final String FXML_EXTENSION = "fxml";
@NonNls public static final String FXML_EXTENSION = "fxml";
@NonNls private static final String DOT_FXML_EXTENSION = "." + FXML_EXTENSION;
public static boolean isFxml(@NotNull PsiFile file) {
final VirtualFile virtualFile = file.getViewProvider().getVirtualFile();
@@ -23,11 +25,18 @@ public class JavaFxFileTypeFactory extends FileTypeFactory {
public static boolean isFxml(@NotNull VirtualFile virtualFile) {
if (FXML_EXTENSION.equals(virtualFile.getExtension())) {
final FileType fileType = virtualFile.getFileType();
if (fileType == FileTypeManager.getInstance().getFileTypeByExtension(FXML_EXTENSION) && !fileType.isBinary()) return true;
if (fileType == getFileType() && !fileType.isBinary()) {
return virtualFile.getName().endsWith(DOT_FXML_EXTENSION);
}
}
return false;
}
@NotNull
public static FileType getFileType() {
return FileTypeManager.getInstance().getFileTypeByExtension(FXML_EXTENSION);
}
@Override
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
final FileType fileType = consumer.getStandardFileTypeByName("XML");
@@ -0,0 +1,100 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.artifacts.ArtifactManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.javaFX.packaging.JavaFxApplicationArtifactType;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Pavel.Dolgov
*/
public class JavaFxModuleUtil {
public static boolean isInJavaFxProject(@NotNull PsiFile file) {
final Project project = file.getProject();
if (hasJavaFxArtifacts(project)) {
return true;
}
return isInJavaFxModule(file);
}
private static boolean isInJavaFxModule(@NotNull PsiFile file) {
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
final Project project = file.getProject();
final Module fileModule = ModuleUtil.findModuleForFile(virtualFile, project);
if (fileModule != null) {
return getCachedJavaFxModules(project).contains(fileModule);
}
}
return false;
}
@NotNull
private static Set<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, ProjectRootManager.getInstance(project));
});
return value;
}
@NotNull
private static boolean hasJavaFxArtifacts(@NotNull Project project) {
return CachedValuesManager.getManager(project).getCachedValue(
project, () -> {
final Collection<? extends Artifact> artifacts =
ArtifactManager.getInstance(project).getArtifactsByType(JavaFxApplicationArtifactType.getInstance());
return CachedValueProvider.Result.create(!artifacts.isEmpty(), ProjectRootManager.getInstance(project));
});
}
/**
* Avoids freeze on first use of Java intentions
*/
public static class JavaFxDetectionStartupActivity implements StartupActivity {
@Override
public void runActivity(@NotNull Project project) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return;
}
StartupManager.getInstance(project).runWhenProjectIsInitialized(
() -> ApplicationManager.getApplication().executeOnPooledThread(
() -> DumbService.getInstance(project).runReadActionInSmartMode(
() -> populateCachedJavaFxModules(project))));
}
private void populateCachedJavaFxModules(@NotNull Project project) {
if (!project.isDisposed() && project.isOpen()) {
hasJavaFxArtifacts(project);
getCachedJavaFxModules(project);
}
}
}
}
@@ -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);
});
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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>