/* * Copyright 2000-2009 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightUtilCore; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.AllClassesSearch; import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import java.util.Set; /** * Created by IntelliJ IDEA. * User: ik * Date: 02.12.2003 * Time: 16:49:25 * To change this template use Options | File Templates. */ public class AllClassesGetter { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.AllClassesGetter"); public static final InsertHandler TRY_SHORTENING = new InsertHandler() { private void _handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) { final Editor editor = context.getEditor(); final PsiClass psiClass = item.getObject(); if (!psiClass.isValid()) return; int endOffset = editor.getCaretModel().getOffset(); final String qname = psiClass.getQualifiedName(); if (qname == null) return; if (endOffset == 0) return; final Document document = editor.getDocument(); final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(psiClass.getProject()); final PsiFile file = context.getFile(); if (file.findElementAt(endOffset - 1) == null) return; final OffsetKey key = OffsetKey.create("endOffset", false); context.getOffsetMap().addOffset(key, endOffset); PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(); final int newOffset = context.getOffsetMap().getOffset(key); if (newOffset >= 0) { endOffset = newOffset; } else { LOG.error(endOffset + " became invalid: " + context.getOffsetMap() + "; inserting " + qname); } final RangeMarker toDelete = JavaCompletionUtil.insertTemporary(endOffset, document, " "); psiDocumentManager.commitAllDocuments(); PsiReference psiReference = file.findReferenceAt(endOffset - 1); boolean insertFqn = true; if (psiReference != null) { final PsiManager psiManager = file.getManager(); if (psiManager.areElementsEquivalent(psiClass, JavaCompletionUtil.resolveReference(psiReference))) { insertFqn = false; } else if (psiClass.isValid()) { try { context.setTailOffset(psiReference.getRangeInElement().getEndOffset() + psiReference.getElement().getTextRange().getStartOffset()); final PsiElement newUnderlying = psiReference.bindToElement(psiClass); if (newUnderlying != null) { final PsiElement psiElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(newUnderlying); if (psiElement != null) { for (final PsiReference reference : psiElement.getReferences()) { if (psiManager.areElementsEquivalent(psiClass, JavaCompletionUtil.resolveReference(reference))) { insertFqn = false; break; } } } } } catch (IncorrectOperationException e) { //if it's empty we just insert fqn below } } } if (toDelete.isValid()) { document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset()); context.setTailOffset(toDelete.getStartOffset()); } if (insertFqn) { INSERT_FQN.handleInsert(context, item); } } @Override public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) { _handleInsert(context, item); item.getTailType().processTail(context.getEditor(), context.getEditor().getCaretModel().getOffset()); } }; public static final InsertHandler INSERT_FQN = new InsertHandler() { @Override public void handleInsert(InsertionContext context, JavaPsiClassReferenceElement item) { final String qName = item.getQualifiedName(); if (qName != null) { int start = context.getTailOffset() - 1; while (start >= 0) { final char ch = context.getDocument().getCharsSequence().charAt(start); if (!Character.isJavaIdentifierPart(ch) && ch != '.') break; start--; } context.getDocument().replaceString(start + 1, context.getTailOffset(), qName); LOG.assertTrue(context.getTailOffset() >= 0); } } }; public static void processJavaClasses(final CompletionParameters parameters, final PrefixMatcher prefixMatcher, final boolean filterByScope, final Consumer consumer) { final PsiElement context = parameters.getPosition(); final Project project = context.getProject(); final GlobalSearchScope scope = filterByScope ? context.getContainingFile().getResolveScope() : GlobalSearchScope.allScope(project); Processor processor = new Processor() { final Set qNames = new THashSet(); final boolean pkgContext = JavaCompletionUtil.inSomePackage(context); final String packagePrefix = getPackagePrefix(context, parameters.getOffset()); @Override public boolean process(PsiClass psiClass) { assert psiClass != null; if (isAcceptableInContext(context, psiClass, filterByScope, pkgContext)) { String qName = psiClass.getQualifiedName(); if (qName != null && qName.startsWith(packagePrefix) && qNames.add(qName)) { consumer.consume(psiClass); } } return true; } }; processJavaClasses(prefixMatcher, project, scope, processor); } public static void processJavaClasses(final PrefixMatcher prefixMatcher, Project project, GlobalSearchScope scope, Processor processor) { AllClassesSearch.search(scope, project, new Condition() { @Override public boolean value(String s) { return prefixMatcher.isStartMatch(s); } }).forEach(processor); AllClassesSearch.search(scope, project, new Condition() { @Override public boolean value(String s) { return prefixMatcher.prefixMatches(s); } }).forEach(processor); } private static String getPackagePrefix(final PsiElement context, final int offset) { final String fileText = context.getContainingFile().getText(); int i = offset - 1; while (i >= 0) { final char c = fileText.charAt(i); if (!Character.isJavaIdentifierPart(c) && c != '.') break; i--; } String prefix = fileText.substring(i + 1, offset); final int j = prefix.lastIndexOf('.'); return j > 0 ? prefix.substring(0, j) : ""; } public static boolean isAcceptableInContext(@NotNull final PsiElement context, @NotNull final PsiClass psiClass, final boolean filterByScope, final boolean pkgContext) { ProgressManager.checkCanceled(); if (!context.isValid() || !psiClass.isValid()) return false; if (JavaCompletionUtil.isInExcludedPackage(psiClass, false)) return false; final String qualifiedName = psiClass.getQualifiedName(); if (qualifiedName == null) return false; if (!filterByScope && !(psiClass instanceof PsiCompiledElement)) return true; return JavaCompletionUtil.isSourceLevelAccessible(context, psiClass, pkgContext); } public static JavaPsiClassReferenceElement createLookupItem(@NotNull final PsiClass psiClass, final InsertHandler insertHandler) { final JavaPsiClassReferenceElement item = new JavaPsiClassReferenceElement(psiClass); item.setInsertHandler(insertHandler); return item; } }