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.refactoring.changeSignature;
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;
40 public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetector {
41 private static final Logger LOG = Logger.getInstance("#" + JavaChangeSignatureDetector.class.getName());
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;
53 newReturnType = returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null;
54 parameterInfos = ParameterInfoImpl.fromMethod(method);
56 catch (IncorrectOperationException e) {
59 final MyJavaChangeInfo fromMethod = new MyJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, null, method.getName());
60 if (changeInfo == null) { //before replacement
61 fromMethod.setSuperMethod(method.findDeepestSuperMethod());
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()) {
81 final MyJavaChangeInfo javaChangeInfo =
82 new MyJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, info.getNewExceptions(), info.getOldName()) {
84 protected void fillOldParams(PsiMethod method) {
85 oldParameterNames = info.getOldParameterNames();
86 oldParameterTypes = info.getOldParameterTypes();
87 if (!method.isConstructor()) {
89 isReturnTypeChanged = info.isReturnTypeChanged || !info.getNewReturnType().equals(newReturnType);
91 catch (IncorrectOperationException e) {
92 isReturnTypeChanged = true;
97 javaChangeInfo.setSuperMethod(info.getSuperMethod());
98 return javaChangeInfo;
106 private static void createParametersInfo(PsiElement element,
107 ParameterInfoImpl[] parameterInfos,
108 MyJavaChangeInfo info) {
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;
125 if (oldParameter != null) {
126 parameterInfos[i] = new ParameterInfoImpl(oldParameter.getOldIndex(),
127 oldParameter.getName(),
128 oldParameter.getTypeWrapper().getType(element, element.getManager()),
130 untouchedParams.put(parameterInfos[i], oldParameter.getOldIndex());
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];
146 parameterInfos[i] = new ParameterInfoImpl(oldParameter != null ? oldParameter.getOldIndex() : - 1,
147 parameterInfo.getName(),
148 parameterInfo.getTypeWrapper().getType(element, element.getManager()),
155 private static class MyJavaChangeInfo extends JavaChangeInfoImpl {
156 private PsiMethod mySuperMethod;
157 private MyJavaChangeInfo(String newVisibility,
159 CanonicalTypes.Type newType,
160 @NotNull ParameterInfoImpl[] newParms,
161 ThrownExceptionInfo[] newExceptions,
163 super(newVisibility, method, method.getName(), newType, newParms, newExceptions, false,
164 new HashSet<PsiMethod>(),
165 new HashSet<PsiMethod>(), oldName);
169 protected void setupPropagationEnabled(PsiParameter[] parameters, ParameterInfoImpl[] newParms) {
170 isPropagationEnabled = false;
173 public PsiMethod getSuperMethod() {
174 if (mySuperMethod == null) {
177 return mySuperMethod;
180 public void setSuperMethod(PsiMethod superMethod) {
181 mySuperMethod = superMethod;
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();
191 //if (ApplicationManager.getApplication().isUnitTestMode()) {
192 temporallyRevertChanges(method, oldText);
193 createChangeSignatureProcessor(info, method).run();
196 final JavaChangeSignatureDialog dialog =
197 new JavaChangeSignatureDialog(method.getProject(), new JavaMethodDescriptor(info.getMethod()) {
199 public String getReturnTypeText() {
200 return info.getNewReturnType().getTypeText();
203 protected BaseRefactoringProcessor createRefactoringProcessor() {
204 return createChangeSignatureProcessor(info, method);
208 protected void invokeRefactoring(final BaseRefactoringProcessor processor) {
209 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
212 temporallyRevertChanges(method, oldText);
213 doRefactor(processor);
215 }, RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(info.getMethod())), null);
218 private void doRefactor(BaseRefactoringProcessor processor) {
219 super.invokeRefactoring(processor);
223 return dialog.isOK();*/
229 private static void temporallyRevertChanges(final PsiMethod method, final String oldText) {
230 ApplicationManager.getApplication().runWriteAction(new Runnable() {
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);
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()) {
251 protected void fillOldParams(PsiMethod method) {
252 super.fillOldParams(method);
253 oldParameterNames = info.getOldParameterNames();
254 oldParameterTypes = info.getOldParameterTypes();
260 public boolean isChangeSignatureAvailable(PsiElement element, ChangeInfo currentInfo) {
261 if (currentInfo instanceof JavaChangeInfo) {
262 return element instanceof PsiIdentifier && Comparing.equal(currentInfo.getMethod(), element.getParent());
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);
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());
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);
293 return parameterIndex > 0;
295 return parameterIndex < method.getParameterList().getParametersCount() - 1;
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){
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();
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);
330 private static boolean isInsideMethodSignature(PsiElement element, @NotNull PsiMethod method) {
331 final PsiCodeBlock body = method.getBody();
333 return element.getTextOffset() < body.getTextOffset() && element.getTextOffset() > method.getModifierList().getTextRange().getEndOffset();
335 return method.hasModifierProperty(PsiModifier.ABSTRACT);