change signature: do not show dialog
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / changeSignature / JavaChangeSignatureDetector.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.refactoring.changeSignature;
17
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.application.Result;
20 import com.intellij.openapi.command.WriteCommandAction;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.editor.Editor;
24 import com.intellij.openapi.util.Comparing;
25 import com.intellij.openapi.util.TextRange;
26 import com.intellij.psi.*;
27 import com.intellij.psi.util.PsiTreeUtil;
28 import com.intellij.refactoring.util.CanonicalTypes;
29 import com.intellij.util.IncorrectOperationException;
30 import com.intellij.util.VisibilityUtil;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import java.util.*;
35
36 /**
37  * User: anna
38  * Date: Sep 6, 2010
39  */
40 public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetector {
41   private static final Logger LOG = Logger.getInstance("#" + JavaChangeSignatureDetector.class.getName());
42
43   @Override
44   public ChangeInfo createCurrentChangeSignature(final @NotNull PsiElement element,
45                                                  final @Nullable ChangeInfo changeInfo) {
46     PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, false);
47     if (method != null && isInsideMethodSignature(element, method)) {
48       final String newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
49       final PsiType returnType = method.getReturnType();
50       final CanonicalTypes.Type newReturnType;
51       final ParameterInfoImpl[] parameterInfos;
52       try {
53         newReturnType = returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null;
54         parameterInfos = ParameterInfoImpl.fromMethod(method);
55       }
56       catch (IncorrectOperationException e) {
57         return null;
58       }
59       final MyJavaChangeInfo fromMethod = new MyJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, null, method.getName());
60       if (changeInfo == null) { //before replacement
61         fromMethod.setSuperMethod(method.findDeepestSuperMethod());
62         return fromMethod;
63       } else {
64         final MyJavaChangeInfo info = (MyJavaChangeInfo)changeInfo;
65         if (!info.getMethod().equals(method)) return null;
66         if (!info.equals(fromMethod)) {
67           createParametersInfo(element, parameterInfos, info);
68           if (info.isReturnTypeChanged()) {
69             final String visibility = info.getNewVisibility();
70             if (Comparing.strEqual(visibility, PsiModifier.PRIVATE) &&
71                 !info.isArrayToVarargs() &&
72                 !info.isExceptionSetOrOrderChanged() &&
73                 !info.isExceptionSetChanged() &&
74                 !info.isNameChanged() &&
75                 !info.isParameterSetOrOrderChanged() &&
76                 !info.isParameterNamesChanged() &&
77                 !info.isParameterTypesChanged()) {
78               return null;
79             }
80           }
81           final MyJavaChangeInfo javaChangeInfo =
82             new MyJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, info.getNewExceptions(), info.getOldName()) {
83               @Override
84               protected void fillOldParams(PsiMethod method) {
85                 oldParameterNames = info.getOldParameterNames();
86                 oldParameterTypes = info.getOldParameterTypes();
87                 if (!method.isConstructor()) {
88                   try {
89                     isReturnTypeChanged = info.isReturnTypeChanged || !info.getNewReturnType().equals(newReturnType);
90                   }
91                   catch (IncorrectOperationException e) {
92                     isReturnTypeChanged = true;
93                   }
94                 }
95               }
96             };
97           javaChangeInfo.setSuperMethod(info.getSuperMethod());
98           return javaChangeInfo;
99         }
100         return changeInfo;
101       }
102     }
103     return null;
104   }
105
106   private static void createParametersInfo(PsiElement element,
107                                            ParameterInfoImpl[] parameterInfos,
108                                            MyJavaChangeInfo info) {
109
110     final JavaParameterInfo[] oldParameters = info.getNewParameters();
111     final String[] oldParameterNames = info.getOldParameterNames();
112     final String[] oldParameterTypes =  info.getOldParameterTypes();
113     final Map<JavaParameterInfo, Integer> untouchedParams = new HashMap<JavaParameterInfo, Integer>();
114     for (int i = 0; i < parameterInfos.length; i++) {
115       ParameterInfoImpl parameterInfo = parameterInfos[i];
116       JavaParameterInfo oldParameter = null;
117       for (JavaParameterInfo parameter : oldParameters) {
118         if (Comparing.strEqual(parameter.getName(), parameterInfo.getName()) &&
119             Comparing.strEqual(parameter.getTypeText(), parameterInfo.getTypeText())) {
120           oldParameter = parameter;
121           break;
122         }
123       }
124
125       if (oldParameter != null) {
126         parameterInfos[i] = new ParameterInfoImpl(oldParameter.getOldIndex(),
127                                                   oldParameter.getName(),
128                                                   oldParameter.getTypeWrapper().getType(element, element.getManager()),
129                                                   null);
130         untouchedParams.put(parameterInfos[i], oldParameter.getOldIndex());
131       }
132     }
133
134     for (int i = 0; i < parameterInfos.length; i++) {
135       ParameterInfoImpl parameterInfo = parameterInfos[i];
136       if (!untouchedParams.containsKey(parameterInfo)) {
137         JavaParameterInfo oldParameter = null;
138         if (oldParameters.length > i && oldParameterNames.length > i) {
139           if (Comparing.strEqual(oldParameterNames[i], parameterInfo.getName()) ||
140               Comparing.strEqual(oldParameterTypes[i], parameterInfo.getTypeText())) {
141             if (!untouchedParams.containsValue(oldParameters[i].getOldIndex())) {
142               oldParameter = oldParameters[i];
143             }
144           }
145         }
146         parameterInfos[i] = new ParameterInfoImpl(oldParameter != null ? oldParameter.getOldIndex() : - 1,
147                                                   parameterInfo.getName(),
148                                                   parameterInfo.getTypeWrapper().getType(element, element.getManager()),
149                                                   null);
150       }
151     }
152   }
153
154
155   private static class MyJavaChangeInfo extends JavaChangeInfoImpl  {
156     private PsiMethod mySuperMethod;
157     private MyJavaChangeInfo(String newVisibility,
158                              PsiMethod method,
159                              CanonicalTypes.Type newType,
160                              @NotNull ParameterInfoImpl[] newParms,
161                              ThrownExceptionInfo[] newExceptions,
162                              String oldName) {
163       super(newVisibility, method, method.getName(), newType, newParms, newExceptions, false,
164             new HashSet<PsiMethod>(),
165             new HashSet<PsiMethod>(), oldName);
166     }
167
168     @Override
169     protected void setupPropagationEnabled(PsiParameter[] parameters, ParameterInfoImpl[] newParms) {
170       isPropagationEnabled = false;
171     }
172
173     public PsiMethod getSuperMethod() {
174       if (mySuperMethod == null) {
175         return getMethod();
176       }
177       return mySuperMethod;
178     }
179
180     public void setSuperMethod(PsiMethod superMethod) {
181       mySuperMethod = superMethod;
182     }
183   }
184
185   @Override
186   public boolean showDialog(ChangeInfo changeInfo, @NotNull final String oldText) {
187     if (changeInfo instanceof MyJavaChangeInfo) {
188       final MyJavaChangeInfo info = (MyJavaChangeInfo)changeInfo;
189       final PsiMethod method = info.getSuperMethod();
190
191       //if (ApplicationManager.getApplication().isUnitTestMode()) {
192         temporallyRevertChanges(method, oldText);
193         createChangeSignatureProcessor(info, method).run();
194         return true;
195       /*}
196       final JavaChangeSignatureDialog dialog =
197         new JavaChangeSignatureDialog(method.getProject(), new JavaMethodDescriptor(info.getMethod()) {
198           @Override
199           public String getReturnTypeText() {
200             return info.getNewReturnType().getTypeText();
201           }
202         }, true, method) {
203           protected BaseRefactoringProcessor createRefactoringProcessor() {
204             return createChangeSignatureProcessor(info, method);
205           }
206
207           @Override
208           protected void invokeRefactoring(final BaseRefactoringProcessor processor) {
209             CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
210               @Override
211               public void run() {
212                 temporallyRevertChanges(method, oldText);
213                 doRefactor(processor);
214               }
215             }, RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(info.getMethod())), null);
216           }
217
218           private void doRefactor(BaseRefactoringProcessor processor) {
219             super.invokeRefactoring(processor);
220           }
221         };
222       dialog.show();
223       return dialog.isOK();*/
224     }
225     return false;
226
227   }
228
229   private static void temporallyRevertChanges(final PsiMethod method, final String oldText) {
230     ApplicationManager.getApplication().runWriteAction(new Runnable() {
231       @Override
232       public void run() {
233         final PsiFile file = method.getContainingFile();
234         final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(method.getProject());
235         final Document document = documentManager.getDocument(file);
236         if (document != null) {
237           document.setText(oldText);
238           documentManager.commitDocument(document);
239         }
240       }
241     });
242   }
243
244   private static ChangeSignatureProcessor createChangeSignatureProcessor(final MyJavaChangeInfo info,
245                                                                          final PsiMethod method) {
246     return new ChangeSignatureProcessor(method.getProject(), new MyJavaChangeInfo(info.getNewVisibility(), info.getSuperMethod(),
247                                                                            info.getNewReturnType(),
248                                                                            (ParameterInfoImpl[])info.getNewParameters(),
249                                                                            info.getNewExceptions(), info.getOldName()) {
250       @Override
251       protected void fillOldParams(PsiMethod method) {
252         super.fillOldParams(method);
253         oldParameterNames = info.getOldParameterNames();
254         oldParameterTypes = info.getOldParameterTypes();
255       }
256     });
257   }
258
259   @Override
260   public boolean isChangeSignatureAvailable(PsiElement element, ChangeInfo currentInfo) {
261     if (currentInfo instanceof JavaChangeInfo) {
262       return element instanceof PsiIdentifier && Comparing.equal(currentInfo.getMethod(), element.getParent());
263     }
264     return false;
265   }
266
267   @Nullable
268   @Override
269   public TextRange getHighlightingRange(PsiElement element) {
270     element = element.getParent();
271     if (element instanceof PsiMethod) {
272       final PsiCodeBlock body = ((PsiMethod)element).getBody();
273       return new TextRange(element.getTextRange().getStartOffset(), body == null ? element.getTextRange().getEndOffset() : body.getTextRange().getStartOffset() - 1);
274     }
275     return null;
276   }
277
278   @Override
279   public boolean wasBanned(PsiElement element, @NotNull ChangeInfo bannedInfo) {
280     final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
281     return method != null && isInsideMethodSignature(element, method) && Comparing.equal(method, bannedInfo.getMethod());
282   }
283
284   @Override
285   public boolean isMoveParameterAvailable(PsiElement element, boolean left) {
286     if (element instanceof PsiParameter) {
287       final PsiParameter parameter = (PsiParameter)element;
288       final PsiElement declarationScope = parameter.getDeclarationScope();
289       if (declarationScope instanceof PsiMethod) {
290         final PsiMethod method = (PsiMethod)declarationScope;
291         final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
292         if (left) {
293           return parameterIndex > 0;
294         } else {
295           return parameterIndex < method.getParameterList().getParametersCount() - 1;
296         }
297       }
298     }
299     return false;
300   }
301
302   @Override
303   public void moveParameter(final PsiElement element, final Editor editor, final boolean left) {
304     final PsiParameter parameter = (PsiParameter)element;
305     final PsiMethod method = (PsiMethod)parameter.getDeclarationScope();
306     final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
307     new WriteCommandAction(element.getProject(), MOVE_PARAMETER){
308       @Override
309       protected void run(Result result) throws Throwable {
310         final PsiParameterList parameterList = method.getParameterList();
311         final PsiParameter[] parameters = parameterList.getParameters();
312         final int deltaOffset = editor.getCaretModel().getOffset() - parameter.getTextRange().getStartOffset();
313         final PsiParameter frst = left ? parameters[parameterIndex - 1] : parameter;
314         final PsiParameter scnd = left ? parameter : parameters[parameterIndex + 1];
315         final int startOffset = frst.getTextRange().getStartOffset();
316         final int endOffset = scnd.getTextRange().getEndOffset();
317
318         final PsiFile file = method.getContainingFile();
319         final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
320         if (document != null) {
321           final String comma_whitespace_between =
322             document.getText().substring(frst.getTextRange().getEndOffset(), scnd.getTextRange().getStartOffset());
323           document.replaceString(startOffset, endOffset, scnd.getText() + comma_whitespace_between + frst.getText());
324           editor.getCaretModel().moveToOffset(document.getText().indexOf(parameter.getText(), startOffset) + deltaOffset);
325         }
326       }
327     }.execute();
328   }
329
330   private static boolean isInsideMethodSignature(PsiElement element, @NotNull PsiMethod method) {
331     final PsiCodeBlock body = method.getBody();
332     if (body != null) {
333       return element.getTextOffset() < body.getTextOffset() && element.getTextOffset() > method.getModifierList().getTextRange().getEndOffset();
334     }
335     return method.hasModifierProperty(PsiModifier.ABSTRACT);
336   }
337 }