javafx: check for acceptable classes to import according to tag placement in .fxml; add checks for hierarchy caused by default property (IDEA-103205)

(cherry picked from commit 968cfb49eb29777863b298beb04b2cfd3cafce15)
This commit is contained in:
anna
2013-03-15 22:04:31 +01:00
parent f649c90a65
commit 1b9cb2dce0
9 changed files with 95 additions and 30 deletions
@@ -262,6 +262,10 @@ public class JavaFXHighlightingTest extends DaemonAnalyzerTestCase {
doTest();
}
public void testUnexpectedNode() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false, false, getTestName(true) + ".fxml");
}
@@ -33,7 +33,13 @@ public class JavaFXImportTest extends DaemonAnalyzerTestCase {
public void testSimpleImport() throws Exception {
doTest();
}
public void testTextField() throws Exception {
configureByFiles(null, getTestName(true) + ".fxml", getTestName(false) + ".java");
final List<HighlightInfo> infos = doHighlighting();
findAndInvokeIntentionAction(infos, "Import Class", getEditor(), getFile());
checkResultByFile(getTestName(true) + "_after.fxml");
}
private void doTest() throws Exception {
configureByFiles(null, getTestName(true) + ".fxml");
@@ -16,6 +16,8 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.fileTypes.StdFileTypes;
@@ -33,6 +35,8 @@ import com.intellij.util.Processor;
import com.intellij.xml.XmlElementDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxClassBackedElementDescriptor;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxPropertyElementDescriptor;
import java.util.ArrayList;
import java.util.Collection;
@@ -430,6 +434,54 @@ public class JavaFxPsiUtil {
return null;
}
public static String isClassAcceptable(@Nullable XmlTag parentTag, final PsiClass aClass) {
if (aClass != null && aClass.isValid()) {
if (parentTag == null) {
if (!InheritanceUtil.isInheritor(aClass, false, JavaFxCommonClassNames.JAVAFX_SCENE_NODE)) {
return unableToCoerceMessage(aClass, JavaFxCommonClassNames.JAVAFX_SCENE_NODE);
}
return null;
}
final XmlElementDescriptor descriptor = parentTag.getDescriptor();
if (descriptor instanceof JavaFxPropertyElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof PsiField) {
return canCoerce(aClass, ((PsiField)declaration).getType());
}
}
else if (descriptor instanceof JavaFxClassBackedElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof PsiClass) {
final PsiType type = getDefaultPropertyExpectedType((PsiClass)declaration);
if (type != null) {
return canCoerce(aClass, type);
}
}
}
}
return null;
}
private static String canCoerce(PsiClass aClass, PsiType type) {
final PsiType collectionItemType = GenericsHighlightUtil.getCollectionItemType(type, aClass.getResolveScope());
if (collectionItemType != null && PsiPrimitiveType.getUnboxedType(collectionItemType) == null) {
final PsiClass baseClass = PsiUtil.resolveClassInType(collectionItemType);
if (baseClass != null) {
final String qualifiedName = baseClass.getQualifiedName();
if (qualifiedName != null && !Comparing.strEqual(qualifiedName, CommonClassNames.JAVA_LANG_STRING)) {
if (!InheritanceUtil.isInheritor(aClass, qualifiedName)) {
return unableToCoerceMessage(aClass, qualifiedName);
}
}
}
}
return null;
}
private static String unableToCoerceMessage(PsiClass aClass, String qualifiedName) {
return "Unable to coerce " + HighlightUtil.formatClass(aClass)+ " to " + qualifiedName;
}
private static class JavaFxControllerCachedValueProvider implements CachedValueProvider<PsiClass> {
private final Project myProject;
private final PsiFile myContainingFile;
@@ -2,7 +2,6 @@ package org.jetbrains.plugins.javaFX.fxml.descriptors;
import com.intellij.codeInsight.daemon.Validator;
import com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
@@ -11,7 +10,6 @@ import com.intellij.psi.impl.source.xml.XmlAttributeImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
@@ -280,7 +278,10 @@ public class JavaFxClassBackedElementDescriptor implements XmlElementDescriptor,
host.addMessage(((XmlAttributeImpl)attribute).getNameElement(), "fx:controller can only be applied to root element", ValidationHost.ErrorType.ERROR); //todo add delete/move to upper tag fix
}
}
validateTagAccordingToFieldType(context, parentTag, host);
final String canCoerceError = JavaFxPsiUtil.isClassAcceptable(parentTag, myPsiClass);
if (canCoerceError != null) {
host.addMessage(context.getNavigationElement(), canCoerceError, ValidationHost.ErrorType.ERROR);
}
if (myPsiClass != null && myPsiClass.isValid()) {
final String message = JavaFxPsiUtil.isAbleToInstantiate(myPsiClass);
if (message != null) {
@@ -288,29 +289,4 @@ public class JavaFxClassBackedElementDescriptor implements XmlElementDescriptor,
}
}
}
private void validateTagAccordingToFieldType(XmlTag context, XmlTag parentTag, ValidationHost host) {
if (myPsiClass != null && myPsiClass.isValid()) {
final XmlElementDescriptor descriptor = parentTag != null ? parentTag.getDescriptor() : null;
if (descriptor instanceof JavaFxPropertyElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof PsiField) {
final PsiType type = ((PsiField)declaration).getType();
final PsiType collectionItemType = GenericsHighlightUtil.getCollectionItemType(type, myPsiClass.getResolveScope());
if (collectionItemType != null && PsiPrimitiveType.getUnboxedType(collectionItemType) == null) {
final PsiClass baseClass = PsiUtil.resolveClassInType(collectionItemType);
if (baseClass != null) {
final String qualifiedName = baseClass.getQualifiedName();
if (qualifiedName != null && !Comparing.strEqual(qualifiedName, CommonClassNames.JAVA_LANG_STRING)) {
if (!InheritanceUtil.isInheritor(myPsiClass, qualifiedName)) {
host.addMessage(context.getNavigationElement(),
"Unable to coerce " + HighlightUtil.formatClass(myPsiClass)+ " to " + qualifiedName, ValidationHost.ErrorType.ERROR);
}
}
}
}
}
}
}
}
}
@@ -66,7 +66,7 @@ abstract class JavaFxImportClassFix extends ImportClassFixBase<XmlTag, JavaFxTag
@Override
protected boolean isAccessible(PsiMember member, XmlTag reference) {
return true;
return member instanceof PsiClass && JavaFxPsiUtil.isClassAcceptable(reference.getParentTag(), (PsiClass)member) == null;
}
@Override
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import java.lang.Double?>
<GridPane xmlns:fx="http://javafx.com/fxml" alignment="center">
<<error descr="Unable to coerce java.lang.Double to javafx.scene.Node">Double</error> />
</GridPane>
@@ -0,0 +1,2 @@
package foo;
public class TextField {}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<children>
<Text<caret>Field/>
</children>
</GridPane>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.TextField?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<children>
<TextField/>
</children>
</GridPane>