Javafx: Validate type of constants and factory methods in FXML attributes fx:constant and fx:factory (IDEA-153811)

This commit is contained in:
Pavel Dolgov
2016-04-01 15:17:32 +03:00
parent bf5998c1f5
commit e0e5ef5655
8 changed files with 68 additions and 12 deletions
@@ -358,6 +358,14 @@ public class JavaFXHighlightingTest extends AbstractJavaFXTestCase {
doTest();
}
public void testBoxedConstantValue() throws Exception {
doTest();
}
public void testFactoryMethod() throws Exception {
doTest();
}
public void testPropertyTagCompatibleClass() throws Exception {
doTest();
}
@@ -35,12 +35,11 @@ import com.intellij.util.containers.HashSet;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.javaFX.fxml.FxmlConstants;
import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxBuiltInAttributeDescriptor;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxClassTagDescriptorBase;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxPropertyTagDescriptor;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxRootTagDescriptor;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxStaticSetterAttributeDescriptor;
import java.util.*;
@@ -145,7 +144,7 @@ public class JavaFxImportsOptimizer implements ImportOptimizer {
appendClassName(((PsiMember)declaration).getContainingClass());
}
}
else if (descriptor instanceof JavaFxBuiltInAttributeDescriptor && FxmlConstants.TYPE.equals(descriptor.getName())) {
else if (descriptor instanceof JavaFxRootTagDescriptor.RootTagTypeAttributeDescriptor) {
appendClassName(JavaFxPsiUtil.findPsiClass(attribute.getValue(), attribute));
}
}
@@ -115,11 +115,16 @@ public class JavaFxBuiltInAttributeDescriptor extends JavaFxPropertyAttributeDes
return "Unable to coerce '" + value + "' to " + tagClass.getQualifiedName() + ".";
}
}
} else if (FxmlConstants.TYPE.equals(attributeName)) {
final PsiReference[] references = context.getReferences();
if (references.length == 0 || references[references.length - 1].resolve() == null) {
return "Cannot resolve class " + value;
}
else if (FxmlConstants.FX_CONSTANT.equals(attributeName)) {
final PsiClass tagClass = JavaFxPsiUtil.getTagClass((XmlAttributeValue)context);
if (tagClass != null) {
final PsiField constField = tagClass.findFieldByName(value, true);
if (constField == null) {
return "Constant '" + value + "' is not found";
}
}
return null;
}
}
}
@@ -320,20 +320,23 @@ public abstract class JavaFxClassTagDescriptorBase implements XmlElementDescript
}
PsiClass aClass = getPsiClass();
final XmlAttribute constAttr = context.getAttribute(FxmlConstants.FX_CONSTANT);
final XmlAttribute factoryAttr = context.getAttribute(FxmlConstants.FX_FACTORY);
if (constAttr != null && aClass != null) {
final PsiField constField = aClass.findFieldByName(constAttr.getValue(), true);
if (constField != null) {
aClass = PsiUtil.resolveClassInType(constField.getType());
final PsiType constType = constField.getType();
aClass = PsiUtil.resolveClassInClassTypeOnly(
constType instanceof PsiPrimitiveType ? ((PsiPrimitiveType)constType).getBoxedType(context) : constType);
}
} else {
final XmlAttribute factoryAttr = context.getAttribute(FxmlConstants.FX_FACTORY);
if (factoryAttr != null) {
aClass = JavaFxPsiUtil.getFactoryProducedClass(aClass, factoryAttr.getValue());
}
}
JavaFxPsiUtil.isClassAcceptable(parentTag, aClass, (errorMessage, errorType) ->
host.addMessage(context.getNavigationElement(), errorMessage, errorType));
if (aClass != null && aClass.isValid()) {
boolean needInstantiate = constAttr == null && factoryAttr == null;
if (needInstantiate && aClass != null && aClass.isValid()) {
JavaFxPsiUtil.isAbleToInstantiate(aClass, errorMessage ->
host.addMessage(context, errorMessage, ValidationHost.ErrorType.ERROR));
}
@@ -75,7 +75,7 @@ public class JavaFxRootTagDescriptor extends JavaFxClassTagDescriptorBase {
return psiClass != null ? psiClass : myXmlTag;
}
private static class RootTagTypeAttributeDescriptor extends JavaFxPropertyAttributeDescriptor {
public static class RootTagTypeAttributeDescriptor extends JavaFxPropertyAttributeDescriptor {
public RootTagTypeAttributeDescriptor() {
super(FxmlConstants.TYPE, null);
}
@@ -59,7 +59,9 @@ class JavaFxFactoryReferenceProvider extends PsiReferenceProvider {
}
private static boolean isFactoryMethod(PsiMethod method) {
return method.hasModifierProperty(PsiModifier.STATIC) && method.getParameterList().getParametersCount() == 0;
return method.hasModifierProperty(PsiModifier.STATIC) &&
method.getParameterList().getParametersCount() == 0 &&
!PsiType.VOID.equals(method.getReturnType());
}
@NotNull
@@ -0,0 +1,24 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Control?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import java.lang.String?>
<?import java.lang.Double?>
<?import java.lang.System?>
<?import java.io.File?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<Button>
<maxWidth>
<Double fx:constant="POSITIVE_INFINITY"/>
</maxWidth>
<minWidth>
<Double fx:constant=<error descr="Constant 'UNKNOWN_CONSTANT' is not found">"<error descr="Wrong attribute value">UNKNOWN_CONSTANT</error>"</error>/>
</minWidth>
</Button>
<padding>
<<error descr="Unable to coerce java.lang.Class to javafx.geometry.Insets">Double</error> fx:constant="TYPE"/>
</padding>
<alignment>
<<error descr="Unable to coerce java.lang.Integer to javafx.geometry.Pos">Double</error> fx:constant="SIZE"/>
</alignment>
</GridPane>
@@ -0,0 +1,15 @@
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.layout.VBox?>
<?import java.lang.System?>
<?import java.util.Collections?>
<VBox xmlns:fx="http://javafx.com/fxml">
<ChoiceBox>
<Collections fx:factory="emptyList"/>
</ChoiceBox>
<ChoiceBox>
<Collections fx:factory="<error descr="Cannot resolve symbol 'unmodifiableList'">unmodifiableList</error>"/>
</ChoiceBox>
<ChoiceBox>
<System fx:factory="<error descr="Cannot resolve symbol 'gc'">gc</error>"/>
</ChoiceBox>
</VBox>