blob: 83bd87271fc4a3d6d5171d9183e58323465b03cc [file] [log] [blame]
package com.intellij.psi.stubs;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiPlainTextFile;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.PsiFileWithStubSupport;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Author: dmitrylomov
*/
public abstract class StubProcessingHelperBase {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.StubProcessingHelperBase");
private static IElementType stubType(@NotNull final StubElement<?> stub) {
if (stub instanceof PsiFileStub) {
return ((PsiFileStub)stub).getType();
}
return stub.getStubType();
}
public <Psi extends PsiElement> boolean processStubsInFile(@NotNull final Project project,
@NotNull final VirtualFile file,
@NotNull StubIdList value,
@NotNull final Processor<? super Psi> processor,
@NotNull Class<Psi> requiredClass) {
return processStubsInFile(project, file, value, processor, requiredClass, false);
}
public <Psi extends PsiElement> boolean processStubsInFile(@NotNull final Project project,
@NotNull final VirtualFile file,
@NotNull StubIdList value,
@NotNull final Processor<? super Psi> processor,
@NotNull Class<Psi> requiredClass, final boolean skipOnErrors) {
StubTree stubTree = null;
PsiFile candidatePsiFile = PsiManager.getInstance(project).findFile(file);
PsiFileWithStubSupport psiFile = null;
boolean customStubs = false;
if (candidatePsiFile != null && !(candidatePsiFile instanceof PsiPlainTextFile)) {
candidatePsiFile = candidatePsiFile.getViewProvider().getStubBindingRoot();
if (candidatePsiFile instanceof PsiFileWithStubSupport) {
psiFile = (PsiFileWithStubSupport)candidatePsiFile;
stubTree = psiFile.getStubTree();
if (stubTree == null && psiFile instanceof PsiFileImpl) {
IElementType contentElementType = ((PsiFileImpl)psiFile).getContentElementType();
if (contentElementType instanceof IStubFileElementType) {
stubTree = ((PsiFileImpl)psiFile).calcStubTree();
}
else {
customStubs = true;
assert BinaryFileStubBuilders.INSTANCE.forFileType(psiFile.getFileType()) != null : "unable to get stub builder for " + psiFile.getFileType();
}
}
}
}
if (stubTree == null && psiFile == null) {
return true;
}
if (stubTree == null) {
ObjectStubTree objectStubTree = StubTreeLoader.getInstance().readFromVFile(project, file);
if (objectStubTree == null) {
return true;
}
if (customStubs && !(objectStubTree instanceof StubTree)) {
return processor.process((Psi)psiFile); // e.g. dom indices
}
stubTree = (StubTree)objectStubTree;
final List<StubElement<?>> plained = stubTree.getPlainList();
for (int i = 0, size = value.size(); i < size; i++) {
final int stubTreeIndex = value.get(i);
if (stubTreeIndex >= plained.size()) {
if (!skipOnErrors)
onInternalError(file);
break;
}
final StubElement<?> stub = plained.get(stubTreeIndex);
PsiUtilCore.ensureValid(psiFile);
final ASTNode tree = psiFile.findTreeForStub(stubTree, stub);
if (tree != null) {
if (tree.getElementType() == stubType(stub)) {
Psi psi = (Psi)tree.getPsi();
PsiUtilCore.ensureValid(psi);
if (!skipOnErrors && !requiredClass.isInstance(psi)) {
inconsistencyDetected(stubTree, psiFile);
break;
}
if (!processor.process(psi)) return false;
}
else if (!skipOnErrors) {
String persistedStubTree = ((PsiFileStubImpl)stubTree.getRoot()).printTree();
String stubTreeJustBuilt =
((PsiFileStubImpl)((IStubFileElementType)((PsiFileImpl)psiFile).getContentElementType()).getBuilder()
.buildStubTree(psiFile)).printTree();
StringBuilder builder = new StringBuilder();
builder.append("Oops\n");
builder.append("Recorded stub:-----------------------------------\n");
builder.append(persistedStubTree);
builder.append("\nAST built stub: ------------------------------------\n");
builder.append(stubTreeJustBuilt);
builder.append("\n");
LOG.info(builder.toString());
onInternalError(file);
}
}
}
}
else {
final List<StubElement<?>> plained = stubTree.getPlainList();
for (int i = 0, size = value.size(); i < size; i++) {
final int stubTreeIndex = value.get(i);
if (stubTreeIndex >= plained.size()) {
if (!skipOnErrors) {
inconsistencyDetected(stubTree, psiFile);
}
break;
}
Psi psi = (Psi)plained.get(stubTreeIndex).getPsi();
PsiUtilCore.ensureValid(psi);
if (!skipOnErrors && !requiredClass.isInstance(psi)) {
inconsistencyDetected(stubTree, psiFile);
break;
}
if (!processor.process(psi)) return false;
}
}
return true;
}
private void inconsistencyDetected(StubTree stubTree, PsiFileWithStubSupport psiFile) {
LOG.error(stubTreeAndIndexDoNotMatch(stubTree, psiFile));
onInternalError(psiFile.getVirtualFile());
}
/***
* Returns a message to log when stub tree and index do not match
*/
protected abstract Object stubTreeAndIndexDoNotMatch(StubTree stubTree, PsiFileWithStubSupport psiFile);
protected abstract void onInternalError(VirtualFile file);
}