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.
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
);
53 public abstract Language
getBaseLanguage();
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
) {
66 protected void removeFile(final Language language
) {
67 myRoots
.remove(language
);
70 protected PsiFile
getPsiInner(final Language target
) {
71 PsiFile file
= myRoots
.get(target
);
74 VirtualFile virtualFile
= getVirtualFile();
75 VirtualFile parent
= virtualFile
.getParent();
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
);
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()]);
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());
123 public final MultiplePsiFilesPerDocumentFileViewProvider
createCopy(final LightVirtualFile fileCopy
) {
124 final MultiplePsiFilesPerDocumentFileViewProvider copy
= cloneInner(fileCopy
);
125 copy
.myOriginal
= myOriginal
== null ?
this : myOriginal
;
129 protected abstract MultiplePsiFilesPerDocumentFileViewProvider
cloneInner(VirtualFile fileCopy
);
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
) {
150 public PsiElement
findElementAt(int offset
) {
151 return findElementAt(offset
, Language
.class);
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
;