blob: 45997b0579af0857a5a8e81b79144fa3381bdc70 [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 com.intellij.codeInspection;
import com.intellij.lang.annotation.ProblemGroup;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.Navigatable;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ProblemDescriptorBase extends CommonProblemDescriptorImpl implements ProblemDescriptor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.ProblemDescriptorImpl");
@NotNull private final SmartPsiElementPointer myStartSmartPointer;
@Nullable private final SmartPsiElementPointer myEndSmartPointer;
private final ProblemHighlightType myHighlightType;
private Navigatable myNavigatable;
private final boolean myAfterEndOfLine;
private final TextRange myTextRangeInElement;
private final boolean myShowTooltip;
private TextAttributesKey myEnforcedTextAttributes;
private int myLineNumber = -1;
private ProblemGroup myProblemGroup;
public ProblemDescriptorBase(@NotNull PsiElement startElement,
@NotNull PsiElement endElement,
@NotNull String descriptionTemplate,
LocalQuickFix[] fixes,
@NotNull ProblemHighlightType highlightType,
boolean isAfterEndOfLine,
@Nullable TextRange rangeInElement,
final boolean tooltip,
boolean onTheFly) {
super(fixes, descriptionTemplate);
myShowTooltip = tooltip;
PsiFile startContainingFile = startElement.getContainingFile();
LOG.assertTrue(startContainingFile != null && startContainingFile.isValid() || startElement.isValid(), startElement);
PsiFile endContainingFile = startElement == endElement ? startContainingFile : endElement.getContainingFile();
LOG.assertTrue(startElement == endElement || endContainingFile != null && endContainingFile.isValid() || endElement.isValid(), endElement);
assertPhysical(startElement);
if (startElement != endElement) assertPhysical(endElement);
final TextRange startElementRange = startElement.getTextRange();
LOG.assertTrue(startElementRange != null, startElement);
final TextRange endElementRange = endElement.getTextRange();
LOG.assertTrue(endElementRange != null, endElement);
if (startElementRange.getStartOffset() >= endElementRange.getEndOffset()) {
if (!(startElement instanceof PsiFile && endElement instanceof PsiFile)) {
LOG.error("Empty PSI elements should not be passed to createDescriptor. Start: " + startElement + ", end: " + endElement);
}
}
myHighlightType = highlightType;
final Project project = startContainingFile == null ? startElement.getProject() : startContainingFile.getProject();
final SmartPointerManager manager = SmartPointerManager.getInstance(project);
myStartSmartPointer = manager.createSmartPsiElementPointer(startElement, startContainingFile);
myEndSmartPointer = startElement == endElement ? null : manager.createSmartPsiElementPointer(endElement, endContainingFile);
myAfterEndOfLine = isAfterEndOfLine;
myTextRangeInElement = rangeInElement;
}
protected void assertPhysical(final PsiElement element) {
if (!element.isPhysical()) {
LOG.error("Non-physical PsiElement. Physical element is required to be able to anchor the problem in the source tree: " +
element + "; file: " + element.getContainingFile());
}
}
@Override
public PsiElement getPsiElement() {
PsiElement startElement = getStartElement();
if (myEndSmartPointer == null) {
return startElement;
}
PsiElement endElement = getEndElement();
if (startElement == endElement) {
return startElement;
}
if (startElement == null || endElement == null) return null;
return PsiTreeUtil.findCommonParent(startElement, endElement);
}
@Nullable
public TextRange getTextRangeInElement() {
return myTextRangeInElement;
}
@Override
public PsiElement getStartElement() {
return myStartSmartPointer.getElement();
}
@Override
public PsiElement getEndElement() {
return myEndSmartPointer == null ? getStartElement() : myEndSmartPointer.getElement();
}
@Override
public int getLineNumber() {
if (myLineNumber == -1) {
PsiElement psiElement = getPsiElement();
if (psiElement == null) return -1;
if (!psiElement.isValid()) return -1;
LOG.assertTrue(psiElement.isPhysical());
InjectedLanguageManager manager = InjectedLanguageManager.getInstance(psiElement.getProject());
PsiFile containingFile = manager.getTopLevelFile(psiElement);
Document document = PsiDocumentManager.getInstance(psiElement.getProject()).getDocument(containingFile);
if (document == null) return -1;
TextRange textRange = getTextRange();
if (textRange == null) return -1;
textRange = manager.injectedToHost(psiElement, textRange);
final int startOffset = textRange.getStartOffset();
final int textLength = document.getTextLength();
LOG.assertTrue(startOffset <= textLength, getDescriptionTemplate() + " at " + startOffset + ", " + textLength);
myLineNumber = document.getLineNumber(startOffset) + 1;
}
return myLineNumber;
}
@NotNull
@Override
public ProblemHighlightType getHighlightType() {
return myHighlightType;
}
@Override
public boolean isAfterEndOfLine() {
return myAfterEndOfLine;
}
@Override
public void setTextAttributes(TextAttributesKey key) {
myEnforcedTextAttributes = key;
}
public TextAttributesKey getEnforcedTextAttributes() {
return myEnforcedTextAttributes;
}
public TextRange getTextRangeForNavigation() {
TextRange textRange = getTextRange();
if (textRange == null) return null;
PsiElement element = getPsiElement();
return InjectedLanguageManager.getInstance(element.getProject()).injectedToHost(element, textRange);
}
public TextRange getTextRange() {
PsiElement startElement = getStartElement();
PsiElement endElement = myEndSmartPointer == null ? startElement : getEndElement();
if (startElement == null || endElement == null) {
return null;
}
TextRange textRange = startElement.getTextRange();
if (startElement == endElement) {
if (isAfterEndOfLine()) return new TextRange(textRange.getEndOffset(), textRange.getEndOffset());
if (myTextRangeInElement != null) {
return new TextRange(textRange.getStartOffset() + myTextRangeInElement.getStartOffset(),
textRange.getStartOffset() + myTextRangeInElement.getEndOffset());
}
return textRange;
}
return new TextRange(textRange.getStartOffset(), endElement.getTextRange().getEndOffset());
}
public Navigatable getNavigatable() {
return myNavigatable;
}
public void setNavigatable(final Navigatable navigatable) {
myNavigatable = navigatable;
}
@Override
@Nullable
public ProblemGroup getProblemGroup() {
return myProblemGroup;
}
@Override
public void setProblemGroup(@Nullable ProblemGroup problemGroup) {
myProblemGroup = problemGroup;
}
@Override
public boolean showTooltip() {
return myShowTooltip;
}
@Override
public String toString() {
PsiElement element = getPsiElement();
return ProblemDescriptorUtil.renderDescriptionMessage(this, element);
}
}