javafx: make field ref unresolvable when controller is set in .fxml; add create field quick fix (IDEA-103198)

(cherry picked from commit b16a973deb71e6d063f0e763cd4e15b6d7983a2f)
This commit is contained in:
anna
2013-03-15 22:04:14 +01:00
parent 62437d4469
commit f649c90a65
7 changed files with 141 additions and 8 deletions
@@ -66,8 +66,6 @@ public class CreateFieldFromUsageFix extends CreateVarFromUsageFix {
}
while (parentClass instanceof PsiAnonymousClass);
final PsiFile targetFile = targetClass.getContainingFile();
ExpectedTypeInfo[] expectedTypes = CreateFromUsageUtils.guessExpectedTypes(myReferenceExpression, false);
String fieldName = myReferenceExpression.getReferenceName();
@@ -94,10 +92,20 @@ public class CreateFieldFromUsageFix extends CreateVarFromUsageFix {
setupVisibility(parentClass, targetClass, field.getModifierList());
createFieldFromUsageTemplate(targetClass, project, expectedTypes, field, createConstantField(), myReferenceExpression);
}
public static void createFieldFromUsageTemplate(final PsiClass targetClass,
final Project project,
final ExpectedTypeInfo[] expectedTypes,
final PsiField field,
final boolean createConstantField,
final PsiElement context) {
final PsiFile targetFile = targetClass.getContainingFile();
final Editor newEditor = positionCursor(project, targetFile, field);
if (newEditor == null) return;
Template template =
CreateFieldFromUsageHelper.setupTemplate(field, expectedTypes, targetClass, newEditor, myReferenceExpression, createConstantField());
CreateFieldFromUsageHelper.setupTemplate(field, expectedTypes, targetClass, newEditor, context, createConstantField);
startTemplate(newEditor, template, project, new TemplateEditingAdapter() {
@Override
@@ -40,6 +40,10 @@ public class JavaFXQuickfixTest extends DaemonAnalyzerTestCase {
doTest("Create Method 'void bar(ActionEvent)'", true);
}
public void testCreateField() throws Exception {
doTest("Create Field 'btn'", true);
}
private void doTest(final String actionName) throws Exception {
doTest(actionName, false);
}
@@ -9,6 +9,7 @@
<xml.schemaProvider implementation="org.jetbrains.plugins.javaFX.fxml.JavaFXSchemaHandler"/>
<psi.referenceContributor implementation="org.jetbrains.plugins.javaFX.fxml.refs.FxmlReferencesContributor"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxEventHandlerReference$JavaFxUnresolvedReferenceHandlerQuickfixProvider"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxFieldIdReferenceProvider$JavaFxUnresolvedReferenceHandlerQuickfixProvider"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxTagNameReference$JavaFxUnresolvedTagRefsProvider"/>
<multiHostInjector implementation="org.jetbrains.plugins.javaFX.fxml.ScriptLanguageInjector"/>
<annotator language="XML" implementationClass="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxAnnotator"/>
@@ -15,28 +15,48 @@
*/
package org.jetbrains.plugins.javaFX.fxml.refs;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFieldFromUsageFix;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFieldFromUsageHelper;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ProcessingContext;
import com.intellij.util.VisibilityUtil;
import com.intellij.xml.XmlElementDescriptor;
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 org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxClassBackedElementDescriptor;
import java.util.ArrayList;
import java.util.List;
/**
* User: anna
* Date: 1/17/13
*/
* User: anna
* Date: 1/17/13
*/
class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferenceProvider {
@Override
protected PsiReference[] getReferencesByElement(@NotNull final PsiClass aClass,
final XmlAttributeValue xmlAttributeValue,
ProcessingContext context) {
final PsiField field = aClass.findFieldByName(xmlAttributeValue.getValue(), false);
return new PsiReference[] {new JavaFxControllerFieldRef(xmlAttributeValue, field, aClass)};
return new PsiReference[]{new JavaFxControllerFieldRef(xmlAttributeValue, field, aClass)};
}
public static class JavaFxControllerFieldRef extends PsiReferenceBase<XmlAttributeValue> {
@@ -54,7 +74,19 @@ class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferenceProvi
@Nullable
@Override
public PsiElement resolve() {
return myField != null ? myField : myXmlAttributeValue;
if (myField != null) {
return myField;
}
else {
if (myAClass != null) {
final XmlFile xmlFile = (XmlFile)myXmlAttributeValue.getContainingFile();
final XmlTag rootTag = xmlFile.getRootTag();
if (rootTag != null && !FxmlConstants.FX_ROOT.equals(rootTag.getName())) {
return null;
}
}
return myXmlAttributeValue;
}
}
@NotNull
@@ -72,4 +104,81 @@ class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferenceProvi
return ArrayUtil.toObjectArray(fieldsToSuggest);
}
}
public static class JavaFxUnresolvedReferenceHandlerQuickfixProvider extends UnresolvedReferenceQuickFixProvider<JavaFxControllerFieldRef> {
@Override
public void registerFixes(final JavaFxControllerFieldRef ref, final QuickFixActionRegistrar registrar) {
if (ref.myAClass != null && ref.myField == null) {
final PsiClass fieldClass = CreateFieldFix.checkContext(ref.myXmlAttributeValue);
if (fieldClass != null) {
registrar.register(new CreateFieldFix(ref, fieldClass));
}
}
}
@NotNull
@Override
public Class<JavaFxControllerFieldRef> getReferenceClass() {
return JavaFxControllerFieldRef.class;
}
private static class CreateFieldFix extends PsiElementBaseIntentionAction {
private final PsiClass myFieldClass;
private final PsiClass myClass;
private final String myCanonicalText;
public CreateFieldFix(JavaFxControllerFieldRef ref, PsiClass fieldClass) {
myFieldClass = fieldClass;
myClass = ref.myAClass;
myCanonicalText = ref.getCanonicalText();
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
if (!CodeInsightUtilBase.prepareFileForWrite(myClass.getContainingFile())) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiField field = factory.createField(myCanonicalText, PsiType.INT);
VisibilityUtil.setVisibility(field.getModifierList(), PsiModifier.PUBLIC);
field = CreateFieldFromUsageHelper.insertField(myClass, field, element);
final PsiClassType fieldType = factory.createType(myFieldClass);
final ExpectedTypeInfo[] types = {new ExpectedTypeInfoImpl(fieldType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, 0, fieldType, TailType.NONE)};
CreateFieldFromUsageFix.createFieldFromUsageTemplate(myClass, project, types, field, false, element);
}
protected static PsiClass checkContext(final XmlAttributeValue attributeValue) {
if (attributeValue == null) return null;
final PsiElement parent = attributeValue.getParent();
if (parent instanceof XmlAttribute){
final XmlTag tag = ((XmlAttribute)parent).getParent();
if (tag != null) {
final XmlElementDescriptor descriptor = tag.getDescriptor();
if (descriptor instanceof JavaFxClassBackedElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof PsiClass) {
return (PsiClass)declaration;
}
}
}
}
return null;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
setText(QuickFixBundle.message("create.field.from.usage.text", myCanonicalText));
return element.isValid();
}
@NotNull
@Override
public String getFamilyName() {
return QuickFixBundle.message("create.field.from.usage.family");
}
}
}
}
@@ -0,0 +1 @@
public class CreateControllerMethod {}
@@ -0,0 +1,5 @@
import javafx.scene.control.Button;
public class CreateControllerMethod {
public Button btn;
}
@@ -0,0 +1,5 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="CreateControllerMethod">
<Button fx:id="b<caret>tn"/>
</GridPane>