mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
javafx: simple expression binding (IDEA-100322)
This commit is contained in:
@@ -22,7 +22,7 @@ import org.jetbrains.annotations.NonNls;
|
||||
* Date: 1/16/13
|
||||
*/
|
||||
public class JavaFxCommonClassNames {
|
||||
@NonNls public static final String JAVAFX_BEANS_PROPERTY_PROPERTY = "javafx.beans.property.Property";
|
||||
@NonNls public static final String JAVAFX_BEANS_PROPERTY = "javafx.beans.property.Property";
|
||||
@NonNls public static final String JAVAFX_ANCHOR_PANE = "javafx.scene.layout.AnchorPane";
|
||||
@NonNls public static final String JAVAFX_EVENT = "javafx.event.Event";
|
||||
@NonNls public static final String JAVAFX_BEANS_DEFAULT_PROPERTY = "javafx.beans.DefaultProperty";
|
||||
|
||||
@@ -296,4 +296,27 @@ public class JavaFxPsiUtil {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public static boolean isExpressionBinding(String value) {
|
||||
if (!value.startsWith("$")) return false;
|
||||
value = value.substring(1);
|
||||
return value.startsWith("{") && value.endsWith("}") && value.contains(".");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getPropertyType(final PsiType type, final Project project) {
|
||||
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
||||
if (psiClass != null) {
|
||||
final PsiClass propertyClass = JavaPsiFacade.getInstance(project).findClass(JavaFxCommonClassNames.JAVAFX_BEANS_PROPERTY,
|
||||
GlobalSearchScope.allScope(project));
|
||||
if (propertyClass != null) {
|
||||
final PsiSubstitutor substitutor =
|
||||
TypeConversionUtil.getClassSubstitutor(propertyClass, psiClass, PsiSubstitutor.EMPTY);
|
||||
if (substitutor != null) {
|
||||
return substitutor.substitute(propertyClass.getTypeParameters()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@ public class JavaFxClassBackedElementDescriptor implements XmlElementDescriptor,
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC)) continue;
|
||||
final PsiType fieldType = field.getType();
|
||||
if (!JavaFxPsiUtil.isReadOnly(field.getName(), context) &&
|
||||
InheritanceUtil.isInheritor(fieldType, JavaFxCommonClassNames.JAVAFX_BEANS_PROPERTY_PROPERTY) ||
|
||||
InheritanceUtil.isInheritor(fieldType, JavaFxCommonClassNames.JAVAFX_BEANS_PROPERTY) ||
|
||||
fieldType.equalsToText(CommonClassNames.JAVA_LANG_STRING) ||
|
||||
includeListProperties && GenericsHighlightUtil.getCollectionItemType(field.getType(), myPsiClass.getResolveScope()) != null) {
|
||||
children.add(factory.fun(field));
|
||||
|
||||
@@ -59,15 +59,17 @@ public class JavaFxAnnotator implements Annotator {
|
||||
if (!JavaFxFileTypeFactory.isFxml(containingFile)) return;
|
||||
if (element instanceof XmlAttributeValue) {
|
||||
final PsiReference[] references = element.getReferences();
|
||||
for (PsiReference reference : references) {
|
||||
final PsiElement resolve = reference.resolve();
|
||||
if (resolve instanceof PsiMember) {
|
||||
if (!JavaFxPsiUtil.isVisibleInFxml((PsiMember)resolve)) {
|
||||
final String symbolPresentation = "'" + SymbolPresentationUtil.getSymbolPresentableText(resolve) + "'";
|
||||
final Annotation annotation = holder.createErrorAnnotation(element,
|
||||
symbolPresentation + (resolve instanceof PsiClass ? " should be public" : " should be public or annotated with @FXML"));
|
||||
if (!(resolve instanceof PsiClass)) {
|
||||
annotation.registerUniversalFix(new AddAnnotationFix(JavaFxCommonClassNames.JAVAFX_FXML_ANNOTATION, (PsiMember)resolve, ArrayUtil.EMPTY_STRING_ARRAY), null, null);
|
||||
if (!JavaFxPsiUtil.isExpressionBinding(((XmlAttributeValue)element).getValue())) {
|
||||
for (PsiReference reference : references) {
|
||||
final PsiElement resolve = reference.resolve();
|
||||
if (resolve instanceof PsiMember) {
|
||||
if (!JavaFxPsiUtil.isVisibleInFxml((PsiMember)resolve)) {
|
||||
final String symbolPresentation = "'" + SymbolPresentationUtil.getSymbolPresentableText(resolve) + "'";
|
||||
final Annotation annotation = holder.createErrorAnnotation(element,
|
||||
symbolPresentation + (resolve instanceof PsiClass ? " should be public" : " should be public or annotated with @FXML"));
|
||||
if (!(resolve instanceof PsiClass)) {
|
||||
annotation.registerUniversalFix(new AddAnnotationFix(JavaFxCommonClassNames.JAVAFX_FXML_ANNOTATION, (PsiMember)resolve, ArrayUtil.EMPTY_STRING_ARRAY), null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+102
-15
@@ -16,18 +16,24 @@
|
||||
package org.jetbrains.plugins.javaFX.fxml.refs;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.psi.xml.XmlAttribute;
|
||||
import com.intellij.psi.xml.XmlAttributeValue;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.xml.XmlAttributeDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.javaFX.fxml.FxmlConstants;
|
||||
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -57,24 +63,105 @@ class JavaFxComponentIdReferenceProvider extends PsiReferenceProvider {
|
||||
}
|
||||
});
|
||||
|
||||
return new PsiReference[]{new PsiReferenceBase<XmlAttributeValue>(xmlAttributeValue) {
|
||||
@Override
|
||||
public TextRange getRangeInElement() {
|
||||
final TextRange rangeInElement = super.getRangeInElement();
|
||||
return startsWithDollar ? new TextRange(rangeInElement.getStartOffset() + 1, rangeInElement.getEndOffset()) : rangeInElement;
|
||||
}
|
||||
if (JavaFxPsiUtil.isExpressionBinding(value)) {
|
||||
final String expressionText = referencesId.substring(1, referencesId.length() - 1);
|
||||
final String newId = StringUtil.getPackageName(expressionText);
|
||||
final String fieldRef = StringUtil.getShortName(expressionText);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return fileIds.get(referencesId);
|
||||
final JavaFxIdReferenceBase idReferenceBase = new JavaFxIdReferenceBase(xmlAttributeValue, fileIds, newId);
|
||||
final TextRange range = idReferenceBase.getRangeInElement();
|
||||
final int startOffset = range.getStartOffset() + 2;
|
||||
final int endOffset = startOffset + newId.length();
|
||||
idReferenceBase.setRangeInElement(new TextRange(startOffset, endOffset));
|
||||
if (fileIds.containsKey(newId)) {
|
||||
final XmlAttributeValue attributeValue = fileIds.get(newId);
|
||||
final PsiClass tagClass = JavaFxPsiUtil.getTagClass(attributeValue);
|
||||
if (tagClass != null) {
|
||||
final JavaFxExpressionReferenceBase referenceBase = new JavaFxExpressionReferenceBase(xmlAttributeValue, tagClass, fieldRef);
|
||||
final TextRange textRange = referenceBase.getRangeInElement();
|
||||
referenceBase.setRangeInElement(new TextRange(endOffset + 1, textRange.getEndOffset() - 1));
|
||||
return new PsiReference[] {idReferenceBase, referenceBase};
|
||||
}
|
||||
}
|
||||
return new PsiReference[] {idReferenceBase};
|
||||
}
|
||||
final JavaFxIdReferenceBase idReferenceBase = new JavaFxIdReferenceBase(xmlAttributeValue, fileIds, referencesId);
|
||||
if (startsWithDollar) {
|
||||
final TextRange rangeInElement = idReferenceBase.getRangeInElement();
|
||||
idReferenceBase.setRangeInElement(new TextRange(rangeInElement.getStartOffset() + 1, rangeInElement.getEndOffset()));
|
||||
}
|
||||
return new PsiReference[]{idReferenceBase};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
return ArrayUtil.toStringArray(fileIds.keySet());
|
||||
private static class JavaFxIdReferenceBase extends PsiReferenceBase<XmlAttributeValue> {
|
||||
private final Map<String, XmlAttributeValue> myFileIds;
|
||||
private final String myReferencesId;
|
||||
|
||||
public JavaFxIdReferenceBase(XmlAttributeValue xmlAttributeValue,
|
||||
Map<String, XmlAttributeValue> fileIds,
|
||||
String referencesId) {
|
||||
super(xmlAttributeValue);
|
||||
myFileIds = fileIds;
|
||||
myReferencesId = referencesId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return myFileIds.get(myReferencesId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
return ArrayUtil.toStringArray(myFileIds.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
private static class JavaFxExpressionReferenceBase extends PsiReferenceBase<XmlAttributeValue> {
|
||||
private final PsiClass myTagClass;
|
||||
private final String myFieldName;
|
||||
|
||||
public JavaFxExpressionReferenceBase(XmlAttributeValue xmlAttributeValue, PsiClass tagClass, String fieldName) {
|
||||
super(xmlAttributeValue);
|
||||
myTagClass = tagClass;
|
||||
myFieldName = fieldName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return myTagClass.findFieldByName(myFieldName, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final PsiElement parent = getElement().getParent();
|
||||
if (parent instanceof XmlAttribute) {
|
||||
final XmlAttributeDescriptor descriptor = ((XmlAttribute)parent).getDescriptor();
|
||||
if (descriptor != null) {
|
||||
final PsiElement declaration = descriptor.getDeclaration();
|
||||
if (declaration instanceof PsiField) {
|
||||
return collectProperties((PsiField)declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
private Object[] collectProperties(@NotNull PsiField psiField) {
|
||||
final PsiType type = psiField.getType();
|
||||
final PsiType propertyType = JavaFxPsiUtil.getPropertyType(type, psiField.getProject());
|
||||
final List<PsiField> objs = new ArrayList<PsiField>();
|
||||
for (PsiField field : myTagClass.getAllFields()) {
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC)) continue;
|
||||
final PsiType fieldType = field.getType();
|
||||
if (TypeConversionUtil.isAssignable(type, fieldType) || (propertyType != null && TypeConversionUtil.isAssignable(propertyType, fieldType))) {
|
||||
objs.add(field);
|
||||
}
|
||||
}
|
||||
return ArrayUtil.toObjectArray(objs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<GridPane xmlns:fx="http://javafx.com/fxml">
|
||||
<TextField fx:id="textField"/>
|
||||
<Label text="${textField.<caret>}"/>
|
||||
</GridPane>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<GridPane xmlns:fx="http://javafx.com/fxml">
|
||||
<TextField fx:id="textField"/>
|
||||
<Label text="${textField.text}"/>
|
||||
</GridPane>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<GridPane xmlns:fx="http://javafx.com/fxml">
|
||||
<TextField fx:id="textField"/>
|
||||
<Label text="${textField.text}"/>
|
||||
</GridPane>
|
||||
@@ -184,6 +184,10 @@ public class JavaFXHighlightingTest extends DaemonAnalyzerTestCase {
|
||||
doDoTest(false, false);
|
||||
}
|
||||
|
||||
public void testExpressionBinding() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
|
||||
@@ -93,6 +93,10 @@ public class JavaFxCompletionTest extends CompletionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testExpressionBinding() throws Exception {
|
||||
doTest("text");
|
||||
}
|
||||
|
||||
public void testReadOnly() throws Exception {
|
||||
configureByFile(getTestName(true) + ".fxml");
|
||||
assertTrue(myItems.length > 0);
|
||||
|
||||
Reference in New Issue
Block a user