blob: 6864853fe476cb7e7654ea7fa4ad36c23133578e [file] [log] [blame]
/*
* Copyright 2000-2012 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 com.intellij.android.designer.inspection;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.TextFormat;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.propertyTable.IXmlAttributeLocator;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.SuppressQuickFix;
import com.intellij.codeInspection.ex.DisableInspectionToolAction;
import com.intellij.codeInspection.ex.EditInspectionToolsSettingsAction;
import com.intellij.designer.model.*;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.inspections.lint.*;
import javax.swing.*;
import java.util.List;
import static com.android.tools.lint.detector.api.TextFormat.RAW;
import static com.android.tools.lint.detector.api.TextFormat.TEXT;
/**
* @author Alexander Lobas
*/
public final class ErrorAnalyzer {
public static void load(final Project project, final XmlFile xmlFile, RadComponent rootComponent, ProgressIndicator progress) {
RadComponent.clearErrors(rootComponent);
AndroidLintExternalAnnotator annotator = new AndroidLintExternalAnnotator();
State state = annotator.collectInformation(xmlFile);
if (state != null) {
state = annotator.doAnnotate(state);
for (ProblemData problemData : state.getProblems()) {
Issue issue = problemData.getIssue();
String message = RAW.convertTo(problemData.getMessage(), TEXT);
TextRange range = problemData.getTextRange();
if (range.getStartOffset() == range.getEndOffset()) {
continue;
}
Pair<AndroidLintInspectionBase, HighlightDisplayLevel> pair =
AndroidLintUtil.getHighlighLevelAndInspection(project, issue, xmlFile);
if (pair == null) {
continue;
}
AndroidLintInspectionBase inspection = pair.getFirst();
if (inspection != null) {
HighlightDisplayKey key = HighlightDisplayKey.find(inspection.getShortName());
if (key != null) {
final PsiElement startElement = xmlFile.findElementAt(range.getStartOffset());
final PsiElement endElement = xmlFile.findElementAt(range.getEndOffset() - 1);
if (startElement != null && endElement != null && !inspection.isSuppressedFor(startElement)) {
Pair<RadComponent, String> componentInfo = findComponent(rootComponent, startElement);
ErrorInfo errorInfo = new ErrorInfo(message, componentInfo.second, pair.getSecond());
RadComponent.addError(componentInfo.first, errorInfo);
Icon icon =
errorInfo.getLevel() == HighlightDisplayLevel.ERROR ? AllIcons.Actions.QuickfixBulb : AllIcons.Actions.IntentionBulb;
List<QuickFix> designerFixes = errorInfo.getQuickFixes();
for (final AndroidLintQuickFix fix : inspection.getQuickFixes(startElement, endElement, message)) {
if (fix.isApplicable(startElement, endElement, AndroidQuickfixContexts.DesignerContext.TYPE)) {
designerFixes.add(new QuickFix(fix.getName(), icon) {
@Override
public void run() {
fix.apply(startElement, endElement, AndroidQuickfixContexts.DesignerContext.getInstance());
}
});
}
}
for (final IntentionAction intention : inspection.getIntentions(startElement, endElement)) {
designerFixes.add(new QuickFix(intention.getText(), icon) {
@Override
public void run() {
intention.invoke(project, null, xmlFile);
}
});
}
final DisableInspectionToolAction disableAction = new DisableInspectionToolAction(key);
designerFixes.add(new QuickFix("Disable inspection", disableAction.getIcon(0)) {
@Override
public void run() {
disableAction.invoke(project, null, xmlFile);
}
});
final EditInspectionToolsSettingsAction editSettingsAction = new EditInspectionToolsSettingsAction(key);
designerFixes
.add(new QuickFix("Edit '" + inspection.getDisplayName() + "' inspection settings", editSettingsAction.getIcon(0)) {
@Override
public void run() {
editSettingsAction.invoke(project, null, xmlFile);
}
});
SuppressQuickFix[] suppressActions = inspection.getBatchSuppressActions(startElement);
if (suppressActions != null) {
for (final SuppressQuickFix action : suppressActions) {
Icon icon1 = action instanceof Iconable ? ((Iconable)action).getIcon(0) : null;
designerFixes.add(new QuickFix(action.getName(), icon1) {
@Override
public void run() {
action.applyFix(project, InspectionManager.getInstance(project).createProblemDescriptor(startElement,"", (LocalQuickFix)null,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,false));
}
});
}
}
}
}
}
}
}
}
private static Pair<RadComponent, String> findComponent(final RadComponent rootComponent, PsiElement element) {
final Pair<XmlTag, XmlAttribute> tagInfo = extractTag(element);
if (tagInfo.first == null) {
return new Pair<RadComponent, String>(rootComponent, null);
}
final RadComponent[] result = new RadComponent[]{rootComponent};
rootComponent.accept(new RadComponentVisitor() {
@Override
public boolean visit(RadComponent component) {
if (tagInfo.first == ((RadViewComponent)component).getTag()) {
if (component == rootComponent && rootComponent.getChildren().size() == 1) {
result[0] = rootComponent.getChildren().get(0);
}
else {
result[0] = component;
}
return false;
}
return true;
}
@Override
public void endVisit(RadComponent component) {
}
}, true);
String propertyName = null;
if (tagInfo.second != null && result[0] != rootComponent) {
RadViewComponent component = (RadViewComponent)result[0];
for (Property property : component.getProperties()) {
if (((IXmlAttributeLocator)property).checkAttribute(component, tagInfo.second)) {
propertyName = property.getName();
break;
}
}
}
return Pair.create(result[0], propertyName);
}
private static Pair<XmlTag, XmlAttribute> extractTag(PsiElement element) {
XmlTag tag = null;
XmlAttribute attribute = null;
while (element != null) {
if (element instanceof XmlAttribute) {
attribute = (XmlAttribute)element;
}
if (element instanceof XmlTag) {
tag = (XmlTag)element;
break;
}
element = element.getParent();
}
return Pair.create(tag, attribute);
}
}