| /* |
| * 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.psi.stubs; |
| |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ModalityState; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.fileEditor.FileDocumentManager; |
| import com.intellij.openapi.project.DumbService; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.impl.PsiManagerEx; |
| import com.intellij.psi.impl.source.PsiFileImpl; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.util.indexing.*; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.IOException; |
| import java.util.List; |
| |
| /** |
| * @author yole |
| */ |
| public class StubTreeLoaderImpl extends StubTreeLoader { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.StubTreeLoaderImpl"); |
| |
| @Override |
| @Nullable |
| public ObjectStubTree readOrBuild(Project project, final VirtualFile vFile, @Nullable PsiFile psiFile) { |
| final ObjectStubTree fromIndices = readFromVFile(project, vFile); |
| if (fromIndices != null) { |
| return fromIndices; |
| } |
| |
| try { |
| final FileContent fc = new FileContentImpl(vFile, vFile.contentsToByteArray()); |
| fc.putUserData(IndexingDataKeys.PROJECT, project); |
| if (psiFile != null && !vFile.getFileType().isBinary()) { |
| fc.putUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY, psiFile.getViewProvider().getContents()); |
| // but don't reuse psiFile itself to avoid loading its contents. If we load AST, the stub will be thrown out anyway. |
| } |
| Stub element = StubTreeBuilder.buildStubTree(fc); |
| if (element instanceof PsiFileStub) { |
| StubTree tree = new StubTree((PsiFileStub)element); |
| tree.setDebugInfo("created from file content"); |
| return tree; |
| } |
| } |
| catch (IOException e) { |
| LOG.info(e); // content can be not cached yet, and the file can be deleted on disk already, without refresh |
| } |
| |
| return null; |
| } |
| |
| @Override |
| @Nullable |
| public ObjectStubTree readFromVFile(Project project, final VirtualFile vFile) { |
| if (DumbService.getInstance(project).isDumb()) { |
| return null; |
| } |
| |
| final int id = Math.abs(FileBasedIndex.getFileId(vFile)); |
| if (id <= 0) { |
| return null; |
| } |
| |
| boolean wasIndexedAlready = ((FileBasedIndexImpl)FileBasedIndex.getInstance()).isFileUpToDate(vFile); |
| |
| Document document = FileDocumentManager.getInstance().getCachedDocument(vFile); |
| boolean saved = document == null || !FileDocumentManager.getInstance().isDocumentUnsaved(document); |
| |
| final List<SerializedStubTree> datas = FileBasedIndex.getInstance().getValues(StubUpdatingIndex.INDEX_ID, id, GlobalSearchScope |
| .fileScope(project, vFile)); |
| final int size = datas.size(); |
| |
| if (size == 1) { |
| SerializedStubTree stubTree = datas.get(0); |
| |
| if (!stubTree.contentLengthMatches(vFile.getLength(), getCurrentTextContentLength(project, vFile, document))) { |
| //todo find another way of early stub-ast mismatch prevention |
| //return processError(vFile, |
| // "Outdated stub in index: " + StubUpdatingIndex.getIndexingStampInfo(vFile) + |
| // ", docSaved=" + saved + |
| // ", queried at " + vFile.getTimeStamp(), |
| // null); |
| } |
| |
| Stub stub; |
| try { |
| stub = stubTree.getStub(false); |
| } |
| catch (SerializerNotFoundException e) { |
| return processError(vFile, "No stub serializer: " + vFile.getPresentableUrl() + ": " + e.getMessage(), e); |
| } |
| ObjectStubTree tree = stub instanceof PsiFileStub ? new StubTree((PsiFileStub)stub) : new ObjectStubTree((ObjectStubBase)stub, true); |
| tree.setDebugInfo("created from index"); |
| return tree; |
| } |
| else if (size != 0) { |
| return processError(vFile, "Twin stubs: " + vFile.getPresentableUrl() + " has " + size + " stub versions. Should only have one. id=" + id, |
| null); |
| } |
| |
| return null; |
| } |
| |
| private static int getCurrentTextContentLength(Project project, VirtualFile vFile, Document document) { |
| if (vFile.getFileType().isBinary()) { |
| return -1; |
| } |
| PsiFile psiFile = ((PsiManagerEx)PsiManager.getInstance(project)).getFileManager().getCachedPsiFile(vFile); |
| if (psiFile instanceof PsiFileImpl && ((PsiFileImpl)psiFile).isContentsLoaded()) { |
| return psiFile.getTextLength(); |
| } |
| |
| if (document != null) { |
| return document.getTextLength(); |
| } |
| return -1; |
| } |
| |
| private static ObjectStubTree processError(final VirtualFile vFile, String message, @Nullable Exception e) { |
| LOG.error(message, e); |
| |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| final Document doc = FileDocumentManager.getInstance().getCachedDocument(vFile); |
| if (doc != null) { |
| FileDocumentManager.getInstance().saveDocument(doc); |
| } |
| } |
| }, ModalityState.NON_MODAL); |
| |
| FileBasedIndex.getInstance().requestReindex(vFile); |
| return null; |
| } |
| |
| @Override |
| public void rebuildStubTree(VirtualFile virtualFile) { |
| FileBasedIndex.getInstance().requestReindex(virtualFile); |
| } |
| |
| @Override |
| public long getStubTreeTimestamp(VirtualFile vFile) { |
| return IndexingStamp.getIndexStamp(vFile, StubUpdatingIndex.INDEX_ID); |
| } |
| |
| @Override |
| public boolean canHaveStub(VirtualFile file) { |
| return StubUpdatingIndex.canHaveStub(file); |
| } |
| } |