@NotNull'ed
[fedora-idea.git] / lang-api / src / com / intellij / psi / util / PsiUtilBase.java
blob51173641d44225120c79abfd2d784968ede64945
1 package com.intellij.psi.util;
3 import com.intellij.lang.ASTNode;
4 import com.intellij.lang.Language;
5 import com.intellij.lang.PsiBuilder;
6 import com.intellij.lang.PsiParser;
7 import com.intellij.lang.injection.InjectedLanguageManager;
8 import com.intellij.openapi.editor.Editor;
9 import com.intellij.openapi.editor.SelectionModel;
10 import com.intellij.openapi.editor.Document;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.util.Comparing;
13 import com.intellij.openapi.util.Key;
14 import com.intellij.openapi.util.TextRange;
15 import com.intellij.openapi.vfs.VirtualFile;
16 import com.intellij.psi.*;
17 import com.intellij.psi.meta.PsiMetaData;
18 import com.intellij.psi.meta.PsiMetaOwner;
19 import com.intellij.psi.scope.PsiScopeProcessor;
20 import com.intellij.psi.search.GlobalSearchScope;
21 import com.intellij.psi.search.SearchScope;
22 import com.intellij.psi.tree.IElementType;
23 import com.intellij.injected.editor.DocumentWindow;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
27 import javax.swing.*;
28 import java.util.List;
30 public class PsiUtilBase {
31 public static final PsiElement NULL_PSI_ELEMENT = new PsiElement() {
32 @NotNull
33 public Project getProject() {
34 throw new PsiInvalidElementAccessException(this);
37 @NotNull
38 public Language getLanguage() {
39 throw new IllegalAccessError();
42 public PsiManager getManager() {
43 return null;
46 @NotNull
47 public PsiElement[] getChildren() {
48 return new PsiElement[0];
51 public PsiElement getParent() {
52 return null;
55 @Nullable
56 public PsiElement getFirstChild() {
57 return null;
60 @Nullable
61 public PsiElement getLastChild() {
62 return null;
65 @Nullable
66 public PsiElement getNextSibling() {
67 return null;
70 @Nullable
71 public PsiElement getPrevSibling() {
72 return null;
75 public PsiFile getContainingFile() {
76 throw new PsiInvalidElementAccessException(this);
79 public TextRange getTextRange() {
80 return null;
83 public int getStartOffsetInParent() {
84 return 0;
87 public int getTextLength() {
88 return 0;
91 public PsiElement findElementAt(int offset) {
92 return null;
95 @Nullable
96 public PsiReference findReferenceAt(int offset) {
97 return null;
100 public int getTextOffset() {
101 return 0;
104 public String getText() {
105 return null;
108 @NotNull
109 public char[] textToCharArray() {
110 return new char[0];
113 public PsiElement getNavigationElement() {
114 return null;
117 public PsiElement getOriginalElement() {
118 return null;
121 public boolean textMatches(@NotNull CharSequence text) {
122 return false;
125 public boolean textMatches(@NotNull PsiElement element) {
126 return false;
129 public boolean textContains(char c) {
130 return false;
133 public void accept(@NotNull PsiElementVisitor visitor) {
137 public void acceptChildren(@NotNull PsiElementVisitor visitor) {
141 public PsiElement copy() {
142 return null;
145 public PsiElement add(@NotNull PsiElement element) {
146 return null;
149 public PsiElement addBefore(@NotNull PsiElement element, PsiElement anchor) {
150 return null;
153 public PsiElement addAfter(@NotNull PsiElement element, PsiElement anchor) {
154 return null;
157 public void checkAdd(@NotNull PsiElement element) {
161 public PsiElement addRange(PsiElement first, PsiElement last) {
162 return null;
165 public PsiElement addRangeBefore(@NotNull PsiElement first, @NotNull PsiElement last, PsiElement anchor) {
166 return null;
169 public PsiElement addRangeAfter(PsiElement first, PsiElement last, PsiElement anchor) {
170 return null;
173 public void delete() {
177 public void checkDelete() {
181 public void deleteChildRange(PsiElement first, PsiElement last) {
185 public PsiElement replace(@NotNull PsiElement newElement) {
186 return null;
189 public boolean isValid() {
190 return false;
193 public boolean isWritable() {
194 return false;
197 @Nullable
198 public PsiReference getReference() {
199 return null;
202 @NotNull
203 public PsiReference[] getReferences() {
204 return new PsiReference[0];
207 public <T> T getCopyableUserData(Key<T> key) {
208 return null;
211 public <T> void putCopyableUserData(Key<T> key, T value) {
215 public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
216 return false;
219 public PsiElement getContext() {
220 return null;
223 public boolean isPhysical() {
224 return false;
227 @NotNull
228 public GlobalSearchScope getResolveScope() {
229 return GlobalSearchScope.EMPTY_SCOPE;
232 @NotNull
233 public SearchScope getUseScope() {
234 return GlobalSearchScope.EMPTY_SCOPE;
237 public ASTNode getNode() {
238 return null;
241 public <T> T getUserData(Key<T> key) {
242 return null;
245 public <T> void putUserData(Key<T> key, T value) {
249 public Icon getIcon(int flags) {
250 return null;
253 public boolean isEquivalentTo(final PsiElement another) {
254 return this == another;
258 public static final PsiParser NULL_PARSER = new PsiParser() {
259 @NotNull
260 public ASTNode parse(IElementType root, PsiBuilder builder) {
261 throw new IllegalAccessError();
265 public static int getRootIndex(PsiElement root) {
266 ASTNode node = root.getNode();
267 while(node != null && node.getTreeParent() != null) {
268 node = node.getTreeParent();
270 if(node != null) root = node.getPsi();
271 final PsiFile containingFile = root.getContainingFile();
272 final PsiFile[] psiRoots = containingFile.getPsiRoots();
273 for (int i = 0; i < psiRoots.length; i++) {
274 if(root == psiRoots[i]) return i;
276 throw new RuntimeException("invalid element");
279 @Nullable
280 public static VirtualFile getVirtualFile(@Nullable PsiElement element) {
281 if (element == null || !element.isValid()) {
282 return null;
285 if (element instanceof PsiFileSystemItem) {
286 return ((PsiFileSystemItem)element).getVirtualFile();
289 final PsiFile containingFile = element.getContainingFile();
290 if (containingFile == null) {
291 return null;
294 return containingFile.getVirtualFile();
297 public static int compareElementsByPosition(final PsiElement element1, final PsiElement element2) {
298 if (element1 != null && element2 != null) {
299 final PsiFile psiFile1 = element1.getContainingFile();
300 final PsiFile psiFile2 = element2.getContainingFile();
301 if (Comparing.equal(psiFile1, psiFile2)){
302 final TextRange textRange1 = element1.getTextRange();
303 final TextRange textRange2 = element2.getTextRange();
304 if (textRange1 != null && textRange2 != null) {
305 return textRange1.getStartOffset() - textRange2.getStartOffset();
307 } else if (psiFile1 != null && psiFile2 != null){
308 final String name1 = psiFile1.getName();
309 final String name2 = psiFile2.getName();
310 return name1.compareToIgnoreCase(name2);
313 return 0;
316 @NotNull
317 public static Language getLanguageAtOffset (@NotNull PsiFile file, int offset) {
318 final PsiElement elt = file.findElementAt(offset);
319 if (elt == null) return file.getLanguage();
320 if (elt instanceof PsiWhiteSpace) {
321 final int decremented = elt.getTextRange().getStartOffset() - 1;
322 if (decremented >= 0) {
323 return getLanguageAtOffset(file, decremented);
326 return findLanguageFromElement(elt);
329 @NotNull
330 public static Language findLanguageFromElement(final PsiElement elt) {
331 if (elt.getFirstChild() == null) { //is leaf
332 final PsiElement parent = elt.getParent();
333 if (parent != null) {
334 return parent.getLanguage();
338 return elt.getLanguage();
341 public static PsiFile getTemplateLanguageFile(final PsiElement element) {
342 if (element == null) return null;
343 final PsiFile containingFile = element.getContainingFile();
344 if (containingFile == null) return null;
346 final FileViewProvider viewProvider = containingFile.getViewProvider();
347 return viewProvider.getPsi(viewProvider.getBaseLanguage());
350 /** @return name for element using element structure info
352 @Nullable
353 public static String getName(PsiElement element) {
354 String name = null;
355 if (element instanceof PsiMetaOwner) {
356 final PsiMetaData data = ((PsiMetaOwner) element).getMetaData();
357 if (data != null) {
358 name = data.getName(element);
361 if (name == null && element instanceof PsiNamedElement) {
362 name = ((PsiNamedElement) element).getName();
364 return name;
367 public static boolean isUnderPsiRoot(PsiFile root, PsiElement element) {
368 PsiFile containingFile = element.getContainingFile();
369 if (containingFile == root) return true;
370 for (PsiFile psiRoot : root.getPsiRoots()) {
371 if (containingFile == psiRoot) return true;
373 PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(root.getProject()).getInjectionHost(element);
374 return host != null && isUnderPsiRoot(root, host);
377 @Nullable
378 public static <T extends PsiElement> T getOriginalElement(@NotNull T psiElement, final Class<? extends T> elementClass) {
379 final PsiFile psiFile = psiElement.getContainingFile();
380 final PsiFile originalFile = psiFile.getOriginalFile();
381 if (originalFile == psiFile) return psiElement;
382 final TextRange range = psiElement.getTextRange();
383 final PsiElement element = originalFile.findElementAt(range.getStartOffset());
384 final int maxLength = range.getLength();
385 T parent = PsiTreeUtil.getParentOfType(element, elementClass, false);
386 for (T next = parent ;
387 next != null && next.getTextLength() <= maxLength;
388 parent = next, next = PsiTreeUtil.getParentOfType(next, elementClass, true)) {
390 return parent;
393 @Nullable
394 public static Language getLanguageInEditor(@NotNull final Editor editor, @NotNull final Project project) {
395 PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
396 if (file == null) return null;
398 final SelectionModel selectionModel = editor.getSelectionModel();
399 int caretOffset = editor.getCaretModel().getOffset();
400 int mostProbablyCorrectLanguageOffset = caretOffset == selectionModel.getSelectionStart() ||
401 caretOffset == selectionModel.getSelectionEnd()
402 ? selectionModel.getSelectionStart()
403 : caretOffset;
404 PsiElement elt = getElementAtOffset(file, mostProbablyCorrectLanguageOffset);
405 Language lang = findLanguageFromElement(elt);
407 if (selectionModel.hasSelection()) {
408 final Language rangeLanguage = evaluateLanguageInRange(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(), file);
409 if (rangeLanguage == null) return file.getLanguage();
411 lang = rangeLanguage;
414 return narrowLanguage(lang, file.getLanguage());
417 public static Language getDialect(@NotNull PsiElement element) {
418 return narrowLanguage(element.getLanguage(), element.getContainingFile().getLanguage());
421 private static Language narrowLanguage(final Language language, final Language candidate) {
422 if (candidate.isKindOf(language)) return candidate;
423 return language;
426 @Nullable
427 public static PsiFile getPsiFileInEditor(final Editor editor, final Project project) {
428 final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
429 if (file == null) return null;
431 final Language language = getLanguageInEditor(editor, project);
432 if (language == null) return file;
434 if (language == file.getLanguage()) return file;
436 final SelectionModel selectionModel = editor.getSelectionModel();
437 int caretOffset = editor.getCaretModel().getOffset();
438 int mostProbablyCorrectLanguageOffset = caretOffset == selectionModel.getSelectionStart() ||
439 caretOffset == selectionModel.getSelectionEnd()
440 ? selectionModel.getSelectionStart()
441 : caretOffset;
442 return getPsiFileAtOffset(file, mostProbablyCorrectLanguageOffset);
445 public static PsiFile getPsiFileAtOffset(final PsiFile file, final int offset) {
446 PsiElement elt = getElementAtOffset(file, offset);
448 assert elt.isValid() : elt + "; file: "+file + "; isvalid: "+file.isValid();
449 return elt.getContainingFile();
452 @Nullable
453 public static Language reallyEvaluateLanguageInRange(final int start, final int end, final PsiFile file) {
454 Language lang = null;
455 int curOffset = start;
456 do {
457 PsiElement elt = getElementAtOffset(file, curOffset);
459 if (!(elt instanceof PsiWhiteSpace)) {
460 final Language language = findLanguageFromElement(elt);
461 if (lang == null) {
462 lang = language;
464 else if (lang != language) {
465 return null;
468 int endOffset = elt.getTextRange().getEndOffset();
469 curOffset = endOffset <= curOffset ? curOffset + 1 : endOffset;
471 while (curOffset < end);
472 return narrowLanguage(lang, file.getLanguage());
475 @Nullable
476 public static Language evaluateLanguageInRange(final int start, final int end, final PsiFile file) {
477 PsiElement elt = getElementAtOffset(file, start);
479 TextRange selectionRange = new TextRange(start, end);
480 if (!(elt instanceof PsiFile)) {
481 elt = elt.getParent();
482 TextRange range = elt.getTextRange();
483 assert range != null : "Range is null for " + elt + "; " + elt.getClass();
484 while(!range.contains(selectionRange) && !(elt instanceof PsiFile)) {
485 elt = elt.getParent();
486 if (elt == null) break;
487 range = elt.getTextRange();
488 assert range != null : "Range is null for " + elt + "; " + elt.getClass();
491 if (elt != null) {
492 return elt.getLanguage();
496 return reallyEvaluateLanguageInRange(start, end, file);
499 @NotNull
500 public static PsiElement getElementAtOffset(@NotNull PsiFile file, int offset) {
501 PsiElement elt = file.findElementAt(offset);
502 if (elt == null && offset > 0) {
503 elt = file.findElementAt(offset - 1);
505 if (elt == null) {
506 return file;
508 return elt;
511 public static <T extends PsiElement> T copyElementPreservingOriginalLinks(final T element, final Key<PsiElement> originalKey) {
512 final PsiElementVisitor originalVisitor = new PsiRecursiveElementWalkingVisitor() {
513 public void visitElement(final PsiElement element) {
514 element.putCopyableUserData(originalKey, element);
515 super.visitElement(element);
518 originalVisitor.visitElement(element);
520 final PsiElement fileCopy = element.copy();
522 final PsiElementVisitor copyVisitor = new PsiRecursiveElementWalkingVisitor() {
523 public void visitElement(final PsiElement element) {
524 final PsiElement originalElement = element.getCopyableUserData(originalKey);
525 if (originalElement != null) {
526 originalElement.putCopyableUserData(originalKey, null);
527 element.putCopyableUserData(originalKey, null);
528 element.putUserData(originalKey, originalElement);
530 super.visitElement(element);
534 copyVisitor.visitElement(fileCopy);
535 return (T) fileCopy;
538 public static boolean hasErrorElementChild(PsiElement element) {
539 for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
540 if (child instanceof PsiErrorElement) return true;
542 return false;
545 @NotNull
546 public static ASTNode getRoot(@NotNull ASTNode node) {
547 ASTNode child = node;
548 do {
549 final ASTNode parent = child.getTreeParent();
550 if (parent == null) return child;
551 child = parent;
553 while (true);
556 public static int findInjectedElementOffsetInRealDocument(final PsiElement element) {
557 final PsiFile containingFile = element.getContainingFile();
558 if (containingFile == null) return -1;
559 Document document = PsiDocumentManager.getInstance(containingFile.getProject()).getDocument(containingFile);
560 if (document instanceof DocumentWindow && !((DocumentWindow)document).isValid()) {
561 return -1;
563 final PsiElement context = containingFile.getContext();
564 if (!(context instanceof PsiLanguageInjectionHost)) {
565 return 0;
567 final int[] result = new int[1];
568 ((PsiLanguageInjectionHost)context).processInjectedPsi(new PsiLanguageInjectionHost.InjectedPsiVisitor() {
569 public void visit(@NotNull final PsiFile injectedPsi, @NotNull final List<PsiLanguageInjectionHost.Shred> places) {
570 if (injectedPsi != containingFile) {
571 return;
573 final PsiLanguageInjectionHost.Shred shred = places.get(0);
574 final TextRange textRange = element.getTextRange();
575 if (shred.prefix != null && textRange.getEndOffset() < shred.prefix.length()) {
576 result[0] = -1;
577 return;
579 final int injectedStart = shred.getRangeInsideHost().getStartOffset() +
580 shred.host.getTextRange().getStartOffset() - (shred.prefix != null ? shred.prefix.length():0);
581 result[0] = injectedStart;
584 return result[0];