2 * Copyright 2000-2010 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.
16 package com.intellij.codeInsight.completion;
18 import com.intellij.codeInsight.AutoPopupController;
19 import com.intellij.codeInsight.ExpectedTypeInfo;
20 import com.intellij.codeInsight.ExpectedTypesProvider;
21 import com.intellij.codeInsight.lookup.LookupElement;
22 import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.editor.Document;
25 import com.intellij.openapi.editor.Editor;
26 import com.intellij.openapi.editor.ex.EditorEx;
27 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.pom.java.LanguageLevel;
30 import com.intellij.psi.*;
31 import com.intellij.psi.filters.FilterPositionUtil;
32 import com.intellij.psi.javadoc.PsiDocTag;
33 import com.intellij.psi.util.PsiTreeUtil;
34 import com.intellij.psi.util.PsiUtil;
35 import com.intellij.util.containers.hash.HashSet;
42 class JavaClassNameInsertHandler implements InsertHandler<JavaPsiClassReferenceElement> {
43 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaClassNameInsertHandler");
44 static final InsertHandler<JavaPsiClassReferenceElement> JAVA_CLASS_INSERT_HANDLER = new JavaClassNameInsertHandler();
46 public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
47 final char c = context.getCompletionChar();
49 int offset = context.getTailOffset() - 1;
50 final PsiFile file = context.getFile();
51 if (PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiImportStatementBase.class, false) != null) {
52 final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiJavaCodeReferenceElement.class, false);
53 final String qname = item.getQualifiedName();
54 if (qname != null && (ref == null || !qname.equals(ref.getCanonicalText()))) {
55 AllClassesGetter.INSERT_FQN.handleInsert(context, item);
60 PsiElement position = file.findElementAt(offset);
61 PsiClass psiClass = item.getObject();
62 final Project project = context.getProject();
63 final boolean annotation = insertingAnnotation(context, item);
65 final Editor editor = context.getEditor();
67 context.setLaterRunnable(new Runnable() {
69 new CodeCompletionHandlerBase(CompletionType.BASIC).invokeCompletion(project, editor);
72 } else if (c == '.' && PsiTreeUtil.getParentOfType(position, PsiParameterList.class) == null) {
73 AutoPopupController.getInstance(context.getProject()).autoPopupMemberLookup(context.getEditor(), null);
76 if (position != null) {
77 PsiElement parent = position.getParent();
78 if (parent instanceof PsiJavaCodeReferenceElement) {
79 final PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)parent;
80 if (PsiTreeUtil.getParentOfType(position, PsiDocTag.class) != null && ref.isReferenceTo(psiClass)) {
86 OffsetKey refEnd = context.trackOffset(context.getTailOffset(), false);
88 boolean fillTypeArgs = context.getCompletionChar() == '<';
90 context.setAddCompletionChar(false);
93 PsiTypeLookupItem.addImportForItem(context, psiClass);
95 if (shouldInsertParentheses(psiClass, file.findElementAt(context.getTailOffset() - 1))) {
96 if (ConstructorInsertHandler.insertParentheses(context, item, psiClass, false)) {
97 fillTypeArgs |= psiClass.hasTypeParameters() && PsiUtil.getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_1_5);
100 else if (insertingAnnotationWithParameters(context, item)) {
101 JavaCompletionUtil.insertParentheses(context, item, false, true);
102 AutoPopupController.getInstance(project).autoPopupParameterInfo(editor, null);
106 // Check if someone inserts annotation class that require @
107 PsiElement elementAt = file.findElementAt(context.getStartOffset());
108 final PsiElement parentElement = elementAt != null ? elementAt.getParent():null;
110 if (elementAt instanceof PsiIdentifier &&
111 (PsiTreeUtil.getParentOfType(elementAt, PsiAnnotationParameterList.class) != null ||
112 parentElement instanceof PsiErrorElement && parentElement.getParent() instanceof PsiJavaFile // top level annotation without @
114 && isAtTokenNeeded(context)) {
115 int expectedOffsetForAtToken = elementAt.getTextRange().getStartOffset();
116 context.getDocument().insertString(expectedOffsetForAtToken, "@");
120 if (fillTypeArgs && context.getCompletionChar() != '(') {
121 JavaCompletionUtil.promptTypeArgs(context, context.getOffset(refEnd));
125 private static boolean shouldInsertParentheses(PsiClass psiClass, PsiElement position) {
126 final PsiJavaCodeReferenceElement ref = PsiTreeUtil.getParentOfType(position, PsiJavaCodeReferenceElement.class);
131 final PsiReferenceParameterList parameterList = ref.getParameterList();
132 if (parameterList != null && parameterList.getTextLength() > 0) {
136 final PsiElement prevElement = FilterPositionUtil.searchNonSpaceNonCommentBack(ref);
137 if (prevElement != null && prevElement.getParent() instanceof PsiNewExpression) {
139 Set<PsiType> expectedTypes = new HashSet<PsiType>();
140 for (ExpectedTypeInfo info : ExpectedTypesProvider.getExpectedTypes((PsiExpression)prevElement.getParent(), true)) {
141 expectedTypes.add(info.getType());
144 return JavaCompletionUtil.isDefinitelyExpected(psiClass, expectedTypes, position);
150 private static boolean insertingAnnotationWithParameters(InsertionContext context, LookupElement item) {
151 if(insertingAnnotation(context, item)) {
152 final Document document = context.getEditor().getDocument();
153 PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
154 PsiElement elementAt = context.getFile().findElementAt(context.getStartOffset());
155 if (elementAt instanceof PsiIdentifier) {
156 final PsiModifierListOwner parent = PsiTreeUtil.getParentOfType(elementAt, PsiModifierListOwner.class, false, PsiCodeBlock.class);
157 if (parent != null) {
158 for (PsiMethod m : ((PsiClass)item.getObject()).getMethods()) {
159 if (!(m instanceof PsiAnnotationMethod)) continue;
160 final PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)m).getDefaultValue();
161 if (defaultValue == null) return true;
169 private static boolean insertingAnnotation(InsertionContext context, LookupElement item) {
170 final Object obj = item.getObject();
171 if (!(obj instanceof PsiClass) || !((PsiClass)obj).isAnnotationType()) return false;
173 final Document document = context.getEditor().getDocument();
174 PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
175 final int offset = context.getStartOffset();
177 final PsiFile file = context.getFile();
179 if (PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiImportStatement.class, false) != null) return false;
181 //outside of any class: we are surely inserting an annotation
182 if (PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiClass.class, false) == null) return true;
184 //the easiest check that there's a @ before the identifier
185 return PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiAnnotation.class, false) != null;
189 private static boolean isAtTokenNeeded(InsertionContext myContext) {
190 HighlighterIterator iterator = ((EditorEx)myContext.getEditor()).getHighlighter().createIterator(myContext.getStartOffset());
191 LOG.assertTrue(iterator.getTokenType() == JavaTokenType.IDENTIFIER);
193 if (iterator.getTokenType() == TokenType.WHITE_SPACE) iterator.retreat();
194 return iterator.getTokenType() != JavaTokenType.AT && iterator.getTokenType() != JavaTokenType.DOT;