/* * Copyright 2000-2015 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.psi.stubs; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; 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)) { final FileViewProvider viewProvider = candidatePsiFile.getViewProvider(); final PsiFile stubBindingRoot = viewProvider.getStubBindingRoot(); if (stubBindingRoot instanceof PsiFileWithStubSupport) { psiFile = (PsiFileWithStubSupport)stubBindingRoot; stubTree = psiFile.getStubTree(); if (stubTree == null && psiFile instanceof PsiFileImpl) { IStubFileElementType elementType = ((PsiFileImpl)psiFile).getElementTypeForStubBuilder(); if (elementType != null) { stubTree = ((PsiFileImpl)psiFile).calcStubTree(); } else { customStubs = true; if (BinaryFileStubBuilders.INSTANCE.forFileType(psiFile.getFileType()) == null) { LOG.error("unable to get stub builder for " + psiFile.getFileType() + ", " + StubTreeLoader.getFileViewProviderMismatchDiagnostics(viewProvider) ); } } } } } 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)) { if (!skipOnErrors && !requiredClass.isInstance(psiFile)) { inconsistencyDetected(objectStubTree, psiFile); return true; } return processor.process((Psi)psiFile); // e.g. dom indices } stubTree = (StubTree)objectStubTree; final List<StubElement<?>> plained = stubTree.getPlainListFromAllRoots(); 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; } ProgressManager.checkCanceled(); // potentially list can be very-very large 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)((PsiFileImpl)psiFile).getElementTypeForStubBuilder().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.getPlainListFromAllRoots(); 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(@NotNull ObjectStubTree stubTree, @NotNull PsiFileWithStubSupport psiFile) { LOG.error(StubTreeLoader.getInstance().stubTreeAndIndexDoNotMatch("PSI and index do not match.", stubTree, psiFile)); onInternalError(psiFile.getVirtualFile()); } protected abstract void onInternalError(VirtualFile file); }