NotNull, dispose
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / TargetElementUtilBase.java
blob399f8b35092ca3ffed7fa16abe0ecf8c47767cc2
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 * User: anna
19 * Date: 30-Jan-2008
21 package com.intellij.codeInsight;
23 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
24 import com.intellij.codeInsight.lookup.Lookup;
25 import com.intellij.codeInsight.lookup.LookupElement;
26 import com.intellij.codeInsight.lookup.LookupManager;
27 import com.intellij.codeInsight.lookup.LookupValueWithPsiElement;
28 import com.intellij.ide.util.EditSourceUtil;
29 import com.intellij.lang.Language;
30 import com.intellij.lang.LanguageExtension;
31 import com.intellij.navigation.NavigationItem;
32 import com.intellij.openapi.application.ApplicationManager;
33 import com.intellij.openapi.components.ServiceManager;
34 import com.intellij.openapi.editor.Document;
35 import com.intellij.openapi.editor.Editor;
36 import com.intellij.openapi.editor.LogicalPosition;
37 import com.intellij.openapi.project.Project;
38 import com.intellij.openapi.util.TextRange;
39 import com.intellij.pom.Navigatable;
40 import com.intellij.pom.PomDeclarationSearcher;
41 import com.intellij.pom.PomTarget;
42 import com.intellij.pom.PsiDeclaredTarget;
43 import com.intellij.pom.references.PomService;
44 import com.intellij.psi.*;
45 import com.intellij.psi.util.PsiTreeUtil;
46 import com.intellij.util.Consumer;
47 import com.intellij.util.containers.CollectionFactory;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.List;
56 public class TargetElementUtilBase {
57 public static final int REFERENCED_ELEMENT_ACCEPTED = 0x01;
58 public static final int ELEMENT_NAME_ACCEPTED = 0x02;
59 public static final int LOOKUP_ITEM_ACCEPTED = 0x08;
61 public static TargetElementUtilBase getInstance() {
62 return ServiceManager.getService(TargetElementUtilBase.class);
65 public int getAllAccepted() {
66 return REFERENCED_ELEMENT_ACCEPTED | ELEMENT_NAME_ACCEPTED | LOOKUP_ITEM_ACCEPTED;
69 /**
70 * Accepts THIS or SUPER but not NEW_AS_CONSTRUCTOR.
72 public int getDefinitionSearchFlags() {
73 return getAllAccepted();
76 /**
77 * Accepts NEW_AS_CONSTRUCTOR but not THIS or SUPER.
79 public int getReferenceSearchFlags() {
80 return getAllAccepted();
83 @Nullable
84 public static PsiReference findReference(Editor editor) {
85 return findReference(editor, editor.getCaretModel().getOffset());
88 @Nullable
89 public static PsiReference findReference(Editor editor, int offset) {
90 Project project = editor.getProject();
91 if (project == null) return null;
93 Document document = editor.getDocument();
94 PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
95 if (file == null) return null;
96 if (ApplicationManager.getApplication().isDispatchThread()) {
97 PsiDocumentManager.getInstance(project).commitAllDocuments();
100 offset = adjustOffset(document, offset);
102 if (file instanceof PsiCompiledElement) {
103 return ((PsiCompiledElement)file).getMirror().findReferenceAt(offset);
106 return file.findReferenceAt(offset);
109 public static int adjustOffset(Document document, final int offset) {
110 CharSequence text = document.getCharsSequence();
111 int correctedOffset = offset;
112 int textLength = document.getTextLength();
113 if (offset >= textLength) {
114 correctedOffset = textLength - 1;
116 else if (!Character.isJavaIdentifierPart(text.charAt(offset))) {
117 correctedOffset--;
119 if (correctedOffset < 0 || !Character.isJavaIdentifierPart(text.charAt(correctedOffset))) return offset;
120 return correctedOffset;
123 @Nullable
124 public static PsiElement findTargetElement(Editor editor, int flags) {
125 ApplicationManager.getApplication().assertIsDispatchThread();
127 return getInstance().findTargetElement(editor, flags, editor.getCaretModel().getOffset());
130 public static boolean inVirtualSpace(Editor editor, int offset) {
131 if (offset == editor.getCaretModel().getOffset()) {
132 return inVirtualSpace(editor, editor.getCaretModel().getLogicalPosition());
135 return false;
138 public static boolean inVirtualSpace(Editor editor, LogicalPosition logicalPosition) {
139 return !editor.offsetToLogicalPosition(editor.logicalPositionToOffset(logicalPosition)).equals(logicalPosition);
142 @Nullable
143 public PsiElement findTargetElement(Editor editor, int flags, int offset) {
144 Project project = editor.getProject();
145 if (project == null) return null;
147 Lookup activeLookup = LookupManager.getInstance(project).getActiveLookup();
148 if (activeLookup != null && (flags & LOOKUP_ITEM_ACCEPTED) != 0) {
149 final PsiElement lookupItem = getLookupItem(activeLookup);
150 return lookupItem != null && lookupItem.isValid() ? lookupItem : null;
153 Document document = editor.getDocument();
154 if (ApplicationManager.getApplication().isDispatchThread()) {
155 PsiDocumentManager.getInstance(project).commitAllDocuments();
157 PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
158 if (file == null) return null;
160 offset = adjustOffset(document, offset);
162 PsiElement element = file.findElementAt(offset);
163 if ((flags & REFERENCED_ELEMENT_ACCEPTED) != 0) {
164 final PsiElement referenceOrReferencedElement = getReferenceOrReferencedElement(file, editor, flags, offset);
165 //if (referenceOrReferencedElement == null) {
166 // return getReferenceOrReferencedElement(file, editor, flags, offset);
168 if (isAcceptableReferencedElement(element, referenceOrReferencedElement)) {
169 return referenceOrReferencedElement;
173 if (element == null) return null;
175 if ((flags & ELEMENT_NAME_ACCEPTED) != 0) {
176 if (element instanceof PsiNamedElement) return element;
177 return getNamedElement(element, offset - element.getTextRange().getStartOffset());
179 return null;
182 protected boolean isAcceptableReferencedElement(final PsiElement element, final PsiElement referenceOrReferencedElement) {
183 return referenceOrReferencedElement != null &&
184 referenceOrReferencedElement.isValid();
187 @Nullable
188 public PsiElement adjustElement(final Editor editor, final int flags, final PsiElement element, final PsiElement contextElement) {
189 return element;
192 @Nullable
193 public PsiElement adjustReference(@NotNull PsiReference ref){
194 return null;
197 @Nullable
198 public PsiElement getNamedElement(@Nullable final PsiElement element, final int offsetInElement) {
199 if (element == null) return null;
201 final List<PomTarget> targets = CollectionFactory.arrayList();
202 final Consumer<PomTarget> consumer = new Consumer<PomTarget>() {
203 public void consume(@NotNull PomTarget target) {
204 if (target instanceof PsiDeclaredTarget) {
205 final PsiDeclaredTarget declaredTarget = (PsiDeclaredTarget)target;
206 final PsiElement navigationElement = declaredTarget.getNavigationElement();
207 final TextRange range = declaredTarget.getNameIdentifierRange();
208 if (range != null && !range.shiftRight(navigationElement.getTextRange().getStartOffset())
209 .contains(element.getTextRange().getStartOffset() + offsetInElement)) {
210 return;
213 targets.add(target);
217 PsiElement parent = element;
219 int offset = offsetInElement;
220 while (parent != null) {
221 for (PomDeclarationSearcher searcher : PomDeclarationSearcher.EP_NAME.getExtensions()) {
222 searcher.findDeclarationsAt(parent, offset, consumer);
223 if (!targets.isEmpty()) {
224 final PomTarget target = targets.get(0);
225 if (target != null) {
226 return PomService.convertToPsi(element.getProject(), target);
230 offset += parent.getStartOffsetInParent();
231 parent = parent.getParent();
234 return getNamedElement(element);
238 @Nullable
239 protected PsiElement getNamedElement(@Nullable final PsiElement element) {
240 PsiElement parent;
241 if ((parent = PsiTreeUtil.getParentOfType(element, PsiNamedElement.class, false)) != null) {
242 // A bit hacky depends on navigation offset correctly overridden
243 assert element != null : "notnull parent?";
244 if (parent.getTextOffset() == element.getTextRange().getStartOffset()) {
245 return parent;
248 return null;
251 @Nullable
252 private static PsiElement getLookupItem(Lookup activeLookup) {
253 LookupElement item = activeLookup.getCurrentItem();
254 if (item == null) return null;
255 Object o = item.getObject();
257 if (o instanceof PsiElement) {
258 PsiElement element = (PsiElement)o;
259 if (!(element instanceof PsiDirectoryContainer)) {
260 if (!isValidElement(element)) return null;
262 return element;
264 else if (o instanceof LookupValueWithPsiElement) {
265 final PsiElement element = ((LookupValueWithPsiElement)o).getElement();
266 if (element != null && isValidElement(element)) return element;
268 return null;
271 private static boolean isValidElement(@NotNull PsiElement element) {
272 if (!element.isValid()) return false;
273 PsiFile file = element.getContainingFile();
274 if (file == null) return false;
275 return file.getOriginalFile().getVirtualFile() != null;
278 @Nullable
279 protected PsiElement getReferenceOrReferencedElement(PsiFile file, Editor editor, int flags, int offset) {
280 PsiReference ref = findReference(editor, offset);
281 if (ref == null) return null;
283 final Language language = ref.getElement().getLanguage();
284 final TargetElementEvaluator evaluator = targetElementEvaluator.forLanguage(language);
285 if (evaluator != null) {
286 final PsiElement element = evaluator.getElementByReference(ref, flags);
287 if (element != null) return element;
290 PsiManager manager = file.getManager();
291 PsiElement refElement = ref.resolve();
292 if (refElement == null) {
293 if (ApplicationManager.getApplication().isDispatchThread()) {
294 DaemonCodeAnalyzer.getInstance(manager.getProject()).updateVisibleHighlighters(editor);
296 return null;
298 else {
299 return refElement;
303 public Collection<PsiElement> getTargetCandidates(PsiReference reference) {
304 if (reference instanceof PsiPolyVariantReference) {
305 final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false);
306 final ArrayList<PsiElement> navigatableResults = new ArrayList<PsiElement>(results.length);
308 for(ResolveResult r:results) {
309 PsiElement element = r.getElement();
310 if (EditSourceUtil.canNavigate(element) || element instanceof Navigatable && ((Navigatable)element).canNavigateToSource()) {
311 navigatableResults.add(element);
315 return navigatableResults;
317 PsiElement resolved = reference.resolve();
318 if (resolved instanceof NavigationItem) {
319 return Collections.singleton(resolved);
321 return Collections.emptyList();
324 public PsiElement getGotoDeclarationTarget(final PsiElement element, final PsiElement navElement) {
325 return navElement;
328 public boolean includeSelfInGotoImplementation(final PsiElement element) {
329 final TargetElementEvaluator elementEvaluator = element != null ? targetElementEvaluator.forLanguage(element.getLanguage()):null;
330 return elementEvaluator == null || elementEvaluator.includeSelfInGotoImplementation(element);
333 protected final LanguageExtension<TargetElementEvaluator> targetElementEvaluator = new LanguageExtension<TargetElementEvaluator>("com.intellij.targetElementEvaluator");