blob: 13f113d540b6d3433a36cc4f43a35ae33eb297d1 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.TailType;
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.codeInspection.*;
import com.intellij.lang.LanguageNamesValidation;
import com.intellij.lang.refactoring.NamesValidator;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
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.VisibilityUtil;
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.JavaFxClassBackedElementDescriptor;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxDefaultPropertyElementDescriptor;
import org.jetbrains.plugins.javaFX.fxml.refs.JavaFxFieldIdReferenceProvider;
/**
* User: anna
*/
public class JavaFxUnresolvedFxIdReferenceInspection extends XmlSuppressableInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder,
final boolean isOnTheFly,
@NotNull LocalInspectionToolSession session) {
return new XmlElementVisitor() {
@Override
public void visitXmlFile(XmlFile file) {
if (!JavaFxFileTypeFactory.isFxml(file)) return;
super.visitXmlFile(file);
}
@Override
public void visitXmlAttribute(XmlAttribute attribute) {
if (FxmlConstants.FX_ID.equals(attribute.getName())) {
final XmlAttributeValue valueElement = attribute.getValueElement();
if (valueElement != null && valueElement.getTextLength() > 0) {
final PsiClass controllerClass = JavaFxPsiUtil.getControllerClass(attribute.getContainingFile());
if (controllerClass != null) {
final PsiReference reference = valueElement.getReference();
if (reference instanceof JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef && ((JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef)reference).isUnresolved()) {
final PsiClass fieldClass =
checkContext(((JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef)reference).getXmlAttributeValue());
if (fieldClass != null) {
final String text = reference.getCanonicalText();
final NamesValidator namesValidator = LanguageNamesValidation.INSTANCE.forLanguage(fieldClass.getLanguage());
boolean validName = namesValidator != null && namesValidator.isIdentifier(text, fieldClass.getProject());
holder.registerProblem(reference.getElement(), reference.getRangeInElement(), "Unresolved fx:id reference",
isOnTheFly && validName ? new LocalQuickFix[]{new CreateFieldFromUsageQuickFix(text)} : LocalQuickFix.EMPTY_ARRAY);
}
}
}
}
}
}
};
}
protected static PsiClass checkContext(final XmlAttributeValue attributeValue) {
if (attributeValue == null) return null;
final PsiElement parent = attributeValue.getParent();
if (parent instanceof XmlAttribute) {
return checkClass(((XmlAttribute)parent).getParent());
}
return null;
}
private static PsiClass checkClass(XmlTag tag) {
if (tag != null) {
final XmlElementDescriptor descriptor = tag.getDescriptor();
if (descriptor instanceof JavaFxClassBackedElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof PsiClass) {
return (PsiClass)declaration;
}
} else if (descriptor instanceof JavaFxDefaultPropertyElementDescriptor) {
final XmlTag includedRoot = JavaFxDefaultPropertyElementDescriptor.getIncludedRoot(tag);
if (includedRoot != null && !includedRoot.equals(tag)) {
return checkClass(includedRoot);
}
}
}
return null;
}
private static class CreateFieldFromUsageQuickFix implements LocalQuickFix {
private final String myCanonicalName;
private CreateFieldFromUsageQuickFix(String canonicalName) {
myCanonicalName = canonicalName;
}
@NotNull
@Override
public String getName() {
return QuickFixBundle.message("create.field.from.usage.text", myCanonicalName);
}
@NotNull
@Override
public String getFamilyName() {
return QuickFixBundle.message("create.field.from.usage.family");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement psiElement = descriptor.getPsiElement();
final XmlAttributeValue attrValue = PsiTreeUtil.getParentOfType(psiElement, XmlAttributeValue.class, false);
assert attrValue != null;
final JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef reference =
(JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef)attrValue.getReference();
assert reference != null;
final PsiClass targetClass = reference.getAClass();
if (!FileModificationService.getInstance().prepareFileForWrite(targetClass.getContainingFile())) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiField field = factory.createField(reference.getCanonicalText(), PsiType.INT);
VisibilityUtil.setVisibility(field.getModifierList(), PsiModifier.PUBLIC);
field = CreateFieldFromUsageHelper.insertField(targetClass, field, psiElement);
final PsiClassType fieldType = factory.createType(checkContext(reference.getXmlAttributeValue()));
final ExpectedTypeInfo[] types = {new ExpectedTypeInfoImpl(fieldType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, fieldType, TailType.NONE,
null, ExpectedTypeInfoImpl.NULL)};
CreateFieldFromUsageFix.createFieldFromUsageTemplate(targetClass, project, types, field, false, psiElement);
}
}
}