1 package com.intellij.codeInsight.completion;
3 import com.intellij.codeInsight.ExpectedTypeInfo;
4 import com.intellij.codeInsight.ExpectedTypesProvider;
5 import com.intellij.codeInsight.generation.GenerateMembersUtil;
6 import com.intellij.codeInsight.generation.OverrideImplementUtil;
7 import com.intellij.codeInsight.generation.PsiGenerationInfo;
8 import com.intellij.codeInsight.generation.PsiMethodMember;
9 import com.intellij.codeInsight.lookup.Lookup;
10 import com.intellij.codeInsight.lookup.LookupElementDecorator;
11 import com.intellij.codeInsight.lookup.LookupItem;
12 import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
13 import com.intellij.featureStatistics.FeatureUsageTracker;
14 import com.intellij.ide.util.MemberChooser;
15 import com.intellij.openapi.application.ApplicationManager;
16 import com.intellij.openapi.command.CommandProcessor;
17 import com.intellij.openapi.command.UndoConfirmationPolicy;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.editor.Editor;
20 import com.intellij.openapi.editor.ScrollType;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.psi.*;
23 import com.intellij.psi.codeStyle.CodeStyleManager;
24 import com.intellij.psi.impl.source.PostprocessReformattingAspect;
25 import com.intellij.psi.infos.CandidateInfo;
26 import com.intellij.psi.util.PsiTreeUtil;
27 import com.intellij.psi.util.PsiUtil;
28 import com.intellij.util.IncorrectOperationException;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
37 class ConstructorInsertHandler implements InsertHandler<LookupElementDecorator<LookupItem>> {
38 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.ConstructorInsertHandler");
39 public static final ConstructorInsertHandler SMART_INSTANCE = new ConstructorInsertHandler(true);
40 public static final ConstructorInsertHandler BASIC_INSTANCE = new ConstructorInsertHandler(false);
41 static final OffsetKey PARAM_LIST_START = OffsetKey.create("paramListStart");
42 static final OffsetKey PARAM_LIST_END = OffsetKey.create("paramListEnd");
43 private final boolean mySmart;
45 private ConstructorInsertHandler(boolean smart) {
49 public void handleInsert(InsertionContext context, LookupElementDecorator<LookupItem> item) {
50 @SuppressWarnings({"unchecked"}) final LookupItem<PsiClass> delegate = item.getDelegate();
52 final PsiElement position = SmartCompletionDecorator.getPosition(context, delegate);
53 final PsiExpression enclosing = PsiTreeUtil.getContextOfType(position, PsiExpression.class, true);
54 final PsiAnonymousClass anonymousClass = PsiTreeUtil.getParentOfType(position, PsiAnonymousClass.class);
55 final boolean inAnonymous = anonymousClass != null && anonymousClass.getParent() == enclosing;
56 PsiClass psiClass = (PsiClass)item.getObject();
58 boolean isAbstract = psiClass.hasModifierProperty(PsiModifier.ABSTRACT);
60 if (Lookup.REPLACE_SELECT_CHAR == context.getCompletionChar()) {
61 final int plStart = context.getOffset(PARAM_LIST_START);
62 final int plEnd = context.getOffset(PARAM_LIST_END);
63 if (plStart >= 0 && plEnd >= 0) {
64 context.getDocument().deleteString(plStart, plEnd);
65 PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
69 OffsetKey insideRef = context.trackOffset(context.getTailOffset(), false);
71 boolean fillTypeArgs = false;
72 if (delegate instanceof PsiTypeLookupItem) {
73 fillTypeArgs = !isRawTypeExpected(context, (PsiTypeLookupItem)delegate) &&
74 psiClass.getTypeParameters().length > 0 &&
75 ((PsiTypeLookupItem)delegate).calcGenerics(position).isEmpty() &&
76 context.getCompletionChar() != '(';
77 delegate.handleInsert(context);
78 PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(context.getFile().getViewProvider());
81 if (item.getDelegate() instanceof JavaPsiClassReferenceElement) {
82 PsiTypeLookupItem.addImportForItem(context, psiClass);
85 insertParentheses(context, delegate, psiClass, !inAnonymous && isAbstract);
93 FeatureUsageTracker.getInstance().triggerFeatureUsed(JavaCompletionFeatures.AFTER_NEW_ANONYMOUS);
96 PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(context.getFile().getViewProvider());
98 final Editor editor = context.getEditor();
99 final int offset = context.getTailOffset();
100 editor.getDocument().insertString(offset, " {}");
101 editor.getCaretModel().moveToOffset(offset + 2);
103 if (fillTypeArgs && JavaCompletionUtil.promptTypeArgs(context, context.getOffset(insideRef))) return;
105 context.setLaterRunnable(generateAnonymousBody(editor, context.getFile()));
108 PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
109 final PsiNewExpression newExpression =
110 PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiNewExpression.class, false);
111 if (newExpression != null) {
112 final PsiJavaCodeReferenceElement classReference = newExpression.getClassOrAnonymousClassReference();
113 if (classReference != null) {
114 CodeStyleManager.getInstance(context.getProject()).reformat(classReference);
118 FeatureUsageTracker.getInstance().triggerFeatureUsed(JavaCompletionFeatures.AFTER_NEW);
120 if (fillTypeArgs && JavaCompletionUtil.promptTypeArgs(context, context.getOffset(insideRef))) return;
124 static boolean isRawTypeExpected(InsertionContext context, PsiTypeLookupItem delegate) {
125 PsiNewExpression newExpr =
126 PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiNewExpression.class, false);
127 if (newExpr != null) {
128 for (ExpectedTypeInfo info : ExpectedTypesProvider.getExpectedTypes(newExpr, true)) {
129 PsiType expected = info.getDefaultType();
130 if (expected.isAssignableFrom(delegate.getPsiType())) {
131 if (expected instanceof PsiClassType && ((PsiClassType)expected).isRaw()) {
140 public static boolean insertParentheses(InsertionContext context,
142 final PsiClass psiClass,
143 final boolean forAnonymous) {
144 if (context.getCompletionChar() == '[') {
148 final PsiElement place = context.getFile().findElementAt(context.getStartOffset());
149 final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper();
150 assert place != null;
151 boolean hasParams = false;
152 for (PsiMethod constructor : psiClass.getConstructors()) {
153 if (!resolveHelper.isAccessible(constructor, place, null)) continue;
154 if (constructor.getParameterList().getParametersCount() > 0) {
160 JavaCompletionUtil.insertParentheses(context, delegate, false, hasParams, forAnonymous);
165 private static Runnable generateAnonymousBody(final Editor editor, final PsiFile file) {
166 final Project project = file.getProject();
167 PsiDocumentManager.getInstance(project).commitAllDocuments();
169 int offset = editor.getCaretModel().getOffset();
170 PsiElement element = file.findElementAt(offset);
171 if (element == null) return null;
173 PsiElement parent = element.getParent();
174 if (!(parent instanceof PsiAnonymousClass)) return null;
177 CodeStyleManager.getInstance(project).reformat(parent);
179 catch(IncorrectOperationException e){
182 offset = parent.getTextRange().getEndOffset() - 1;
183 editor.getCaretModel().moveToOffset(offset);
184 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
185 editor.getSelectionModel().removeSelection();
187 return new Runnable() {
189 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
191 PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
192 final PsiAnonymousClass aClass = PsiTreeUtil.findElementOfClassAtOffset(file, editor.getCaretModel().getOffset(), PsiAnonymousClass.class, false);
193 if (aClass == null) return;
195 final Collection<CandidateInfo> candidatesToImplement = OverrideImplementUtil.getMethodsToOverrideImplement(aClass, true);
196 boolean invokeOverride = candidatesToImplement.isEmpty();
198 chooseAndOverrideMethodsInAdapter(project, editor, aClass);
201 ApplicationManager.getApplication().runWriteAction(new Runnable() {
204 List<PsiMethod> methods = OverrideImplementUtil.overrideOrImplementMethodCandidates(aClass, candidatesToImplement, false);
205 List<PsiGenerationInfo<PsiMethod>> prototypes = OverrideImplementUtil.convert2GenerationInfos(methods);
206 List<PsiGenerationInfo<PsiMethod>> resultMembers = GenerateMembersUtil.insertMembersBeforeAnchor(aClass, null, prototypes);
207 GenerateMembersUtil.positionCaret(editor, resultMembers.get(0).getPsiMember(), true);
209 catch(IncorrectOperationException ioe){
217 }, CompletionBundle.message("completion.smart.type.generate.anonymous.body"), null, UndoConfirmationPolicy.DEFAULT, editor.getDocument());
222 private static void chooseAndOverrideMethodsInAdapter(final Project project, final Editor editor, final PsiAnonymousClass aClass) {
223 PsiClass baseClass = aClass.getBaseClassType().resolve();
224 if (baseClass == null) return;
225 PsiMethod[] allBaseMethods = baseClass.getMethods();
226 if(allBaseMethods.length == 0) return;
228 List<PsiMethodMember> methods = new ArrayList<PsiMethodMember>();
229 for (final PsiMethod method : allBaseMethods) {
230 if (OverrideImplementUtil.isOverridable(method)) {
231 methods.add(new PsiMethodMember(method, PsiSubstitutor.UNKNOWN));
235 boolean canInsertOverride = PsiUtil.isLanguageLevel5OrHigher(aClass) && (PsiUtil.isLanguageLevel6OrHigher(aClass) || !aClass.isInterface());
236 final PsiMethodMember[] array = methods.toArray(new PsiMethodMember[methods.size()]);
237 final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(array, false, true, project, canInsertOverride);
238 chooser.setTitle(CompletionBundle.message("completion.smarttype.select.methods.to.override"));
239 chooser.setCopyJavadocVisible(true);
242 List<PsiMethodMember> selected = chooser.getSelectedElements();
243 if (selected == null || selected.isEmpty()) return;
247 final List<PsiGenerationInfo<PsiMethod>> prototypes = OverrideImplementUtil.overrideOrImplementMethods(aClass, selected, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());
249 final int offset = editor.getCaretModel().getOffset();
251 ApplicationManager.getApplication().runWriteAction(new Runnable() {
254 for (PsiGenerationInfo<PsiMethod> prototype : prototypes) {
255 PsiStatement[] statements = prototype.getPsiMember().getBody().getStatements();
256 if (statements.length > 0 && PsiType.VOID.equals(prototype.getPsiMember().getReturnType())) {
257 statements[0].delete(); // remove "super(..)" call
261 List<PsiGenerationInfo<PsiMethod>> resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);
262 GenerateMembersUtil.positionCaret(editor, resultMembers.get(0).getPsiMember(), true);
264 catch(IncorrectOperationException e){
270 catch(IncorrectOperationException ioe){