get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / platform / core-impl / src / com / intellij / util / indexing / FileContentImpl.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.util.indexing;
17
18 import com.intellij.lang.FileASTNode;
19 import com.intellij.lang.Language;
20 import com.intellij.lang.LighterAST;
21 import com.intellij.lang.TreeBackedLighterAST;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.fileEditor.FileDocumentManager;
24 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
25 import com.intellij.openapi.fileTypes.FileType;
26 import com.intellij.openapi.fileTypes.LanguageFileType;
27 import com.intellij.openapi.project.DefaultProjectFactory;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.util.Key;
30 import com.intellij.openapi.util.UserDataHolderBase;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.psi.*;
33 import com.intellij.psi.impl.PsiManagerEx;
34 import com.intellij.psi.impl.file.impl.FileManager;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37 import org.jetbrains.annotations.TestOnly;
38
39 import java.io.IOException;
40 import java.nio.charset.Charset;
41
42 /**
43  * @author nik
44  *
45  * Class is not final since it is overridden in Upsource
46  */
47 public class FileContentImpl extends UserDataHolderBase implements PsiDependentFileContent {
48   private final VirtualFile myFile;
49   private final String myFileName;
50   private final FileType myFileType;
51   private Charset myCharset;
52   private byte[] myContent;
53   private CharSequence myContentAsText;
54   private final long myStamp;
55   private byte[] myHash;
56   private boolean myLighterASTShouldBeThreadSafe;
57   private final boolean myPhysicalContent;
58
59   public FileContentImpl(@NotNull final VirtualFile file, @NotNull final CharSequence contentAsText, long documentStamp) {
60     this(file, contentAsText, null, documentStamp, false);
61   }
62
63   public FileContentImpl(@NotNull final VirtualFile file, @NotNull final byte[] content) {
64     this(file, null, content, -1, true);
65   }
66
67   FileContentImpl(@NotNull final VirtualFile file) {
68     this(file, null, null, -1, true);
69   }
70
71   private FileContentImpl(@NotNull VirtualFile file,
72                           CharSequence contentAsText,
73                           byte[] content,
74                           long stamp,
75                           boolean physicalContent) {
76     myFile = file;
77     myContentAsText = contentAsText;
78     myContent = content;
79     myFileType = file.getFileType();
80     // remember name explicitly because the file could be renamed afterwards
81     myFileName = file.getName();
82     myStamp = stamp;
83     myPhysicalContent = physicalContent;
84   }
85
86   @Override
87   public Project getProject() {
88     return getUserData(IndexingDataKeys.PROJECT);
89   }
90
91   private static final Key<PsiFile> CACHED_PSI = Key.create("cached psi from content");
92
93   @NotNull
94   @Override
95   public PsiFile getPsiFile() {
96     return getPsiFileForPsiDependentIndex();
97   }
98
99   @NotNull
100   private PsiFile getFileFromText() {
101     PsiFile psi = getUserData(IndexingDataKeys.PSI_FILE);
102
103     if (psi == null) {
104       psi = getUserData(CACHED_PSI);
105     }
106
107     if (psi == null) {
108       psi = createFileFromText(getContentAsText());
109       psi.putUserData(IndexingDataKeys.VIRTUAL_FILE, getFile());
110       putUserData(CACHED_PSI, psi);
111     }
112     return psi;
113   }
114
115   @Override
116   @NotNull
117   public LighterAST getLighterAST() {
118     LighterAST lighterAST = getUserData(IndexingDataKeys.LIGHTER_AST_NODE_KEY);
119     if (lighterAST == null) {
120       FileASTNode node = getPsiFile().getNode();
121       lighterAST = myLighterASTShouldBeThreadSafe ? new TreeBackedLighterAST(node) : node.getLighterAST();
122       putUserData(IndexingDataKeys.LIGHTER_AST_NODE_KEY, lighterAST);
123     }
124     return lighterAST;
125   }
126
127   /**
128    * Expand the AST to ensure {@link com.intellij.lang.FCTSBackedLighterAST} won't be used, because it's not thread-safe,
129    * but unsaved documents may be indexed in many concurrent threads
130    */
131   void ensureThreadSafeLighterAST() {
132     myLighterASTShouldBeThreadSafe = true;
133   }
134
135   public PsiFile createFileFromText(@NotNull CharSequence text) {
136     Project project = getProject();
137     if (project == null) {
138       project = DefaultProjectFactory.getInstance().getDefaultProject();
139     }
140     return createFileFromText(project, text, (LanguageFileType)getFileTypeWithoutSubstitution(), myFile, myFileName);
141   }
142
143   @NotNull
144   public static PsiFile createFileFromText(@NotNull Project project, @NotNull CharSequence text, @NotNull LanguageFileType fileType,
145                                            @NotNull VirtualFile file, @NotNull String fileName) {
146     final Language language = fileType.getLanguage();
147     final Language substitutedLanguage = LanguageSubstitutors.INSTANCE.substituteLanguage(language, file, project);
148     PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText(fileName, substitutedLanguage, text, false, false, false, file);
149     if (psiFile == null) {
150       throw new IllegalStateException("psiFile is null. language = " + language.getID() +
151                                       ", substitutedLanguage = " + substitutedLanguage.getID());
152     }
153     return psiFile;
154   }
155
156   public static class IllegalDataException extends RuntimeException {
157     IllegalDataException(final String message) {
158       super(message);
159     }
160   }
161
162   @NotNull
163   private FileType getSubstitutedFileType() {
164     return SubstitutedFileType.substituteFileType(myFile, myFileType, getProject());
165   }
166
167   @TestOnly
168   public static FileContent createByFile(@NotNull VirtualFile file) {
169     try {
170       return new FileContentImpl(file, file.contentsToByteArray());
171     }
172     catch (IOException e) {
173       throw new RuntimeException(e);
174     }
175   }
176
177   private FileType getFileTypeWithoutSubstitution() {
178     return myFileType;
179   }
180
181   @NotNull
182   @Override
183   public FileType getFileType() {
184     return getSubstitutedFileType();
185   }
186
187   @NotNull
188   @Override
189   public VirtualFile getFile() {
190     return myFile;
191   }
192
193   @NotNull
194   @Override
195   public String getFileName() {
196     return myFileName;
197   }
198
199   @NotNull
200   public Charset getCharset() {
201     Charset charset = myCharset;
202     if (charset == null) {
203       myCharset = charset = myFile.getCharset();
204     }
205     return charset;
206   }
207
208   public long getStamp() {
209     return myStamp;
210   }
211
212   @NotNull
213   @Override
214   public byte[] getContent() {
215     byte[] content = myContent;
216     if (content == null) {
217       myContent = content = myContentAsText.toString().getBytes(getCharset());
218     }
219     return content;
220   }
221
222   @NotNull
223   @Override
224   public CharSequence getContentAsText() {
225     if (myFileType.isBinary()) {
226       throw new IllegalDataException("Cannot obtain text for binary file type : " + myFileType.getDescription());
227     }
228     final CharSequence content = getUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY);
229     if (content != null) {
230       return content;
231     }
232     CharSequence contentAsText = myContentAsText;
233     if (contentAsText == null) {
234       myContentAsText = contentAsText = LoadTextUtil.getTextByBinaryPresentation(myContent, myFile);
235       myContent = null; // help gc, indices are expected to use bytes or chars but not both
236     }
237     return contentAsText;
238   }
239
240   @Override
241   public String toString() {
242     return myFileName;
243   }
244
245   @Nullable
246   public byte[] getHash() {
247     return myHash;
248   }
249
250   public void setHash(byte[] hash) {
251     myHash = hash;
252   }
253
254   /**
255    * @deprecated use {@link FileContent#getPsiFile()}
256    */
257   @SuppressWarnings("DeprecatedIsStillUsed")
258   @Deprecated
259   @NotNull
260   public PsiFile getPsiFileForPsiDependentIndex() {
261     PsiFile psi = null;
262     if (!myPhysicalContent) {
263       Document document = FileDocumentManager.getInstance().getCachedDocument(getFile());
264
265       if (document != null) {
266         PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(getProject());
267         if (psiDocumentManager.isUncommited(document)) {
268           PsiFile existingPsi = psiDocumentManager.getPsiFile(document);
269           if (existingPsi != null) {
270             psi = existingPsi;
271           }
272         }
273       }
274     }
275     if (psi == null) {
276       psi = getFileFromText();
277     }
278     return psi;
279   }
280 }