1fd242f2607cc114bf06ad37cfc78b95c8062b73
[idea/community.git] / platform / core-impl / src / com / intellij / psi / impl / SharedPsiElementImplUtil.java
1 /*
2  * Copyright 2000-2009 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
17 package com.intellij.psi.impl;
18
19 import com.intellij.lang.Language;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.TextRange;
22 import com.intellij.psi.*;
23 import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
24 import com.intellij.psi.templateLanguages.OuterLanguageElement;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
27
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.List;
32
33 public class SharedPsiElementImplUtil {
34   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.SharedPsiElementImplUtil");
35
36   private SharedPsiElementImplUtil() {
37   }
38
39   @Nullable
40   public static PsiReference findReferenceAt(PsiElement thisElement, int offset, @Nullable Language lang) {
41     if (thisElement == null) return null;
42     PsiElement element = lang != null ? thisElement.getContainingFile().getViewProvider().findElementAt(offset, lang) :
43                          thisElement.findElementAt(offset);
44     if (element == null || element instanceof OuterLanguageElement) return null;
45     offset = thisElement.getTextRange().getStartOffset() + offset - element.getTextRange().getStartOffset();
46
47     List<PsiReference> referencesList = new ArrayList<PsiReference>();
48     while (element != null) {
49       addReferences(offset, element, referencesList);
50       offset = element.getStartOffsetInParent() + offset;
51       if (element instanceof PsiFile) break;
52       element = element.getParent();
53     }
54
55     if (referencesList.isEmpty()) return null;
56     if (referencesList.size() == 1) return referencesList.get(0);
57     return new PsiMultiReference(referencesList.toArray(new PsiReference[referencesList.size()]),
58                                  referencesList.get(referencesList.size() - 1).getElement());
59   }
60
61   @Nullable
62   public static PsiReference findReferenceAt(PsiElement thisElement, int offset) {
63     return findReferenceAt(thisElement, offset, null);
64   }
65
66   private static void addReferences(int offset, PsiElement element, final Collection<PsiReference> outReferences) {
67     for (final PsiReference reference : element.getReferences()) {
68       if (reference == null) {
69         LOG.error(element);
70       }
71       for (TextRange range : ReferenceRange.getRanges(reference)) {
72         assert range != null : reference;
73         if (range.containsOffset(offset)) {
74           outReferences.add(reference);
75         }
76       }
77     }
78   }
79
80   @NotNull
81   public static PsiReference[] getReferences(PsiElement thisElement) {
82     PsiReference ref = thisElement.getReference();
83     if (ref == null) return PsiReference.EMPTY_ARRAY;
84     return new PsiReference[]{ref};
85   }
86
87   @Nullable
88   public static PsiElement getNextSibling(PsiElement element) {
89     if (element instanceof PsiFile) {
90       final FileViewProvider viewProvider = ((PsiFile)element).getViewProvider();
91       element = viewProvider.getPsi(viewProvider.getBaseLanguage());
92     }
93     if (element == null) return null;
94     final PsiElement parent = element.getParent();
95     if (parent == null) return null;
96
97     final PsiElement[] children = parent.getChildren();
98     final int index = getChildIndex(children, element);
99     return 0 <= index && index < children.length - 1 ? children[index + 1] : null;
100   }
101
102   @Nullable
103   public static PsiElement getPrevSibling(PsiElement element) {
104     if (element instanceof PsiFile) {
105       final FileViewProvider viewProvider = ((PsiFile)element).getViewProvider();
106       element = viewProvider.getPsi(viewProvider.getBaseLanguage());
107     }
108     if (element == null) return null;
109     final PsiElement parent = element.getParent();
110     if (parent == null) return null;
111
112     final PsiElement[] children = parent.getChildren();
113     final int index = getChildIndex(children, element);
114     return index > 0 ? children[index - 1] : null;
115   }
116
117   private static int getChildIndex(final PsiElement[] children, final PsiElement child) {
118     for (int i = 0; i < children.length; i++) {
119       PsiElement candidate = children[i];
120       // do not use equals() since some smart-heads are used to override it (e.g. JspxImportStatementImpl)
121       if (candidate == child) {
122         return i;
123       }
124     }
125     LOG.error("Cannot find element among its parent' children." +
126               " element: '" + child + "';" +
127               " parent: '" + child.getParent() + "';" +
128               " children: " + Arrays.asList(children) + "; " +
129               " file:" + child.getContainingFile());
130     return -1;
131   }
132 }