blob: f79280fd1365f06032fa36722f6b2ae7c23ef74f [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.extractMethodObject;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.extractMethod.AbstractExtractDialog;
import com.intellij.refactoring.extractMethod.InputVariables;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.ParameterTablePanel;
import com.intellij.refactoring.util.VariableData;
import com.intellij.ui.EditorTextField;
import com.intellij.util.Function;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
public class ExtractMethodObjectDialog extends DialogWrapper implements AbstractExtractDialog {
private final Project myProject;
private final PsiType myReturnType;
private final PsiTypeParameterList myTypeParameterList;
private final PsiType[] myExceptions;
private final boolean myStaticFlag;
private final boolean myCanBeStatic;
private final PsiElement[] myElementsToExtract;
private final boolean myMultipleExitPoints;
private final InputVariables myVariableData;
private final PsiClass myTargetClass;
private final boolean myWasStatic;
private JRadioButton myCreateInnerClassRb;
private JRadioButton myCreateAnonymousClassWrapperRb;
private JTextArea mySignatureArea;
private JCheckBox myCbMakeStatic;
private JCheckBox myCbMakeVarargs;
private JCheckBox myCbMakeVarargsAnonymous;
private JPanel myWholePanel;
private JPanel myParametersTableContainer;
private JRadioButton myPrivateRadioButton;
private JRadioButton myProtectedRadioButton;
private JRadioButton myPackageLocalRadioButton;
private JRadioButton myPublicRadioButton;
private EditorTextField myInnerClassName;
private EditorTextField myMethodName;
private JPanel myInnerClassPanel;
private JPanel myAnonymousClassPanel;
private JCheckBox myFoldCb;
private ButtonGroup myVisibilityGroup;
private VariableData[] myInputVariables;
public ExtractMethodObjectDialog(Project project, PsiClass targetClass, final InputVariables inputVariables, PsiType returnType,
PsiTypeParameterList typeParameterList, PsiType[] exceptions, boolean isStatic, boolean canBeStatic,
final PsiElement[] elementsToExtract, final boolean multipleExitPoints) {
super(project, true);
myProject = project;
myTargetClass = targetClass;
myReturnType = returnType;
myTypeParameterList = typeParameterList;
myExceptions = exceptions;
myStaticFlag = isStatic;
myCanBeStatic = canBeStatic;
myElementsToExtract = elementsToExtract;
myMultipleExitPoints = multipleExitPoints;
boolean canBeVarargs = false;
for (VariableData data : inputVariables.getInputVariables()) {
canBeVarargs |= data.type instanceof PsiArrayType;
}
canBeVarargs |= inputVariables.isFoldable() && inputVariables.isFoldingSelectedByDefault();
myWasStatic = canBeVarargs;
myVariableData = inputVariables;
setTitle(ExtractMethodObjectProcessor.REFACTORING_NAME);
// Create UI components
myCbMakeVarargs.setVisible(canBeVarargs);
myCbMakeVarargsAnonymous.setVisible(canBeVarargs);
// Initialize UI
init();
}
public boolean isMakeStatic() {
if (myStaticFlag) return true;
if (!myCanBeStatic) return false;
return myCbMakeStatic.isSelected();
}
public boolean isChainedConstructor() {
return false;
}
@NotNull
protected Action[] createActions() {
return new Action[]{getOKAction(), getCancelAction(), getHelpAction()};
}
public String getChosenMethodName() {
return myCreateInnerClassRb.isSelected() ? myInnerClassName.getText() : myMethodName.getText();
}
public VariableData[] getChosenParameters() {
return myInputVariables;
}
public JComponent getPreferredFocusedComponent() {
return myInnerClassName;
}
protected void doHelpAction() {
HelpManager.getInstance().invokeHelp(HelpID.EXTRACT_METHOD_OBJECT);
}
protected void doOKAction() {
MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
if (myCreateInnerClassRb.isSelected()) {
final PsiClass innerClass = myTargetClass.findInnerClassByName(myInnerClassName.getText(), false);
if (innerClass != null) {
conflicts.putValue(innerClass, "Inner class " + myInnerClassName.getText() + " already defined in class " + myTargetClass.getName());
}
}
if (conflicts.size() > 0) {
final ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts);
conflictsDialog.show();
if (!conflictsDialog.isOK()){
if (conflictsDialog.isShowConflicts()) close(CANCEL_EXIT_CODE);
return;
}
}
final JCheckBox makeVarargsCb = myCreateInnerClassRb.isSelected() ? myCbMakeVarargs : myCbMakeVarargsAnonymous;
if (makeVarargsCb != null && makeVarargsCb.isSelected()) {
final VariableData data = myInputVariables[myInputVariables.length - 1];
if (data.type instanceof PsiArrayType) {
data.type = new PsiEllipsisType(((PsiArrayType)data.type).getComponentType());
}
}
super.doOKAction();
}
private void updateVarargsEnabled() {
final boolean enabled = myInputVariables.length > 0 && myInputVariables[myInputVariables.length - 1].type instanceof PsiArrayType;
if (myCreateInnerClassRb.isSelected()) {
myCbMakeVarargs.setEnabled(enabled);
} else {
myCbMakeVarargsAnonymous.setEnabled(enabled);
}
}
private void update() {
myCbMakeStatic.setEnabled(myCreateInnerClassRb.isSelected() && myCanBeStatic && !myStaticFlag);
updateSignature();
final PsiNameHelper helper = PsiNameHelper.getInstance(myProject);
setOKActionEnabled((myCreateInnerClassRb.isSelected() && helper.isIdentifier(myInnerClassName.getText())) ||
(!myCreateInnerClassRb.isSelected() && helper.isIdentifier(myMethodName.getText())));
}
public String getVisibility() {
if (myPublicRadioButton.isSelected()) {
return PsiModifier.PUBLIC;
}
if (myPackageLocalRadioButton.isSelected()) {
return PsiModifier.PACKAGE_LOCAL;
}
if (myProtectedRadioButton.isSelected()) {
return PsiModifier.PROTECTED;
}
if (myPrivateRadioButton.isSelected()) {
return PsiModifier.PRIVATE;
}
return null;
}
protected JComponent createCenterPanel() {
mySignatureArea.setEditable(false);
myCreateInnerClassRb.setSelected(true);
final ActionListener enableDisableListener = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
enable(myCreateInnerClassRb.isSelected());
}
};
myCreateInnerClassRb.addActionListener(enableDisableListener);
myCreateAnonymousClassWrapperRb.addActionListener(enableDisableListener);
myCreateAnonymousClassWrapperRb.setEnabled(!myMultipleExitPoints);
myFoldCb.setSelected(myVariableData.isFoldingSelectedByDefault());
myFoldCb.setVisible(myVariableData.isFoldable());
myVariableData.setFoldingAvailable(myFoldCb.isSelected());
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
myFoldCb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myVariableData.setFoldingAvailable(myFoldCb.isSelected());
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
myParametersTableContainer.removeAll();
myParametersTableContainer.add(createParametersPanel(), BorderLayout.CENTER);
updateSignature();
updateVarargsEnabled();
}
});
myParametersTableContainer.add(createParametersPanel(), BorderLayout.CENTER);
final ActionListener updateSugnatureListener = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
updateSignature();
IdeFocusManager.getInstance(myProject).requestFocus(myCreateInnerClassRb.isSelected() ? myInnerClassName : myMethodName, false);
}
};
if (myStaticFlag || myCanBeStatic) {
myCbMakeStatic.setEnabled(!myStaticFlag);
myCbMakeStatic.setSelected(myStaticFlag);
myCbMakeStatic.addActionListener(updateSugnatureListener);
} else {
myCbMakeStatic.setSelected(false);
myCbMakeStatic.setEnabled(false);
}
updateVarargsEnabled();
myCbMakeVarargs.setSelected(myWasStatic);
myCbMakeVarargs.addActionListener(updateSugnatureListener);
myCbMakeVarargsAnonymous.setSelected(myWasStatic);
myCbMakeVarargsAnonymous.addActionListener(updateSugnatureListener);
final DocumentAdapter nameListener = new DocumentAdapter() {
@Override
public void documentChanged(final DocumentEvent e) {
update();
}
};
myInnerClassName.getDocument().addDocumentListener(nameListener);
myMethodName.getDocument().addDocumentListener(nameListener);
myPrivateRadioButton.setSelected(true);
myCreateInnerClassRb.addActionListener(updateSugnatureListener);
myCreateAnonymousClassWrapperRb.addActionListener(updateSugnatureListener);
final Enumeration<AbstractButton> visibilities = myVisibilityGroup.getElements();
while(visibilities.hasMoreElements()) {
visibilities.nextElement().addActionListener(updateSugnatureListener);
}
enable(true);
return myWholePanel;
}
private void enable(boolean innerClassSelected){
UIUtil.setEnabled(myInnerClassPanel, innerClassSelected, true);
UIUtil.setEnabled(myAnonymousClassPanel, !innerClassSelected, true);
update();
}
private JComponent createParametersPanel() {
return new ParameterTablePanel(myProject, myInputVariables, myElementsToExtract) {
protected void updateSignature() {
updateVarargsEnabled();
ExtractMethodObjectDialog.this.updateSignature();
}
protected void doEnterAction() {
clickDefaultButton();
}
protected void doCancelAction() {
ExtractMethodObjectDialog.this.doCancelAction();
}
@Override
protected boolean isUsedAfter(PsiVariable variable) {
return ExtractMethodObjectDialog.this.isUsedAfter(variable);
}
};
}
protected boolean isUsedAfter(PsiVariable variable) {
return false;
}
protected void updateSignature() {
if (mySignatureArea == null) return;
@NonNls StringBuffer buffer = getSignature();
mySignatureArea.setText(buffer.toString());
}
protected StringBuffer getSignature() {
final String INDENT = " ";
@NonNls StringBuffer buffer = new StringBuffer();
final String visibilityString = VisibilityUtil.getVisibilityString(getVisibility());
if (myCreateInnerClassRb.isSelected()) {
buffer.append(visibilityString);
if (buffer.length() > 0) {
buffer.append(" ");
}
if (isMakeStatic()) {
buffer.append("static ");
}
buffer.append("class ");
buffer.append(myInnerClassName.getText());
if (myTypeParameterList != null) {
buffer.append(myTypeParameterList.getText());
buffer.append(" ");
}
buffer.append("{\n");
buffer.append(INDENT);
buffer.append("public ");
buffer.append(myInnerClassName.getText());
methodSignature(INDENT, buffer);
buffer.append("\n}");
} else {
buffer.append("new Object(){\n");
buffer.append(INDENT);
buffer.append("private ");
buffer.append(PsiFormatUtil.formatType(myReturnType, 0, PsiSubstitutor.EMPTY));
buffer.append(" ");
buffer.append(myMethodName.getText());
methodSignature(INDENT, buffer);
buffer.append("\n}.");
buffer.append(myMethodName.getText());
buffer.append("(");
buffer.append(StringUtil.join(myInputVariables, new Function<VariableData, String>() {
public String fun(final VariableData variableData) {
return variableData.name;
}
}, ", "));
buffer.append(")");
}
return buffer;
}
private void methodSignature(final String INDENT, final StringBuffer buffer) {
buffer.append("(");
int count = 0;
final String indent = " ";
for (int i = 0; i < myInputVariables.length; i++) {
VariableData data = myInputVariables[i];
if (data.passAsParameter) {
//String typeAndModifiers = PsiFormatUtil.formatVariable(data.variable,
// PsiFormatUtil.SHOW_MODIFIERS | PsiFormatUtil.SHOW_TYPE);
PsiType type = data.type;
if (i == myInputVariables.length - 1 && type instanceof PsiArrayType && ((myCreateInnerClassRb.isSelected() && myCbMakeVarargs.isSelected()) || (myCreateAnonymousClassWrapperRb.isSelected() && myCbMakeVarargsAnonymous.isSelected()))) {
type = new PsiEllipsisType(((PsiArrayType)type).getComponentType());
}
String typeText = type.getPresentableText();
if (count > 0) {
buffer.append(", ");
}
buffer.append("\n");
buffer.append(indent);
buffer.append(typeText);
buffer.append(" ");
buffer.append(data.name);
count++;
}
}
buffer.append(")");
if (myExceptions.length > 0) {
buffer.append("\n");
buffer.append("throws\n");
for (PsiType exception : myExceptions) {
buffer.append(INDENT);
buffer.append(PsiFormatUtil.formatType(exception, 0, PsiSubstitutor.EMPTY));
buffer.append("\n");
}
}
buffer.append("{}");
}
public boolean createInnerClass() {
return myCreateInnerClassRb.isSelected();
}
}