NPE (18686)
[fedora-idea.git] / platform / lang-impl / src / com / intellij / psi / MultiplePsiFilesPerDocumentFileViewProvider.java
blob655dbbdac45e0a086d7252e1c5f33f68abd586b4
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author max
20 package com.intellij.psi;
22 import com.intellij.lang.Language;
23 import com.intellij.openapi.editor.Document;
24 import com.intellij.openapi.util.TextRange;
25 import com.intellij.openapi.vfs.VirtualFile;
26 import com.intellij.psi.impl.PsiDocumentManagerImpl;
27 import com.intellij.psi.impl.SharedPsiElementImplUtil;
28 import com.intellij.psi.impl.source.PsiFileImpl;
29 import com.intellij.psi.impl.source.tree.FileElement;
30 import com.intellij.psi.templateLanguages.OuterLanguageElement;
31 import com.intellij.testFramework.LightVirtualFile;
32 import com.intellij.util.ConcurrencyUtil;
33 import com.intellij.util.ReflectionCache;
34 import com.intellij.util.containers.ConcurrentHashMap;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37 import org.jetbrains.annotations.TestOnly;
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.List;
42 import java.util.concurrent.ConcurrentMap;
44 public abstract class MultiplePsiFilesPerDocumentFileViewProvider extends SingleRootFileViewProvider {
45 private final ConcurrentMap<Language, PsiFile> myRoots = new ConcurrentHashMap<Language, PsiFile>(1, ConcurrentHashMap.DEFAULT_LOAD_FACTOR, 1);
46 private MultiplePsiFilesPerDocumentFileViewProvider myOriginal = null;
48 public MultiplePsiFilesPerDocumentFileViewProvider(PsiManager manager, VirtualFile virtualFile, boolean physical) {
49 super(manager, virtualFile, physical, Language.ANY);
52 @NotNull
53 public abstract Language getBaseLanguage();
55 @NotNull
56 public List<PsiFile> getAllFiles() {
57 final ArrayList<PsiFile> roots = new ArrayList<PsiFile>(myRoots.values());
58 final PsiFile base = myRoots.get(getBaseLanguage());
59 if (!roots.isEmpty() && roots.get(0) != base) {
60 roots.remove(base);
61 roots.add(0, base);
63 return roots;
66 protected void removeFile(final Language language) {
67 myRoots.remove(language);
70 protected PsiFile getPsiInner(final Language target) {
71 PsiFile file = myRoots.get(target);
72 if (file == null) {
73 if (isPhysical()) {
74 VirtualFile virtualFile = getVirtualFile();
75 VirtualFile parent = virtualFile.getParent();
76 if (parent != null) {
77 getManager().findDirectory(parent);
80 file = createFile(target);
81 if (file == null) return null;
82 if (myOriginal != null) {
83 final PsiFile originalFile = myOriginal.getPsi(target);
84 if (originalFile != null) {
85 ((PsiFileImpl)file).setOriginalFile(originalFile);
88 file = ConcurrencyUtil.cacheOrGet(myRoots, target, file);
90 return file;
94 public PsiFile getCachedPsi(Language target) {
95 return myRoots.get(target);
98 public FileElement[] getKnownTreeRoots() {
99 List<FileElement> files = new ArrayList<FileElement>(myRoots.size());
100 for (PsiFile file : myRoots.values()) {
101 final FileElement treeElement = ((PsiFileImpl)file).getTreeElement();
102 if (treeElement != null) {
103 files.add(treeElement);
107 return files.toArray(new FileElement[files.size()]);
110 @TestOnly
111 public void checkAllTreesEqual() {
112 Collection<PsiFile> roots = myRoots.values();
113 PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getManager().getProject());
114 documentManager.commitAllDocuments();
115 for (PsiFile root : roots) {
116 Document document = documentManager.getDocument(root);
117 PsiDocumentManagerImpl.checkConsistency(root, document);
118 assert root.getText().equals(document.getText());
122 @NotNull
123 public final MultiplePsiFilesPerDocumentFileViewProvider createCopy(final LightVirtualFile fileCopy) {
124 final MultiplePsiFilesPerDocumentFileViewProvider copy = cloneInner(fileCopy);
125 copy.myOriginal = myOriginal == null ? this : myOriginal;
126 return copy;
129 protected abstract MultiplePsiFilesPerDocumentFileViewProvider cloneInner(VirtualFile fileCopy);
131 @Nullable
132 public PsiElement findElementAt(int offset, Class<? extends Language> lang) {
133 final PsiFile mainRoot = getPsi(getBaseLanguage());
134 PsiElement ret = null;
135 for (final Language language : getLanguages()) {
136 if (!ReflectionCache.isAssignable(lang, language.getClass())) continue;
137 if (lang.equals(Language.class) && !getLanguages().contains(language)) continue;
139 final PsiFile psiRoot = getPsi(language);
140 final PsiElement psiElement = findElementAt(psiRoot, offset);
141 if (psiElement == null || psiElement instanceof OuterLanguageElement) continue;
142 if (ret == null || psiRoot != mainRoot) {
143 ret = psiElement;
146 return ret;
149 @Nullable
150 public PsiElement findElementAt(int offset) {
151 return findElementAt(offset, Language.class);
154 @Nullable
155 public PsiReference findReferenceAt(int offset) {
156 TextRange minRange = new TextRange(0, getContents().length());
157 PsiReference ret = null;
158 for (final Language language : getLanguages()) {
159 final PsiElement psiRoot = getPsi(language);
160 final PsiReference reference = SharedPsiElementImplUtil.findReferenceAt(psiRoot, offset, language);
161 if (reference == null) continue;
162 final TextRange textRange = reference.getRangeInElement().shiftRight(reference.getElement().getTextRange().getStartOffset());
163 if (minRange.contains(textRange)) {
164 minRange = textRange;
165 ret = reference;
168 return ret;