From 528c63497e26599c92d7c5f8522b0638f62c3040 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Tue, 14 Jun 2016 16:00:05 +0300 Subject: [PATCH] Javafx: Resolve $null symbol and ${null} expression in FXML (IDEA-112153) --- .../javaFX/fxml/JavaFXHighlightingTest.java | 8 ++++++++ .../plugins/javaFX/fxml/FxmlConstants.java | 8 ++++++++ .../plugins/javaFX/fxml/JavaFxPsiUtil.java | 12 +++++++++++- .../JavaFxPropertyAttributeDescriptor.java | 16 ++++++++++++++-- .../refs/JavaFxComponentIdReferenceProvider.java | 2 ++ .../testData/highlighting/nullObjectValue.fxml | 9 +++++++++ .../highlighting/nullPrimitiveValue.fxml | 8 ++++++++ 7 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 plugins/javaFX/testData/highlighting/nullObjectValue.fxml create mode 100644 plugins/javaFX/testData/highlighting/nullPrimitiveValue.fxml diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java index af1d00f56f6a..f87e362250ed 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java @@ -374,6 +374,14 @@ public class JavaFXHighlightingTest extends AbstractJavaFXTestCase { doTest(); } + public void testNullObjectValue() throws Exception { + doTest(); + } + + public void testNullPrimitiveValue() throws Exception { + doTest(); + } + public void testFactoryMethod() throws Exception { doTest(); } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/FxmlConstants.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/FxmlConstants.java index a2fe11308e89..f620b5975596 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/FxmlConstants.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/FxmlConstants.java @@ -15,8 +15,10 @@ */ package org.jetbrains.plugins.javaFX.fxml; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -51,6 +53,9 @@ public class FxmlConstants { @NonNls public static final String SOURCE = "source"; + @NonNls public static final String NULL_EXPRESSION = "${null}"; + @NonNls private static final String NULL_VALUE = "$null"; + public static final Set FX_BUILT_IN_ATTRIBUTES = ContainerUtil.immutableSet(FX_ID, FX_CONTROLLER, VALUE, FX_VALUE, FX_FACTORY, FX_CONSTANT); @@ -71,4 +76,7 @@ public class FxmlConstants { .put(FX_COPY, Collections.singletonList(SOURCE)) .build(); + public static boolean isNullValue(@NotNull String value) { + return NULL_VALUE.equals(StringUtil.trimTrailing(value)); + } } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java index a54a3f93a0d0..ae0737c8e12e 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java @@ -38,6 +38,7 @@ import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlElementDescriptor; import gnu.trove.THashMap; import gnu.trove.THashSet; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -804,11 +805,19 @@ public class JavaFxPsiUtil { @Nullable public static PsiClass getWritablePropertyClass(@Nullable XmlAttributeValue xmlAttributeValue) { + if (xmlAttributeValue != null) { + return getPropertyClass(getWritablePropertyType(xmlAttributeValue), xmlAttributeValue); + } + return null; + } + + @Nullable + public static PsiType getWritablePropertyType(@Nullable XmlAttributeValue xmlAttributeValue) { final PsiClass tagClass = getTagClass(xmlAttributeValue); if (tagClass != null) { final PsiElement declaration = getAttributeDeclaration(xmlAttributeValue); if (declaration != null) { - return getPropertyClass(getWritablePropertyType(tagClass, declaration), xmlAttributeValue); + return getWritablePropertyType(tagClass, declaration); } } return null; @@ -843,6 +852,7 @@ public class JavaFxPsiUtil { return null; } + @Contract("null->false") public static boolean isPrimitiveOrBoxed(@Nullable PsiType psiType) { return psiType instanceof PsiPrimitiveType || PsiPrimitiveType.getUnboxedType(psiType) != null; } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java index ee17676b1872..ea4ea65b6d6e 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java @@ -9,6 +9,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.xml.impl.BasicXmlAttributeDescriptor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; @@ -129,6 +130,9 @@ public class JavaFxPropertyAttributeDescriptor extends BasicXmlAttributeDescript if (value.startsWith("$")) { return validatePropertyExpression(xmlAttributeValue, value); } + else if (StringUtil.trimLeading(value).startsWith("$")) { + return "Spaces aren't allowed before property or expression"; + } else { return validateLiteral(xmlAttributeValue, value); } @@ -160,9 +164,17 @@ public class JavaFxPropertyAttributeDescriptor extends BasicXmlAttributeDescript if (isIncompletePropertyChain(propertyNames)) { return "Incorrect expression syntax"; } + if (FxmlConstants.NULL_EXPRESSION.equals(value)) return null; final XmlTag currentTag = PsiTreeUtil.getParentOfType(xmlAttributeValue, XmlTag.class); - final PsiClass targetPropertyClass = JavaFxPsiUtil.getWritablePropertyClass(xmlAttributeValue); + final PsiType targetPropertyType = JavaFxPsiUtil.getWritablePropertyType(xmlAttributeValue); + if (FxmlConstants.isNullValue(value)) { + if (JavaFxPsiUtil.isPrimitiveOrBoxed(targetPropertyType)) { + return "Unable to coerce to " + targetPropertyType.getPresentableText(); + } + return null; + } + final PsiClass targetPropertyClass = JavaFxPsiUtil.getPropertyClass(targetPropertyType, xmlAttributeValue); if (targetPropertyClass == null || JavaFxPsiUtil.hasConversionFromAnyType(targetPropertyClass)) return null; final String firstPropertyName = propertyNames.get(0); @@ -242,7 +254,7 @@ public class JavaFxPropertyAttributeDescriptor extends BasicXmlAttributeDescript @Override public PsiReference[] getValueReferences(XmlElement element, @NotNull String text) { - return !text.startsWith("${") ? super.getValueReferences(element, text) : PsiReference.EMPTY_ARRAY; + return !text.startsWith("${") && !FxmlConstants.isNullValue(text) ? super.getValueReferences(element, text) : PsiReference.EMPTY_ARRAY; } @Override diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java index fe8d81f915ed..d6a9c74d30b3 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java @@ -81,6 +81,7 @@ public class JavaFxComponentIdReferenceProvider extends PsiReferenceProvider { @NotNull XmlAttributeValue xmlAttributeValue, @NotNull String value, @NotNull Map fileIds) { + if (FxmlConstants.NULL_EXPRESSION.equals(value)) return PsiReference.EMPTY_ARRAY; final String expressionBody = value.substring(2, value.length() - 1); final List propertyNames = StringUtil.split(expressionBody, ".", true, false); if (JavaFxPropertyAttributeDescriptor.isIncompletePropertyChain(propertyNames)) return PsiReference.EMPTY_ARRAY; @@ -115,6 +116,7 @@ public class JavaFxComponentIdReferenceProvider extends PsiReferenceProvider { private static PsiReference[] getSinglePropertyReferences(@NotNull XmlAttributeValue xmlAttributeValue, @NotNull String value, @NotNull Map fileIds) { + if (FxmlConstants.isNullValue(value)) return PsiReference.EMPTY_ARRAY; return getSinglePropertyReferences(xmlAttributeValue, fileIds, value.substring(1), 1); } diff --git a/plugins/javaFX/testData/highlighting/nullObjectValue.fxml b/plugins/javaFX/testData/highlighting/nullObjectValue.fxml new file mode 100644 index 000000000000..34d405a15be9 --- /dev/null +++ b/plugins/javaFX/testData/highlighting/nullObjectValue.fxml @@ -0,0 +1,9 @@ + + + + +