package com.intellij.lang.javascript.flex.flexunit;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.configuration.BrowseModuleValueActionListener;
import com.intellij.execution.configurations.RuntimeConfigurationError;
import com.intellij.javascript.flex.resolve.ActionScriptClassResolver;
import com.intellij.lang.javascript.flex.FlexBundle;
import com.intellij.lang.javascript.flex.FlexMethodChooserDialog;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration;
import com.intellij.lang.javascript.psi.JSFunction;
import com.intellij.lang.javascript.psi.ecmal4.JSClass;
import com.intellij.lang.javascript.refactoring.ui.JSReferenceEditor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class WhatToTestForm {
private JPanel myMainPanel; // needed for form reuse
private JRadioButton myPackageRadioButton;
private JRadioButton myClassRadioButton;
private JRadioButton myMethodRadioButton;
private JLabel myPackageOrClassLabel;
private JSReferenceEditor myPackageField;
private JSReferenceEditor myClassField;
private JPanel myMethodPanel;
private TextFieldWithBrowseButton.NoPathCompletion myMethodField;
private final Project myProject;
private final ThrowableComputable<Module, RuntimeConfigurationError> myModuleComputable;
private final ThrowableComputable<FlexUnitSupport, RuntimeConfigurationError> myFlexUnitSupportComputable;
private TestClassFilter myMainClassFilter;
public WhatToTestForm(final Project project,
final ThrowableComputable<Module, RuntimeConfigurationError> moduleComputable,
final ThrowableComputable<FlexUnitSupport, RuntimeConfigurationError> flexUnitSupportComputable) {
myProject = project;
myModuleComputable = moduleComputable;
myFlexUnitSupportComputable = flexUnitSupportComputable;
final ActionListener scopeChangeListener = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
updateOnScopeChange();
}
};
myClassRadioButton.addActionListener(scopeChangeListener);
myPackageRadioButton.addActionListener(scopeChangeListener);
myMethodRadioButton.addActionListener(scopeChangeListener);
new MethodChooserActionListener(project);
}
private void createUIComponents() {
myMainClassFilter = new TestClassFilter(myProject);
myClassField = JSReferenceEditor.forClassName("", myProject, null, GlobalSearchScope.EMPTY_SCOPE, null, myMainClassFilter,
ExecutionBundle.message("choose.test.class.dialog.title"));
myPackageField = JSReferenceEditor.forPackageName("", myProject, null, GlobalSearchScope.EMPTY_SCOPE,
ExecutionBundle.message("choose.package.dialog.title"));
}
private void updateOnScopeChange() {
if (myClassRadioButton.isSelected()) {
setText(myPackageOrClassLabel, "Class:", 'C');
myMethodPanel.setVisible(false);
myPackageOrClassLabel.setLabelFor(myClassField.getChildComponent());
myMainClassFilter.setAllowSuite(true);
myClassField.setVisible(true);
myClassField.invalidateHighlight();
myPackageField.setVisible(false);
}
else if (myPackageRadioButton.isSelected()) {
setText(myPackageOrClassLabel, "Package:", 'g');
myPackageOrClassLabel.setLabelFor(myPackageField.getChildComponent());
myMethodPanel.setVisible(false);
myClassField.setVisible(false);
myPackageField.setVisible(true);
}
else if (myMethodRadioButton.isSelected()) {
setText(myPackageOrClassLabel, "Class:", 'C');
myPackageOrClassLabel.setLabelFor(myClassField.getChildComponent());
myMethodPanel.setVisible(true);
myMainClassFilter.setAllowSuite(false);
myClassField.setVisible(true);
myClassField.invalidateHighlight();
myPackageField.setVisible(false);
}
}
private static void setText(JLabel label, String text, char mnemonic) {
label.setText(text);
label.setDisplayedMnemonic(mnemonic);
}
public void updateOnBCChange(final @Nullable FlexBuildConfiguration bc, final Module module) {
if (bc == null) {
updateOnError(FlexBundle.message("bc.not.specified"));
return;
}
final FlexUnitSupport support = FlexUnitSupport.getSupport(bc, module);
if (support == null) {
updateOnError(FlexBundle.message("flexunit.not.found.for.bc", bc.getName()));
return;
}
updateControls(GlobalSearchScope.moduleScope(module), support);
}
private void updateControls(final @NotNull GlobalSearchScope scope, final @NotNull FlexUnitSupport support) {
myClassField.setScope(scope);
myMainClassFilter.setSupport(support);
myClassField.setChooserBlockingMessage(null);
myPackageField.setScope(scope);
myPackageField.setChooserBlockingMessage(null);
}
public void updateOnError(final String message) {
myClassField.setScope(GlobalSearchScope.EMPTY_SCOPE);
myMainClassFilter.setSupport(null);
myClassField.setChooserBlockingMessage(message);
myPackageField.setScope(GlobalSearchScope.EMPTY_SCOPE);
myPackageField.setChooserBlockingMessage(message);
}
public void resetFrom(final @Nullable Module module,
final @Nullable FlexBuildConfiguration bc,
final FlexUnitRunnerParameters params) {
switch (params.getScope()) {
case Class:
myClassRadioButton.setSelected(true);
myClassField.setText(params.getClassName());
myPackageField.setText("");
break;
case Method:
myMethodRadioButton.setSelected(true);
myClassField.setText(params.getClassName());
myPackageField.setText("");
myMethodField.setText(params.getMethodName());
break;
case Package:
myPackageRadioButton.setSelected(true);
myClassField.setText("");
myPackageField.setText(params.getPackageName());
break;
default:
assert false : "Unknown scope: " + params.getScope();
}
updateOnBCChange(bc, module);
updateOnScopeChange();
}
public void applyTo(final FlexUnitRunnerParameters params) {
if (myClassRadioButton.isSelected()) {
params.setScope(FlexUnitRunnerParameters.Scope.Class);
params.setClassName(myClassField.getText());
}
else if (myPackageRadioButton.isSelected()) {
params.setScope(FlexUnitRunnerParameters.Scope.Package);
params.setPackageName(myPackageField.getText());
}
else if (myMethodRadioButton.isSelected()) {
params.setScope(FlexUnitRunnerParameters.Scope.Method);
params.setClassName(myClassField.getText());
params.setMethodName(myMethodField.getText());
}
}
@SuppressWarnings("BoundFieldAssignment")
public void dispose() {
myPackageRadioButton = null;
myClassRadioButton = null;
myMethodRadioButton = null;
myPackageOrClassLabel = null;
myClassField = null;
myPackageField = null;
myMethodPanel = null;
myMethodField = null;
}
private class MethodChooserActionListener extends BrowseModuleValueActionListener {
protected MethodChooserActionListener(final Project project) {
super(project);
setField(myMethodField);
}
protected String showDialog() {
if (StringUtil.isEmpty(myClassField.getText())) {
Messages.showInfoMessage(getProject(), ExecutionBundle.message("set.class.name.message"),
ExecutionBundle.message("choose.test.method.dialog.title"));
return null;
}
final Module module;
final FlexUnitSupport support;
try {
module = myModuleComputable.compute();
support = myFlexUnitSupportComputable.compute();
}
catch (RuntimeConfigurationError e) {
Messages.showErrorDialog(getProject(), e.getMessage(), ExecutionBundle.message("choose.test.method.dialog.title"));
return null;
}
final PsiElement clazz =
ActionScriptClassResolver.findClassByQNameStatic(myClassField.getText(), GlobalSearchScope.moduleScope(module));
if (!(clazz instanceof JSClass)) {
Messages.showErrorDialog(getProject(), FlexBundle.message("class.not.found", myClassField.getText()),
ExecutionBundle.message("choose.test.method.dialog.title"));
return null;
}
FlexMethodChooserDialog dialog = new FlexMethodChooserDialog((JSClass)clazz, jsFunction -> support.isTestMethod(jsFunction), myMainPanel, myMethodField.getText());
if (dialog.showAndGet()) {
final JSFunction method = dialog.getSelectedMethod();
return method != null ? method.getName() : null;
}
else {
return null;
}
}
}
private static class TestClassFilter implements Condition<JSClass> {
@NotNull private final Project myProject;
@Nullable private FlexUnitSupport mySupport;
private boolean myAllowSuite;
private Condition<JSClass> myCondition;
public TestClassFilter(@NotNull Project project) {
myProject = project;
setSupport(null);
setAllowSuite(false);
}
private synchronized Condition<JSClass> getCondition() {
if (DumbService.getInstance(myProject).isDumb() || mySupport == null) {
return Conditions.alwaysFalse();
}
if (myCondition == null) {
myCondition = Conditions.cached(jsClass -> {
assert mySupport != null;
return mySupport.isTestClass(jsClass, myAllowSuite);
});
}
return myCondition;
}
public synchronized void setSupport(@Nullable FlexUnitSupport support) {
mySupport = support;
myCondition = null;
}
public synchronized void setAllowSuite(boolean allowSuite) {
myAllowSuite = allowSuite;
myCondition = null;
}
@Override
public boolean value(JSClass jsClass) {
return getCondition().value(jsClass);
}
}
}