/* * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.PsiElement; import com.intellij.refactoring.ui.NameSuggestionsField; import com.intellij.ui.TitledSeparator; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import kotlin.Unit; import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.KotlinFileType; import org.jetbrains.kotlin.idea.core.KotlinNameSuggester; import org.jetbrains.kotlin.idea.core.UtilsKt; import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt; import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle; import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*; import org.jetbrains.kotlin.idea.refactoring.introduce.ui.KotlinSignatureComponent; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.types.KotlinType; import javax.swing.*; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.*; import java.util.List; public class KotlinExtractFunctionDialog extends DialogWrapper { private JPanel contentPane; private TitledSeparator inputParametersPanel; private JComboBox visibilityBox; private KotlinSignatureComponent signaturePreviewField; private JPanel functionNamePanel; private NameSuggestionsField functionNameField; private JLabel functionNameLabel; private JComboBox returnTypeBox; private JPanel returnTypePanel; private ExtractFunctionParameterTablePanel parameterTablePanel; private final Project project; private final ExtractableCodeDescriptorWithConflicts originalDescriptor; private ExtractableCodeDescriptor currentDescriptor; private final Function1<KotlinExtractFunctionDialog, Unit> onAccept; public KotlinExtractFunctionDialog( @NotNull Project project, @NotNull ExtractableCodeDescriptorWithConflicts originalDescriptor, @NotNull Function1<KotlinExtractFunctionDialog, Unit> onAccept) { super(project, true); this.project = project; this.originalDescriptor = originalDescriptor; this.currentDescriptor = originalDescriptor.getDescriptor(); this.onAccept = onAccept; setModal(true); setTitle(KotlinRefactoringBundle.message("extract.function")); init(); update(); } private void createUIComponents() { this.signaturePreviewField = new KotlinSignatureComponent("", project); } private boolean isVisibilitySectionAvailable() { return ExtractableAnalysisUtilKt.isVisibilityApplicable(originalDescriptor.getDescriptor().getExtractionData()); } private String getFunctionName() { return UtilsKt.quoteIfNeeded(functionNameField.getEnteredName()); } private String getVisibility() { if (!isVisibilitySectionAvailable()) return ""; String value = (String) visibilityBox.getSelectedItem(); return KtTokens.PUBLIC_KEYWORD.getValue().equals(value) ? "" : value; } private boolean checkNames() { if (!KotlinNameSuggester.INSTANCE.isIdentifier(getFunctionName())) return false; for (ExtractFunctionParameterTablePanel.ParameterInfo parameterInfo : parameterTablePanel.getSelectedParameterInfos()) { if (!KotlinNameSuggester.INSTANCE.isIdentifier(parameterInfo.getName())) return false; } return true; } private void update() { this.currentDescriptor = createDescriptor(); setOKActionEnabled(checkNames()); signaturePreviewField.setText( ExtractorUtilKt.getSignaturePreview(getCurrentConfiguration(), IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES) ); } @Override protected void init() { super.init(); ExtractableCodeDescriptor extractableCodeDescriptor = originalDescriptor.getDescriptor(); functionNameField = new NameSuggestionsField( ArrayUtil.toStringArray(extractableCodeDescriptor.getSuggestedNames()), project, KotlinFileType.INSTANCE ); functionNameField.addDataChangedListener( new NameSuggestionsField.DataChanged() { @Override public void dataChanged() { update(); } } ); functionNamePanel.add(functionNameField, BorderLayout.CENTER); functionNameLabel.setLabelFor(functionNameField); List<KotlinType> possibleReturnTypes = ExtractableCodeDescriptorKt.getPossibleReturnTypes(extractableCodeDescriptor.getControlFlow()); if (possibleReturnTypes.size() > 1) { DefaultComboBoxModel returnTypeBoxModel = new DefaultComboBoxModel(possibleReturnTypes.toArray()); returnTypeBox.setModel(returnTypeBoxModel); returnTypeBox.setRenderer( new DefaultListCellRenderer() { @NotNull @Override public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus ) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setText(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType((KotlinType) value)); return this; } } ); returnTypeBox.addItemListener( new ItemListener() { @Override public void itemStateChanged(@NotNull ItemEvent e) { update(); } } ); } else { returnTypePanel.getParent().remove(returnTypePanel); } boolean enableVisibility = isVisibilitySectionAvailable(); visibilityBox.setEnabled(enableVisibility); if (enableVisibility) { String defaultVisibility = extractableCodeDescriptor.getVisibility(); if (defaultVisibility.isEmpty()) { defaultVisibility = KtTokens.PUBLIC_KEYWORD.getValue(); } visibilityBox.setSelectedItem(defaultVisibility); } visibilityBox.addItemListener( new ItemListener() { @Override public void itemStateChanged(@NotNull ItemEvent e) { update(); } } ); parameterTablePanel = new ExtractFunctionParameterTablePanel() { @Override protected void updateSignature() { KotlinExtractFunctionDialog.this.update(); } @Override protected void onEnterAction() { doOKAction(); } @Override protected void onCancelAction() { doCancelAction(); } }; parameterTablePanel.init(extractableCodeDescriptor.getReceiverParameter(), extractableCodeDescriptor.getParameters()); inputParametersPanel.setText("&Parameters"); inputParametersPanel.setLabelFor(parameterTablePanel.getTable()); inputParametersPanel.add(parameterTablePanel); } @SuppressWarnings("SuspiciousMethodCalls") @Override protected void doOKAction() { MultiMap<PsiElement, String> conflicts = ExtractableAnalysisUtilKt.validate(currentDescriptor).getConflicts(); conflicts.values().removeAll(originalDescriptor.getConflicts().values()); KotlinRefactoringUtilKt.checkConflictsInteractively( project, conflicts, new Function0<Unit>() { @Override public Unit invoke() { close(OK_EXIT_CODE); return Unit.INSTANCE; } }, new Function0<Unit>() { @Override public Unit invoke() { KotlinExtractFunctionDialog.super.doOKAction(); return onAccept.invoke(KotlinExtractFunctionDialog.this); } } ); } @Override public JComponent getPreferredFocusedComponent() { return functionNameField; } @Override protected JComponent createCenterPanel() { return contentPane; } @NotNull @Override protected JComponent createContentPane() { return contentPane; } @NotNull private ExtractableCodeDescriptor createDescriptor() { return createNewDescriptor(originalDescriptor.getDescriptor(), getFunctionName(), getVisibility(), parameterTablePanel.getSelectedReceiverInfo(), parameterTablePanel.getSelectedParameterInfos(), (KotlinType) returnTypeBox.getSelectedItem()); } @NotNull public ExtractionGeneratorConfiguration getCurrentConfiguration() { return new ExtractionGeneratorConfiguration(currentDescriptor, ExtractionGeneratorOptions.DEFAULT); } public static ExtractableCodeDescriptor createNewDescriptor( @NotNull ExtractableCodeDescriptor originalDescriptor, @NotNull String newName, @NotNull String newVisibility, @Nullable ExtractFunctionParameterTablePanel.ParameterInfo newReceiverInfo, @NotNull List<ExtractFunctionParameterTablePanel.ParameterInfo> newParameterInfos, @Nullable KotlinType returnType ) { Map<Parameter, Parameter> oldToNewParameters = ContainerUtil.newLinkedHashMap(); for (ExtractFunctionParameterTablePanel.ParameterInfo parameterInfo : newParameterInfos) { oldToNewParameters.put(parameterInfo.getOriginalParameter(), parameterInfo.toParameter()); } Parameter originalReceiver = originalDescriptor.getReceiverParameter(); Parameter newReceiver = newReceiverInfo != null ? newReceiverInfo.toParameter() : null; if (originalReceiver != null && newReceiver != null) { oldToNewParameters.put(originalReceiver, newReceiver); } return ExtractableCodeDescriptorKt.copy(originalDescriptor, newName, newVisibility, oldToNewParameters, newReceiver, returnType); } }