/* * 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); } }