refactoring
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / TargetElementUtilBase.java
blob22fa5c5310b60af528b3439363442ac1120f4ada
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(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 return target == null ? null : PomService.convertToPsi(element.getProject(), target);
228 offset += parent.getStartOffsetInParent();
229 parent = parent.getParent();
232 return getNamedElement(element);
236 @Nullable
237 protected PsiElement getNamedElement(@Nullable final PsiElement element) {
238 PsiElement parent;
239 if ((parent = PsiTreeUtil.getParentOfType(element, PsiNamedElement.class, false)) != null) {
240 // A bit hacky depends on navigation offset correctly overridden
241 assert element != null : "notnull parent?";
242 if (parent.getTextOffset() == element.getTextRange().getStartOffset()) {
243 return parent;
246 return null;
249 @Nullable
250 private static PsiElement getLookupItem(Lookup activeLookup) {
251 LookupElement item = activeLookup.getCurrentItem();
252 if (item == null) return null;
253 Object o = item.getObject();
255 if (o instanceof PsiElement) {
256 PsiElement element = (PsiElement)o;
257 if (!(element instanceof PsiDirectoryContainer)) {
258 if (!isValidElement(element)) return null;
260 return element;
262 else if (o instanceof LookupValueWithPsiElement) {
263 final PsiElement element = ((LookupValueWithPsiElement)o).getElement();
264 if (element != null && isValidElement(element)) return element;
266 return null;
269 private static boolean isValidElement(@NotNull PsiElement element) {
270 if (!element.isValid()) return false;
271 PsiFile file = element.getContainingFile();
272 if (file == null) return false;
273 return file.getOriginalFile().getVirtualFile() != null;
276 @Nullable
277 protected PsiElement getReferenceOrReferencedElement(PsiFile file, Editor editor, int flags, int offset) {
278 PsiReference ref = findReference(editor, offset);
279 if (ref == null) return null;
281 final Language language = ref.getElement().getLanguage();
282 final TargetElementEvaluator evaluator = targetElementEvaluator.forLanguage(language);
283 if (evaluator != null) {
284 final PsiElement element = evaluator.getElementByReference(ref, flags);
285 if (element != null) return element;
288 PsiManager manager = file.getManager();
289 PsiElement refElement = ref.resolve();
290 if (refElement == null) {
291 if (ApplicationManager.getApplication().isDispatchThread()) {
292 DaemonCodeAnalyzer.getInstance(manager.getProject()).updateVisibleHighlighters(editor);
294 return null;
296 else {
297 return refElement;
301 public Collection<PsiElement> getTargetCandidates(PsiReference reference) {
302 if (reference instanceof PsiPolyVariantReference) {
303 final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false);
304 final ArrayList<PsiElement> navigatableResults = new ArrayList<PsiElement>(results.length);
306 for(ResolveResult r:results) {
307 PsiElement element = r.getElement();
308 if (EditSourceUtil.canNavigate(element) || element instanceof Navigatable && ((Navigatable)element).canNavigateToSource()) {
309 navigatableResults.add(element);
313 return navigatableResults;
315 PsiElement resolved = reference.resolve();
316 if (resolved instanceof NavigationItem) {
317 return Collections.singleton(resolved);
319 return Collections.emptyList();
322 public PsiElement getGotoDeclarationTarget(final PsiElement element, final PsiElement navElement) {
323 return navElement;
326 public boolean includeSelfInGotoImplementation(final PsiElement element) {
327 final TargetElementEvaluator elementEvaluator = element != null ? targetElementEvaluator.forLanguage(element.getLanguage()):null;
328 return elementEvaluator == null || elementEvaluator.includeSelfInGotoImplementation(element);
331 protected final LanguageExtension<TargetElementEvaluator> targetElementEvaluator = new LanguageExtension<TargetElementEvaluator>("com.intellij.targetElementEvaluator");