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 org.jetbrains.plugins.groovy.refactoring.changeSignature;
18 import com.intellij.openapi.editor.Document;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.util.text.StringUtil;
21 import com.intellij.psi.*;
22 import com.intellij.refactoring.HelpID;
23 import com.intellij.refactoring.changeSignature.ExceptionsTableModel;
24 import com.intellij.refactoring.changeSignature.ThrownExceptionInfo;
25 import com.intellij.refactoring.ui.CodeFragmentTableCellEditor;
26 import com.intellij.refactoring.ui.CodeFragmentTableCellRenderer;
27 import com.intellij.refactoring.ui.RefactoringDialog;
28 import com.intellij.refactoring.util.CanonicalTypes;
29 import com.intellij.refactoring.util.CommonRefactoringUtil;
30 import com.intellij.ui.EditableRowTable;
31 import com.intellij.ui.EditorTextField;
32 import com.intellij.ui.TableUtil;
33 import com.intellij.ui.table.JBTable;
34 import com.intellij.util.Function;
35 import com.intellij.util.containers.ContainerUtil;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.plugins.groovy.debugger.fragments.GroovyCodeFragment;
38 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
39 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
41 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
42 import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
43 import org.jetbrains.plugins.groovy.refactoring.ui.GrCodeFragmentTableCellEditor;
44 import org.jetbrains.plugins.groovy.refactoring.ui.GrCodeFragmentTableCellRenderer;
47 import javax.swing.event.TableModelEvent;
48 import javax.swing.event.TableModelListener;
49 import javax.swing.table.TableColumnModel;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.util.List;
55 import static org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle.message;
58 * @author Maxim.Medvedev
60 public class GrChangeSignatureDialog extends RefactoringDialog {
61 private EditorTextField myNameField;
62 private EditorTextField myReturnTypeField;
63 private JRadioButton myPublicRadioButton;
64 private JRadioButton myProtectedRadioButton;
65 private JRadioButton myPrivateRadioButton;
66 private JBTable myParameterTable;
67 private JPanel contentPane;
68 private JTextArea mySignatureLabel;
69 private JLabel myNameLabel;
70 private JLabel myReturnTypeLabel;
71 @SuppressWarnings({"UnusedDeclaration"}) private JRadioButton myModifyRadioButton;
72 private JRadioButton myDelegateRadioButton;
73 @SuppressWarnings({"UnusedDeclaration"}) private JPanel myParameterButtonPanel;
74 private JBTable myExceptionsTable;
75 @SuppressWarnings({"UnusedDeclaration"}) private JPanel myExceptionsButtonPanel;
76 private JPanel myDelegatePanel;
77 private GrParameterTableModel myParameterModel;
78 private GrMethod myMethod;
79 private PsiTypeCodeFragment myReturnTypeCodeFragment;
80 private GroovyCodeFragment myNameCodeFragment;
81 private ExceptionsTableModel myExceptionTableModel;
82 private static final String INDENT = " ";
84 public GrChangeSignatureDialog(@NotNull Project project, GrMethod method) {
89 ActionListener listener = new ActionListener() {
90 public void actionPerformed(ActionEvent e) {
94 myPublicRadioButton.addActionListener(listener);
95 myPrivateRadioButton.addActionListener(listener);
96 myProtectedRadioButton.addActionListener(listener);
99 protected void init() {
101 final PsiClass psiClass = myMethod.getContainingClass();
102 if (psiClass == null) return;
103 if (psiClass.isInterface()) {
104 myDelegatePanel.setVisible(false);
108 private void stopEditing() {
109 TableUtil.stopEditing(myParameterTable);
113 protected JComponent createCenterPanel() {
118 private void createUIComponents() {
119 createNameAndReturnTypeEditors();
120 createParametersPanel();
121 createExceptionsPanel();
124 private void createNameAndReturnTypeEditors() {
125 myNameCodeFragment = new GroovyCodeFragment(myProject, "");
126 myNameField = new EditorTextField(PsiDocumentManager.getInstance(myProject).getDocument(myNameCodeFragment), myProject,
127 myNameCodeFragment.getFileType());
129 myReturnTypeCodeFragment = JavaPsiFacade.getInstance(myProject).getElementFactory().createTypeCodeFragment("", myMethod, true, true);
130 final Document document = PsiDocumentManager.getInstance(myProject).getDocument(myReturnTypeCodeFragment);
131 myReturnTypeField = new EditorTextField(document, myProject, myReturnTypeCodeFragment.getFileType());
133 myNameField.setText(myMethod.getName());
134 final GrTypeElement element = myMethod.getReturnTypeElementGroovy();
135 if (element != null) {
136 myReturnTypeField.setText(element.getText());
139 myReturnTypeLabel = new JLabel();
140 myReturnTypeLabel.setLabelFor(myReturnTypeField);
142 myNameLabel = new JLabel();
143 myNameLabel.setLabelFor(myNameField);
146 private void createParametersPanel() {
147 myParameterModel = new GrParameterTableModel(myMethod, this, myProject);
148 myParameterModel.addTableModelListener(new TableModelListener() {
149 public void tableChanged(TableModelEvent e) {
153 myParameterTable = new JBTable(myParameterModel);
154 myParameterTable.setPreferredScrollableViewportSize(new Dimension(550, myParameterTable.getRowHeight() * 8));
156 myParameterButtonPanel = EditableRowTable.createButtonsTable(myParameterTable, myParameterModel, true);
158 myParameterTable.setCellSelectionEnabled(true);
159 final TableColumnModel columnModel = myParameterTable.getColumnModel();
160 columnModel.getColumn(0).setCellRenderer(new CodeFragmentTableCellRenderer(myProject));
161 columnModel.getColumn(1).setCellRenderer(new GrCodeFragmentTableCellRenderer(myProject));
162 columnModel.getColumn(2).setCellRenderer(new GrCodeFragmentTableCellRenderer(myProject));
163 columnModel.getColumn(3).setCellRenderer(new GrCodeFragmentTableCellRenderer(myProject));
165 columnModel.getColumn(0).setCellEditor(new CodeFragmentTableCellEditor(myProject));
166 columnModel.getColumn(1).setCellEditor(new GrCodeFragmentTableCellEditor(myProject));
167 columnModel.getColumn(2).setCellEditor(new GrCodeFragmentTableCellEditor(myProject));
168 columnModel.getColumn(3).setCellEditor(new GrCodeFragmentTableCellEditor(myProject));
170 if (myParameterModel.getRowCount() > 0) {
171 myParameterTable.setRowSelectionInterval(0, 0);
172 myParameterTable.setColumnSelectionInterval(0, 0);
176 private void createExceptionsPanel() {
177 myExceptionTableModel = new ExceptionsTableModel(myMethod);
178 myExceptionTableModel.setTypeInfos(myMethod);
179 myExceptionTableModel.addTableModelListener(new TableModelListener() {
180 public void tableChanged(TableModelEvent e) {
184 myExceptionsTable = new JBTable(myExceptionTableModel);
185 myExceptionsTable.setPreferredScrollableViewportSize(new Dimension(200, myExceptionsTable.getRowHeight() * 8));
187 myExceptionsButtonPanel = EditableRowTable.createButtonsTable(myExceptionsTable, myExceptionTableModel, false);
189 myExceptionsTable.getColumnModel().getColumn(0).setCellRenderer(new CodeFragmentTableCellRenderer(myProject));
190 myExceptionsTable.getColumnModel().getColumn(0).setCellEditor(new CodeFragmentTableCellEditor(myProject));
192 if (myExceptionTableModel.getRowCount() > 0) {
193 myExceptionsTable.setRowSelectionInterval(0, 0);
194 myExceptionsTable.setColumnSelectionInterval(0, 0);
200 private void updateSignature() {
201 mySignatureLabel.setText(generateSignatureText());
204 private String generateSignatureText() {
205 String name = getNewName();
206 String type = myReturnTypeField.getText().trim();
208 StringBuilder builder = new StringBuilder();
209 if (myPublicRadioButton.isSelected() && type.length() == 0) {
210 builder.append(GrModifier.DEF);
212 if (myPrivateRadioButton.isSelected()) {
213 builder.append(GrModifier.PRIVATE).append(' ');
215 else if (myProtectedRadioButton.isSelected()) {
216 builder.append(GrModifier.PROTECTED).append(' ');
218 builder.append(type).append(' ');
219 builder.append(name).append('(');
221 final List<GrTableParameterInfo> infos = myParameterModel.getParameterInfos();
222 if (infos.size() > 0) {
223 final List<String> paramsText = ContainerUtil.map(infos, new Function<GrTableParameterInfo, String>() {
224 public String fun(GrTableParameterInfo grParameterInfo) {
225 return generateParameterText(grParameterInfo);
228 builder.append("\n").append(INDENT);
229 builder.append(StringUtil.join(paramsText, ",\n" + INDENT));
230 builder.append('\n');
234 final PsiTypeCodeFragment[] exceptions = myExceptionTableModel.getTypeCodeFragments();
235 if (exceptions.length > 0) {
236 builder.append("\nthrows\n");
237 final List<String> exceptionNames = ContainerUtil.map(exceptions, new Function<PsiTypeCodeFragment, String>() {
238 public String fun(PsiTypeCodeFragment fragment) {
239 return fragment.getText();
243 builder.append(INDENT).append(StringUtil.join(exceptionNames, ",\n" + INDENT));
245 return builder.toString();
249 private static String generateParameterText(GrTableParameterInfo info) {
250 StringBuilder builder = new StringBuilder();
251 final PsiTypeCodeFragment typeFragment = info.getTypeFragment();
252 String typeText = typeFragment != null ? typeFragment.getText().trim() : GrModifier.DEF;
253 if (typeText.length() == 0) typeText = GrModifier.DEF;
254 builder.append(typeText).append(' ');
255 final GroovyCodeFragment nameFragment = info.getNameFragment();
256 builder.append(nameFragment != null ? nameFragment.getText().trim() : "");
257 final GroovyCodeFragment defaultInitializer = info.getDefaultInitializerFragment();
259 final String defaultInitializerText = defaultInitializer != null ? defaultInitializer.getText().trim() : "";
260 if (defaultInitializerText.length() > 0) {
261 builder.append(" = ").append(defaultInitializerText);
263 return builder.toString();
267 protected void doAction() {
268 if (!validateInputData()) {
273 String modifier = "";
274 if (myPublicRadioButton.isSelected()) {
275 modifier = GrModifier.PUBLIC;
277 else if (myPrivateRadioButton.isSelected()) {
278 modifier = GrModifier.PRIVATE;
280 else if (myProtectedRadioButton.isSelected()) {
281 modifier = GrModifier.PROTECTED;
284 PsiType returnType = null;
286 returnType = myReturnTypeCodeFragment.getType();
288 catch (PsiTypeCodeFragment.TypeSyntaxException ignored) {
290 catch (PsiTypeCodeFragment.NoTypeException ignored) {
293 String newName = getNewName();
294 final List<GrTableParameterInfo> tableParameterInfos = myParameterModel.getParameterInfos();
295 final List<GrParameterInfo> parameterInfos = ContainerUtil.map(tableParameterInfos, new Function<GrTableParameterInfo, GrParameterInfo>() {
296 public GrParameterInfo fun(GrTableParameterInfo info) {
297 return info.generateParameterInfo();
300 final ThrownExceptionInfo[] exceptionInfos = myExceptionTableModel.getThrownExceptions();
301 invokeRefactoring(new GrChangeSignatureProcessor(myProject, new GrChangeInfoImpl(myMethod, modifier, returnType == null
304 .createTypeWrapper(returnType),
305 newName, parameterInfos, exceptionInfos,
306 myDelegateRadioButton.isSelected())));
309 private String getNewName() {
310 return myNameField.getText().trim();
313 private void showErrorHint(String hint) {
314 CommonRefactoringUtil.showErrorHint(myProject, null, hint, GroovyRefactoringBundle.message("incorrect.data"), HelpID.CHANGE_SIGNATURE);
317 private boolean isGroovyMethodName(String name) {
318 String methodText = "def " + name + "(){}";
320 final GrMethod method = GroovyPsiElementFactory.getInstance(getProject()).createMethodFromText(methodText);
321 return method != null;
323 catch (Throwable e) {
328 private boolean validateInputData() {
329 if (!isGroovyMethodName(getNewName())) {
330 showErrorHint(message("name.is.wrong", getNewName()));
334 if (!checkType(myReturnTypeCodeFragment)) {
335 showErrorHint(message("return.type.is.wrong"));
339 for (GrTableParameterInfo info : myParameterModel.getParameterInfos()) {
340 if (!StringUtil.isJavaIdentifier(info.getName())) {
341 showErrorHint(message("name.is.wrong", info.getName()));
344 if (!checkType(info.getTypeFragment())) {
345 showErrorHint(message("type.for.parameter.is.incorrect", info.getName()));
348 String defaultValue = info.getDefaultValue();
349 if (info.getOldIndex() < 0 && (defaultValue == null || defaultValue.trim().length() == 0)) {
350 showErrorHint(message("specify.default.value", info.getName()));
355 ThrownExceptionInfo[] exceptionInfos = myExceptionTableModel.getThrownExceptions();
356 PsiTypeCodeFragment[] typeCodeFragments = myExceptionTableModel.getTypeCodeFragments();
357 for (int i = 0; i < exceptionInfos.length; i++) {
358 ThrownExceptionInfo exceptionInfo = exceptionInfos[i];
359 PsiTypeCodeFragment typeCodeFragment = typeCodeFragments[i];
361 PsiType type = typeCodeFragment.getType();
362 if (!(type instanceof PsiClassType)) {
363 showErrorHint(GroovyRefactoringBundle.message("changeSignature.wrong.type.for.exception", typeCodeFragment.getText()));
367 PsiClassType throwable = JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory()
368 .createTypeByFQClassName("java.lang.Throwable", type.getResolveScope());
369 if (!throwable.isAssignableFrom(type)) {
370 showErrorHint(GroovyRefactoringBundle.message("changeSignature.not.throwable.type", typeCodeFragment.getText()));
373 exceptionInfo.setType((PsiClassType)type);
375 catch (PsiTypeCodeFragment.TypeSyntaxException e) {
376 showErrorHint(GroovyRefactoringBundle.message("changeSignature.wrong.type.for.exception", typeCodeFragment.getText()));
379 catch (PsiTypeCodeFragment.NoTypeException e) {
380 showErrorHint(GroovyRefactoringBundle.message("changeSignature.no.type.for.exception"));
388 private static boolean checkType(PsiTypeCodeFragment typeCodeFragment) {
390 typeCodeFragment.getType();
392 catch (PsiTypeCodeFragment.TypeSyntaxException e) {
395 catch (PsiTypeCodeFragment.NoTypeException e) {
396 return true; //Groovy accepts methods and parameters without explicit type