diff --git a/java/execution/impl/src/com/intellij/execution/MethodBrowser.java b/java/execution/impl/src/com/intellij/execution/MethodBrowser.java new file mode 100644 index 000000000000..6fe1aa024260 --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/MethodBrowser.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-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 com.intellij.execution; + +import com.intellij.execution.configuration.BrowseModuleValueActionListener; +import com.intellij.execution.junit.JUnitUtil; +import com.intellij.execution.ui.ConfigurationModuleSelector; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; + +public abstract class MethodBrowser extends BrowseModuleValueActionListener { + + public MethodBrowser(final Project project) { + super(project); + } + + protected abstract String getClassName(); + protected abstract ConfigurationModuleSelector getModuleSelector(); + protected abstract Condition getFilter(PsiClass testClass); + + protected String showDialog() { + final String className = getClassName(); + if (className.trim().length() == 0) { + Messages.showMessageDialog(getField(), ExecutionBundle.message("set.class.name.message"), + ExecutionBundle.message("cannot.browse.method.dialog.title"), Messages.getInformationIcon()); + return null; + } + final PsiClass testClass = getModuleSelector().findClass(className); + if (testClass == null) { + Messages.showMessageDialog(getField(), ExecutionBundle.message("class.does.not.exists.error.message", className), + ExecutionBundle.message("cannot.browse.method.dialog.title"), + Messages.getInformationIcon()); + return null; + } + final MethodListDlg dlg = new MethodListDlg(testClass, getFilter(testClass), getField()); + if (dlg.showAndGet()) { + final PsiMethod method = dlg.getSelected(); + if (method != null) { + return method.getName(); + } + } + return null; + } + +} diff --git a/plugins/junit/src/com/intellij/execution/junit2/configuration/MethodListDlg.java b/java/execution/impl/src/com/intellij/execution/MethodListDlg.java similarity index 97% rename from plugins/junit/src/com/intellij/execution/junit2/configuration/MethodListDlg.java rename to java/execution/impl/src/com/intellij/execution/MethodListDlg.java index 71bfae29e6b8..e01943fd1e63 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/configuration/MethodListDlg.java +++ b/java/execution/impl/src/com/intellij/execution/MethodListDlg.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.intellij.execution.junit2.configuration; +package com.intellij.execution; -import com.intellij.execution.ExecutionBundle; import com.intellij.ide.structureView.impl.StructureNodeRenderer; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Condition; diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java index 7aa0574eb131..4886879edfd0 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java @@ -15,16 +15,29 @@ */ package com.intellij.execution.testDiscovery; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.execution.MethodBrowser; +import com.intellij.execution.junit.JUnitUtil; import com.intellij.execution.ui.AlternativeJREPanel; +import com.intellij.execution.ui.ClassBrowser; import com.intellij.execution.ui.CommonJavaParametersPanel; import com.intellij.execution.ui.ConfigurationModuleSelector; +import com.intellij.ide.util.ClassFilter; +import com.intellij.openapi.fileTypes.PlainTextLanguage; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.LabeledComponent; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.EditorTextFieldWithBrowseButton; import com.intellij.ui.PanelWithAnchor; +import com.intellij.util.TextFieldCompletionProvider; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -32,6 +45,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.List; public class TestDiscoveryConfigurable extends SettingsEditor implements PanelWithAnchor { @@ -41,7 +55,9 @@ public class TestDiscoveryConfigurable ext private LabeledComponent myModule = new LabeledComponent(); private CommonJavaParametersPanel myCommonJavaParameters = new CommonJavaParametersPanel(); private AlternativeJREPanel myAlternativeJREPanel = new AlternativeJREPanel(); - private JTextField myPosition = new JTextField(); + private LabeledComponent myClass = new LabeledComponent(); + private LabeledComponent myMethod = new LabeledComponent(); + private ComboBox myChangeLists = new ComboBox(); private JRadioButton myPositionRb = new JRadioButton("Tests for method:"); private JRadioButton myChangesRb = new JRadioButton("Tests for change list:"); @@ -64,7 +80,77 @@ public class TestDiscoveryConfigurable ext GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); panelWithSettings.add(myPositionRb, gc); - panelWithSettings.add(myPosition, gc); + myClass.setText("Class:"); + final ClassBrowser classBrowser = new ClassBrowser(project, "Choose Class") { + @Override + protected ClassFilter.ClassFilterWithScope getFilter() throws NoFilterException { + return new ClassFilter.ClassFilterWithScope() { + @Override + public GlobalSearchScope getScope() { + return GlobalSearchScope.allScope(project); + } + + @Override + public boolean isAccepted(PsiClass aClass) { + return true; + } + }; + } + + @Override + protected PsiClass findClass(String className) { + return JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project)); + } + }; + final EditorTextFieldWithBrowseButton classComponent = new EditorTextFieldWithBrowseButton(project, true); + myClass.setComponent(classComponent); + classBrowser.setField(classComponent); + panelWithSettings.add(myClass, gc); + myMethod.setText("Method:"); + final EditorTextFieldWithBrowseButton textFieldWithBrowseButton = new EditorTextFieldWithBrowseButton(project, true, + JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE, + PlainTextLanguage.INSTANCE.getAssociatedFileType()); + new TextFieldCompletionProvider() { + @Override + protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix, @NotNull CompletionResultSet result) { + final String className = myClass.getComponent().getText(); + if (className.trim().length() == 0) { + return; + } + final PsiClass testClass = getModuleSelector().findClass(className); + if (testClass == null) return; + final JUnitUtil.TestMethodFilter filter = new JUnitUtil.TestMethodFilter(testClass); + for (PsiMethod psiMethod : testClass.getAllMethods()) { + if (filter.value(psiMethod)) { + result.addElement(LookupElementBuilder.create(psiMethod.getName())); + } + } + } + }.apply(textFieldWithBrowseButton.getChildComponent()); + myMethod.setComponent(textFieldWithBrowseButton); + final MethodBrowser methodBrowser = new MethodBrowser(project) { + protected Condition getFilter(final PsiClass testClass) { + return new Condition() { + @Override + public boolean value(PsiMethod method) { + return method.getContainingClass() == testClass; + } + }; + } + + @Override + protected String getClassName() { + return myClass.getComponent().getText().trim(); + } + + @Override + protected ConfigurationModuleSelector getModuleSelector() { + return myModuleSelector; + } + }; + methodBrowser.setField(textFieldWithBrowseButton); + + panelWithSettings.add(myMethod, gc); panelWithSettings.add(myChangesRb, gc); panelWithSettings.add(myChangeLists, gc); @@ -82,7 +168,7 @@ public class TestDiscoveryConfigurable ext myChangesRb.addActionListener(l); - final java.util.List changeLists = ChangeListManager.getInstance(project).getChangeLists(); + final List changeLists = ChangeListManager.getInstance(project).getChangeLists(); final DefaultComboBoxModel model = new DefaultComboBoxModel(); model.addElement("All"); for (LocalChangeList changeList : changeLists) { @@ -109,7 +195,8 @@ public class TestDiscoveryConfigurable ext } private void updateComponents() { - myPosition.setEnabled(myPositionRb.isSelected()); + myClass.setEnabled(myPositionRb.isSelected()); + myMethod.setEnabled(myPositionRb.isSelected()); myChangeLists.setEnabled(myChangesRb.isSelected()); } @@ -117,7 +204,8 @@ public class TestDiscoveryConfigurable ext applyHelpersTo(configuration); configuration.setAlternativeJrePath(myAlternativeJREPanel.getPath()); configuration.setAlternativeJrePathEnabled(myAlternativeJREPanel.isPathEnabled()); - configuration.setPosition(myPositionRb.isSelected() ? myPosition.getText() : null); + configuration.setPosition(myPositionRb.isSelected() ? Pair.create(myClass.getComponent().getText().trim(), + myMethod.getComponent().getText().trim()) : null); if (myChangesRb.isSelected()) { final Object selectedItem = myChangeLists.getSelectedItem(); configuration.setChangeList("All".equals(selectedItem) ? null : (String)selectedItem); @@ -132,10 +220,11 @@ public class TestDiscoveryConfigurable ext myCommonJavaParameters.reset(configuration); getModuleSelector().reset(configuration); myAlternativeJREPanel.init(configuration.getAlternativeJrePath(), configuration.isAlternativeJrePathEnabled()); - final String position = configuration.getPosition(); + final Pair position = configuration.getPosition(); if (position != null) { myPositionRb.setSelected(true); - myPosition.setText(position); + myClass.getComponent().setText(position.first); + myMethod.getComponent().setText(position.second); } else if (myChangesRb.isEnabled()) { myChangesRb.setSelected(true); diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java index cf20ba0fc9e9..3548ffbf46ad 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java @@ -24,7 +24,9 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.options.SettingsEditorGroup; import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.changes.ChangeListManager; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -36,7 +38,7 @@ import java.util.Map; public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBase { private String myChangeList; - private String myPosition; + private Pair myPosition; protected JavaTestConfigurationBase myDelegate; @@ -85,8 +87,13 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa myChangeList != null && ChangeListManager.getInstance(getProject()).findChangeList(myChangeList) == null) { throw new RuntimeConfigurationException("Change list " + myChangeList + " doesn't exist"); } - if (myPosition != null && !myPosition.contains(",")) { - throw new RuntimeConfigurationException("Wrong position format: className,methodName expected"); + if (myPosition != null) { + if (StringUtil.isEmptyOrSpaces(myPosition.first)) { + throw new RuntimeConfigurationException("No class specified"); + } + if (StringUtil.isEmptyOrSpaces(myPosition.second)) { + throw new RuntimeConfigurationException("No method specified"); + } } JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this); } @@ -113,7 +120,9 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa super.readExternal(element); readModule(element); - myPosition = element.getAttributeValue("position"); + final String classQName = element.getAttributeValue("class"); + final String methodName = element.getAttributeValue("method"); + myPosition = classQName != null && methodName != null ? Pair.create(classQName, methodName) : null; myChangeList = element.getAttributeValue("changeList"); if ("All".equals(myChangeList)) { myChangeList = null; @@ -128,7 +137,8 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa writeModule(element); if (myPosition != null) { - element.setAttribute("position", myPosition); + element.setAttribute("class", myPosition.first); + element.setAttribute("method", myPosition.second); } element.setAttribute("changeList", myChangeList == null ? "All" : myChangeList); } @@ -194,7 +204,7 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa return myDelegate.createTestConsoleProperties(executor); } - public void setPosition(String position) { + public void setPosition(Pair position) { myPosition = position; } @@ -202,7 +212,7 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa myChangeList = changeList; } - public String getPosition() { + public Pair getPosition() { return myPosition; } diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurationProducer.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurationProducer.java index 577be4bd75d2..0908b6be1720 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurationProducer.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurationProducer.java @@ -22,6 +22,7 @@ import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.configurations.ConfigurationType; import com.intellij.execution.junit.JavaRunConfigurationProducerBase; import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiClass; @@ -46,25 +47,25 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur assert contextLocation != null; final Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation); if (location == null) return false; - final String position = getPosition(location); + final Pair position = getPosition(location); if (position != null) { try { final Collection testsByMethodName = TestDiscoveryIndex - .getInstance(configuration.getProject()).getTestsByMethodName(position.replace(',', '.')); + .getInstance(configuration.getProject()).getTestsByMethodName(position.first, position.second); if (testsByMethodName == null || testsByMethodName.isEmpty()) return false; } catch (IOException e) { return false; } configuration.setPosition(position); - configuration.setName("Tests for " + StringUtil.getShortName(position)); + configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "," + position.second); setupPackageConfiguration(configurationContext, configuration, TestSearchScope.MODULE_WITH_DEPENDENCIES); return true; } return false; } - private static String getPosition(Location location) { + private static Pair getPosition(Location location) { final PsiElement psiElement = location.getPsiElement(); final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class); if (psiMethod != null) { @@ -76,7 +77,7 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur } final String qualifiedName = containingClass.getQualifiedName(); if (qualifiedName != null) { - return qualifiedName + "," + psiMethod.getName(); + return Pair.create(qualifiedName, psiMethod.getName()); } } } @@ -85,7 +86,7 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur @Override public boolean isConfigurationFromContext(TestDiscoveryConfiguration configuration, ConfigurationContext configurationContext) { - final String position = getPosition(configurationContext.getLocation()); + final Pair position = getPosition(configurationContext.getLocation()); return position != null && position.equals(configuration.getPosition()); } } diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryIndex.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryIndex.java index b1120ea84ba9..1db8875489b4 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryIndex.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryIndex.java @@ -17,6 +17,7 @@ package com.intellij.execution.testDiscovery; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon; @@ -37,309 +38,348 @@ import java.util.zip.InflaterInputStream; * Created by Maxim.Mossienko on 7/9/2015. */ public class TestDiscoveryIndex implements ProjectComponent { - private static final String REMOVED_MARKER = "-removed-"; - private final Object ourLock = new Object(); - private final Project myProject; - private volatile Holder myHolder; + private static final String REMOVED_MARKER = "-removed-"; + private final Object ourLock = new Object(); + private final Project myProject; + private volatile Holder myHolder; - public TestDiscoveryIndex(Project project) { - myProject = project; + public TestDiscoveryIndex(Project project) { + myProject = project; + } + + public Collection getTestsByMethodName(String classFQName, String methodName) throws IOException { + synchronized (ourLock) { + return getHolder().myMethodQNameToTestNames.get(Pair.create(classFQName, methodName)); } + } - public Collection getTestsByMethodName(String fqmethodName) throws IOException { - synchronized (ourLock) { - return getHolder().myMethodQNameToTestNames.get(fqmethodName); - } + private Holder getHolder() { + Holder holder = myHolder; + + if (holder == null) { + synchronized (ourLock) { + holder = myHolder; + if (holder == null) holder = myHolder = new Holder(); + } } + return holder; + } - private Holder getHolder() { - Holder holder = myHolder; + public static TestDiscoveryIndex getInstance(Project project) { + return project.getComponent(TestDiscoveryIndex.class); + } - if (holder == null) { + @Override + public void initComponent() { + } + + @Override + public void disposeComponent() { + synchronized (ourLock) { + Holder holder = myHolder; + if (holder != null) { + holder.dispose(); + myHolder = null; + } + } + } + + @NotNull + @Override + public String getComponentName() { + return getClass().getName(); + } + + @Override + public void projectOpened() { + } + + @Override + public void projectClosed() { + } + + private final class Holder { + final PersistentHashMap, Collection> myMethodQNameToTestNames; + final PersistentHashMap>> myTestNameToUsedClassesAndMethodMap; + private ScheduledFuture myFlushingFuture; + private boolean myDisposed; + + Holder() { + String path = myProject != null ? + TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject) : + "out"; + final File methodQNameToTestNameFile = new File(path + File.separator + "methodQNameToTestName.data"); + final File testNameToUsedClassesAndMethodMapFile = new File(path + File.separator + "testToCalledMethodNames"); + + try { + // todo better io recovery + myMethodQNameToTestNames = IOUtil.openCleanOrResetBroken( + new ThrowableComputable, Collection>, IOException>() { + @Override + public PersistentHashMap, Collection> compute() throws IOException { + return new PersistentHashMap, Collection>( + methodQNameToTestNameFile, + new StringPairKeyDescriptor(), + new DataExternalizer>() { + public void save(@NotNull DataOutput dataOutput, Collection strings) throws IOException { + for (String string : strings) IOUtil.writeUTF(dataOutput, string); + } + + public Collection read(@NotNull DataInput dataInput) throws IOException { + Set result = new THashSet(); + + while (((InputStream)dataInput).available() > 0) { + String string = IOUtil.readUTF(dataInput); + if (REMOVED_MARKER.equals(string)) { + string = IOUtil.readUTF(dataInput); + result.remove(string); + } + else { + result.add(string); + } + } + + return result; + } + } + ); + } + }, methodQNameToTestNameFile); + myTestNameToUsedClassesAndMethodMap = IOUtil.openCleanOrResetBroken( + new ThrowableComputable>>, IOException>() { + @Override + public PersistentHashMap>> compute() throws IOException { + return new PersistentHashMap>>( + testNameToUsedClassesAndMethodMapFile, + EnumeratorStringDescriptor.INSTANCE, + new DataExternalizer>>() { + public void save(@NotNull DataOutput dataOutput, Map> classAndMethodsMap) + throws IOException { + BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(); + Deflater deflater = new Deflater(1); + DataOutputStream dataOutputStream = + new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(out, deflater))); + DataInputOutputUtil.writeINT(dataOutputStream, classAndMethodsMap.size()); + for (Map.Entry> e : classAndMethodsMap.entrySet()) { + IOUtil.writeUTF(dataOutputStream, e.getKey()); + DataInputOutputUtil.writeINT(dataOutputStream, e.getValue().size()); + for (String methodName : e.getValue()) IOUtil.writeUTF(dataOutputStream, methodName); + } + dataOutputStream.close(); + deflater.end(); + dataOutput.write(out.getInternalBuffer(), 0, out.size()); + } + + public Map> read(@NotNull DataInput dataInput) throws IOException { + byte[] buf; + dataInput.readFully(buf = new byte[((InputStream)dataInput).available()]); + DataInputStream dataInputStream = + new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(buf))); + int numberOfClasses = DataInputOutputUtil.readINT(dataInputStream); + THashMap> result = new THashMap>(numberOfClasses); + while (numberOfClasses-- > 0) { + String className = IOUtil.readUTF(dataInputStream); + int numberOfMethods = DataInputOutputUtil.readINT(dataInputStream); + ArrayList methods = new ArrayList(numberOfMethods); + while (numberOfMethods-- > 0) methods.add(IOUtil.readUTF(dataInputStream)); + result.put(className, methods); + } + dataInputStream.close(); + return result; + } + } + ); + } + }, testNameToUsedClassesAndMethodMapFile); + myFlushingFuture = FlushingDaemon.everyFiveSeconds(new Runnable() { + @Override + public void run() { synchronized (ourLock) { - holder = myHolder; - if (holder == null) holder = myHolder = new Holder(); + if (myDisposed) { + myFlushingFuture.cancel(false); + return; + } + if (myMethodQNameToTestNames.isDirty()) { + myMethodQNameToTestNames.force(); + } + if (myTestNameToUsedClassesAndMethodMap.isDirty()) { + myTestNameToUsedClassesAndMethodMap.force(); + } } - } - return holder; + } + }); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } } - public static TestDiscoveryIndex getInstance(Project project) { - return project.getComponent(TestDiscoveryIndex.class); + void dispose() { + assert Thread.holdsLock(ourLock); + try { + myMethodQNameToTestNames.close(); + myTestNameToUsedClassesAndMethodMap.close(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + finally { + myDisposed = true; + } } + - @Override - public void initComponent() {} + } + + private static class StringPairKeyDescriptor implements KeyDescriptor> { + public static final StringPairKeyDescriptor INSTANCE = new StringPairKeyDescriptor(); @Override - public void disposeComponent() { - synchronized (ourLock) { - Holder holder = myHolder; - if (holder != null) { - holder.dispose(); - myHolder = null; - } - } - } - - @NotNull - @Override - public String getComponentName() { - return getClass().getName(); + public void save(@NotNull DataOutput out, Pair value) throws IOException { + IOUtil.writeUTF(out, value.first); + IOUtil.writeUTF(out, value.second); } @Override - public void projectOpened() {} + public Pair read(@NotNull DataInput in) throws IOException { + return Pair.create(IOUtil.readUTF(in), IOUtil.readUTF(in)); + } @Override - public void projectClosed() {} - - private final class Holder { - final PersistentHashMap> myMethodQNameToTestNames; - final PersistentHashMap>> myTestNameToUsedClassesAndMethodMap; - private ScheduledFuture myFlushingFuture; - private boolean myDisposed; - - Holder() { - String path = myProject != null ? - TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject) : - "out"; - final File methodQNameToTestNameFile = new File(path + File.separator + "methodQNameToTestName.data"); - final File testNameToUsedClassesAndMethodMapFile = new File(path + File.separator + "testToCalledMethodNames"); - - try { - // todo better io recovery - myMethodQNameToTestNames = IOUtil.openCleanOrResetBroken( - new ThrowableComputable>, IOException>() { - @Override - public PersistentHashMap> compute() throws IOException { - return new PersistentHashMap>( - methodQNameToTestNameFile, - EnumeratorStringDescriptor.INSTANCE, - new DataExternalizer>() { - public void save(@NotNull DataOutput dataOutput, Collection strings) throws IOException { - for (String string : strings) IOUtil.writeUTF(dataOutput, string); - } - - public Collection read(@NotNull DataInput dataInput) throws IOException { - Set result = new THashSet(); - - while (((InputStream)dataInput).available() > 0) { - String string = IOUtil.readUTF(dataInput); - if (REMOVED_MARKER.equals(string)) { - string = IOUtil.readUTF(dataInput); - result.remove(string); - } - else { - result.add(string); - } - } - - return result; - } - } - ); - } - }, methodQNameToTestNameFile); - myTestNameToUsedClassesAndMethodMap = IOUtil.openCleanOrResetBroken( - new ThrowableComputable>>, IOException>() { - @Override - public PersistentHashMap>> compute() throws IOException { - return new PersistentHashMap>>( - testNameToUsedClassesAndMethodMapFile, - EnumeratorStringDescriptor.INSTANCE, - new DataExternalizer>>() { - public void save(@NotNull DataOutput dataOutput, Map> classAndMethodsMap) - throws IOException { - BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(); - Deflater deflater = new Deflater(1); - DataOutputStream dataOutputStream = - new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(out, deflater))); - DataInputOutputUtil.writeINT(dataOutputStream, classAndMethodsMap.size()); - for (Map.Entry> e : classAndMethodsMap.entrySet()) { - IOUtil.writeUTF(dataOutputStream, e.getKey()); - DataInputOutputUtil.writeINT(dataOutputStream, e.getValue().size()); - for (String methodName : e.getValue()) IOUtil.writeUTF(dataOutputStream, methodName); - } - dataOutputStream.close(); - deflater.end(); - dataOutput.write(out.getInternalBuffer(), 0, out.size()); - } - - public Map> read(@NotNull DataInput dataInput) throws IOException { - byte[] buf; - dataInput.readFully(buf = new byte[((InputStream)dataInput).available()]); - DataInputStream dataInputStream = - new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(buf))); - int numberOfClasses = DataInputOutputUtil.readINT(dataInputStream); - THashMap> result = new THashMap>(numberOfClasses); - while (numberOfClasses-- > 0) { - String className = IOUtil.readUTF(dataInputStream); - int numberOfMethods = DataInputOutputUtil.readINT(dataInputStream); - ArrayList methods = new ArrayList(numberOfMethods); - while (numberOfMethods-- > 0) methods.add(IOUtil.readUTF(dataInputStream)); - result.put(className, methods); - } - dataInputStream.close(); - return result; - } - } - ); - } - }, testNameToUsedClassesAndMethodMapFile); - myFlushingFuture = FlushingDaemon.everyFiveSeconds(new Runnable() { - @Override - public void run() { - synchronized (ourLock) { - if (myDisposed) { - myFlushingFuture.cancel(false); - return; - } - if (myMethodQNameToTestNames.isDirty()) { - myMethodQNameToTestNames.force(); - } - if (myTestNameToUsedClassesAndMethodMap.isDirty()) { - myTestNameToUsedClassesAndMethodMap.force(); - } - } - } - }); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - void dispose() { - assert Thread.holdsLock(ourLock); - try { - myMethodQNameToTestNames.close(); - myTestNameToUsedClassesAndMethodMap.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - myDisposed = true; - } - } + public int getHashCode(Pair value) { + return value.hashCode(); } - public void updateFromTestTrace(File file) throws IOException { - synchronized (ourLock) { - int fileNameDotIndex = file.getName().lastIndexOf('.'); - final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName(); - - Holder holder = getHolder(); - if (holder.myDisposed) return; - Map> classData = loadClassAndMethodsMap(file); - Map> previousClassData = holder.myTestNameToUsedClassesAndMethodMap.get(testName); - - ValueDiff valueDiff = new ValueDiff(classData, previousClassData); - - if (valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty()) { - for(String classQName: valueDiff.myRemovedClassData.keySet()) { - for(String methodName: valueDiff.myRemovedClassData.get(classQName)) { - holder.myMethodQNameToTestNames.appendData(classQName + "." + methodName, - new PersistentHashMap.ValueDataAppender() { - @Override - public void append(DataOutput dataOutput) throws IOException { - IOUtil.writeUTF(dataOutput, REMOVED_MARKER); - IOUtil.writeUTF(dataOutput, testName); - } - } - ); - } - } - } - - if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty()) { - for(String classQName: valueDiff.myAddedOrChangedClassData.keySet()) { - for(String methodName: valueDiff.myAddedOrChangedClassData.get(classQName)) { - holder.myMethodQNameToTestNames.appendData(classQName + "." + methodName, - new PersistentHashMap.ValueDataAppender() { - @Override - public void append(DataOutput dataOutput) throws IOException { - IOUtil.writeUTF(dataOutput, testName); - } - }); - } - } - } - - if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty() || - valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty() - ) { - holder.myTestNameToUsedClassesAndMethodMap.put(testName, classData); - } - } + @Override + public boolean isEqual(Pair val1, Pair val2) { + return val1.equals(val2); } + } + + public void updateFromTestTrace(File file) throws IOException { + synchronized (ourLock) { + int fileNameDotIndex = file.getName().lastIndexOf('.'); + final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName(); - static class ValueDiff { - final Map> myAddedOrChangedClassData; - final Map> myRemovedClassData; + Holder holder = getHolder(); + if (holder.myDisposed) return; + Map> classData = loadClassAndMethodsMap(file); + Map> previousClassData = holder.myTestNameToUsedClassesAndMethodMap.get(testName); - ValueDiff(Map> classData, Map> previousClassData) { - Map> addedOrChangedClassData = classData; - Map> removedClassData = previousClassData; + ValueDiff valueDiff = new ValueDiff(classData, previousClassData); - if (previousClassData != null && !previousClassData.isEmpty()) { - removedClassData = new THashMap>(); - addedOrChangedClassData = new THashMap>(); - - for(String classQName: classData.keySet()) { - List currentMethods = classData.get(classQName); - List previousMethods = previousClassData.get(classQName); - - if (previousMethods == null) { - addedOrChangedClassData.put(classQName, currentMethods); - continue; - } - - THashSet previousMethodsSet = new THashSet(previousMethods); - THashSet currentMethodsSet = new THashSet(currentMethods); - currentMethodsSet.removeAll(previousMethods); - previousMethodsSet.removeAll(currentMethods); - - if (!currentMethodsSet.isEmpty()) { - addedOrChangedClassData.put(classQName, new ArrayList(currentMethodsSet)); - } - if (!previousMethodsSet.isEmpty()) { - removedClassData.put(classQName, new ArrayList(previousMethodsSet)); - } - } - for(String classQName: previousClassData.keySet()) { - if (classData.containsKey(classQName)) continue; - - List previousMethods = previousClassData.get(classQName); - removedClassData.put(classQName, previousMethods); - } - } - - myAddedOrChangedClassData = addedOrChangedClassData; - myRemovedClassData = removedClassData; + if (valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty()) { + for (String classQName : valueDiff.myRemovedClassData.keySet()) { + for (String methodName : valueDiff.myRemovedClassData.get(classQName)) { + holder.myMethodQNameToTestNames.appendData(createKey(classQName, methodName), + new PersistentHashMap.ValueDataAppender() { + @Override + public void append(DataOutput dataOutput) throws IOException { + IOUtil.writeUTF(dataOutput, REMOVED_MARKER); + IOUtil.writeUTF(dataOutput, testName); + } + } + ); + } } - } - @NotNull - private static Map> loadClassAndMethodsMap(File file) throws IOException { - DataInputStream inputStream = new DataInputStream(new InflaterInputStream(new BufferedInputStream(new FileInputStream(file)))); + } - try { - int numberOfClasses = inputStream.readInt(); - Map> classData = new THashMap>(); - while (numberOfClasses-- > 0) { - String classQName = inputStream.readUTF(); - int numberOfMethods = inputStream.readInt(); - List methodsList; - classData.put(classQName, methodsList = new ArrayList(numberOfMethods)); - //System.out.println(classQName + "," + numberOfMethods); - - while (numberOfMethods-- > 0) { - String methodName = inputStream.readUTF(); - methodsList.add(methodName); - //System.out.println(methodName); - } - } - return classData; - } finally { - inputStream.close(); + if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty()) { + for (String classQName : valueDiff.myAddedOrChangedClassData.keySet()) { + for (String methodName : valueDiff.myAddedOrChangedClassData.get(classQName)) { + holder.myMethodQNameToTestNames.appendData(createKey(classQName, methodName), + new PersistentHashMap.ValueDataAppender() { + @Override + public void append(DataOutput dataOutput) throws IOException { + IOUtil.writeUTF(dataOutput, testName); + } + }); + } } + } + + if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty() || + valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty() + ) { + holder.myTestNameToUsedClassesAndMethodMap.put(testName, classData); + } } + } + + private static Pair createKey(String classQName, String methodName) { + return Pair.create(classQName, methodName); + } + + static class ValueDiff { + final Map> myAddedOrChangedClassData; + final Map> myRemovedClassData; + + ValueDiff(Map> classData, Map> previousClassData) { + Map> addedOrChangedClassData = classData; + Map> removedClassData = previousClassData; + + if (previousClassData != null && !previousClassData.isEmpty()) { + removedClassData = new THashMap>(); + addedOrChangedClassData = new THashMap>(); + + for (String classQName : classData.keySet()) { + List currentMethods = classData.get(classQName); + List previousMethods = previousClassData.get(classQName); + + if (previousMethods == null) { + addedOrChangedClassData.put(classQName, currentMethods); + continue; + } + + THashSet previousMethodsSet = new THashSet(previousMethods); + THashSet currentMethodsSet = new THashSet(currentMethods); + currentMethodsSet.removeAll(previousMethods); + previousMethodsSet.removeAll(currentMethods); + + if (!currentMethodsSet.isEmpty()) { + addedOrChangedClassData.put(classQName, new ArrayList(currentMethodsSet)); + } + if (!previousMethodsSet.isEmpty()) { + removedClassData.put(classQName, new ArrayList(previousMethodsSet)); + } + } + for (String classQName : previousClassData.keySet()) { + if (classData.containsKey(classQName)) continue; + + List previousMethods = previousClassData.get(classQName); + removedClassData.put(classQName, previousMethods); + } + } + + myAddedOrChangedClassData = addedOrChangedClassData; + myRemovedClassData = removedClassData; + } + } + + @NotNull + private static Map> loadClassAndMethodsMap(File file) throws IOException { + DataInputStream inputStream = new DataInputStream(new InflaterInputStream(new BufferedInputStream(new FileInputStream(file)))); + + try { + int numberOfClasses = inputStream.readInt(); + Map> classData = new THashMap>(); + while (numberOfClasses-- > 0) { + String classQName = inputStream.readUTF(); + int numberOfMethods = inputStream.readInt(); + List methodsList; + classData.put(classQName, methodsList = new ArrayList(numberOfMethods)); + //System.out.println(classQName + "," + numberOfMethods); + + while (numberOfMethods-- > 0) { + String methodName = inputStream.readUTF(); + methodsList.add(methodName); + //System.out.println(methodName); + } + } + return classData; + } + finally { + inputStream.close(); + } + } } diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoverySearchTask.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoverySearchTask.java index 03865db1d2d2..fc6a1035b6ec 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoverySearchTask.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoverySearchTask.java @@ -20,8 +20,8 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.testframework.SearchForTestsTask; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ContentRevision; @@ -38,10 +38,10 @@ import java.net.ServerSocket; import java.util.*; public abstract class TestDiscoverySearchTask extends SearchForTestsTask { - private final String myPosition; + private final Pair myPosition; private final String myChangeList; - public TestDiscoverySearchTask(Project project, ServerSocket socket, String position, String changeList) { + public TestDiscoverySearchTask(Project project, ServerSocket socket, Pair position, String changeList) { super(project, socket); myPosition = position; myChangeList = changeList; @@ -56,7 +56,7 @@ public abstract class TestDiscoverySearchTask extends SearchForTestsTask { if (myPosition != null) { try { final Collection testsByMethodName = TestDiscoveryIndex - .getInstance(project).getTestsByMethodName(myPosition.replace(',', '.')); + .getInstance(project).getTestsByMethodName(myPosition.first, myPosition.second); if (testsByMethodName != null) { for (String pattern : testsByMethodName) { patterns.add(pattern.replace('-', ',')); @@ -141,10 +141,9 @@ public abstract class TestDiscoverySearchTask extends SearchForTestsTask { if (containingClass != null) { final String qualifiedName = containingClass.getQualifiedName(); if (qualifiedName != null) { - final String methodFQN = StringUtil.getQualifiedName(qualifiedName, psiMethod.getName()); try { final Collection testsByMethodName - = TestDiscoveryIndex.getInstance(containingClass.getProject()).getTestsByMethodName(methodFQN); + = TestDiscoveryIndex.getInstance(containingClass.getProject()).getTestsByMethodName(qualifiedName, psiMethod.getName()); if (testsByMethodName != null) { for (String pattern : testsByMethodName) { patterns.add(pattern.replace('-', ',')); diff --git a/plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.java b/plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.java index 1eed6613c178..dc6c95fa2760 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.java +++ b/plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.java @@ -19,6 +19,7 @@ package com.intellij.execution.junit2.configuration; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.MethodBrowser; import com.intellij.execution.configuration.BrowseModuleValueActionListener; import com.intellij.execution.junit.JUnitConfiguration; import com.intellij.execution.junit.JUnitConfigurationType; @@ -43,6 +44,7 @@ import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.*; import com.intellij.openapi.ui.ex.MessagesEx; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -123,7 +125,21 @@ public class JUnitConfigurable extends SettingsEdi myBrowsers = new BrowseModuleValueActionListener[]{ new PackageChooserActionListener(project), new TestClassBrowser(project), - new MethodBrowser(project), + new MethodBrowser(project) { + protected Condition getFilter(PsiClass testClass) { + return new JUnitUtil.TestMethodFilter(testClass); + } + + @Override + protected String getClassName() { + return JUnitConfigurable.this.getClassName(); + } + + @Override + protected ConfigurationModuleSelector getModuleSelector() { + return myModuleSelector; + } + }, new TestsChooserActionListener(project), new BrowseModuleValueActionListener(project) { @Override @@ -610,36 +626,6 @@ public class JUnitConfigurable extends SettingsEdi } } - private class MethodBrowser extends BrowseModuleValueActionListener { - public MethodBrowser(final Project project) { - super(project); - } - - protected String showDialog() { - final String className = getClassName(); - if (className.trim().length() == 0) { - Messages.showMessageDialog(getField(), ExecutionBundle.message("set.class.name.message"), - ExecutionBundle.message("cannot.browse.method.dialog.title"), Messages.getInformationIcon()); - return null; - } - final PsiClass testClass = getModuleSelector().findClass(className); - if (testClass == null) { - Messages.showMessageDialog(getField(), ExecutionBundle.message("class.does.not.exists.error.message", className), - ExecutionBundle.message("cannot.browse.method.dialog.title"), - Messages.getInformationIcon()); - return null; - } - final MethodListDlg dlg = new MethodListDlg(testClass, new JUnitUtil.TestMethodFilter(testClass), getField()); - if (dlg.showAndGet()) { - final PsiMethod method = dlg.getSelected(); - if (method != null) { - return method.getName(); - } - } - return null; - } - } - private class CategoryBrowser extends ClassBrowser { public CategoryBrowser(Project project) { super(project, "Category Interface"); diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java index 9fdaa87144f3..f28298035d52 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java @@ -26,7 +26,9 @@ import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.MethodBrowser; import com.intellij.execution.configuration.BrowseModuleValueActionListener; +import com.intellij.execution.junit.JUnitUtil; import com.intellij.execution.testframework.TestSearchScope; import com.intellij.execution.ui.AlternativeJREPanel; import com.intellij.execution.ui.CommonJavaParametersPanel; @@ -43,6 +45,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.*; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -115,7 +118,7 @@ public class TestNGConfigurationEditor extends SettingsEditor getFilter(PsiClass testClass) { + return new Condition() { + @Override + public boolean value(PsiMethod method) { + return TestNGUtil.hasTest(method); + } + }; + } + + @Override + protected String getClassName() { + return TestNGConfigurationEditor.this.getClassName(); + } + + @Override + protected ConfigurationModuleSelector getModuleSelector() { + return moduleSelector; + } + } } diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/browser/MethodBrowser.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/browser/MethodBrowser.java deleted file mode 100644 index 1dfcb3ea7c37..000000000000 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/browser/MethodBrowser.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.theoryinpractice.testng.configuration.browser; - -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; -import com.intellij.execution.configuration.BrowseModuleValueActionListener; -import com.theoryinpractice.testng.configuration.TestNGConfigurationEditor; - -/** - * @author Hani Suleiman - * Date: Jul 21, 2005 - * Time: 4:29:00 PM - */ -public class MethodBrowser extends BrowseModuleValueActionListener -{ - private final TestNGConfigurationEditor editor; - - public MethodBrowser(Project project, TestNGConfigurationEditor editor) { - super(project); - this.editor = editor; - } - - @Override - protected String showDialog() { - String className = editor.getClassName(); - if(className.trim().length() == 0) { - Messages.showMessageDialog(getField(), "Set class name first", "Cannot Browse Methods", Messages.getInformationIcon()); - return null; - } - PsiClass psiclass = editor.getModuleSelector().findClass(className); - if(psiclass == null) { - Messages.showMessageDialog(getField(), "Class " + className + " does not exist", "Cannot Browse Methods", Messages.getInformationIcon()); - return null; - } else { - PsiMethod psimethod = MethodList.showDialog(psiclass, getField()); - return psimethod == null ? null : psimethod.getName(); - } - } - -} diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/browser/MethodList.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/browser/MethodList.java deleted file mode 100644 index 0793338779b8..000000000000 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/browser/MethodList.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2000-2013 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.theoryinpractice.testng.configuration.browser; - -import com.intellij.ide.structureView.impl.StructureNodeRenderer; -import com.intellij.openapi.ui.DialogBuilder; -import com.intellij.openapi.util.Condition; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiSubstitutor; -import com.intellij.psi.util.PsiFormatUtil; -import com.intellij.psi.util.PsiFormatUtilBase; -import com.intellij.ui.*; -import com.intellij.ui.components.JBList; -import com.theoryinpractice.testng.util.TestNGUtil; - -import javax.swing.*; -import java.awt.*; -import java.util.Comparator; - -public class MethodList extends JPanel -{ - private final SortedListModel model; - private final JList list; - private final PsiClass psiClass; - - private static final Comparator comparator = new Comparator() { - - public int compare(PsiMethod method1, PsiMethod method2) - { - return method1.getName().compareToIgnoreCase(method2.getName()); - } - }; - - public static class TestMethodFilter implements Condition - { - public boolean value(PsiMethod method) - { - return TestNGUtil.hasTest(method); - } - } - - public MethodList(PsiClass psiClass) - { - super(new BorderLayout()); - model = new SortedListModel(comparator); - list = new JBList(model); - this.psiClass = psiClass; - evaluate(psiClass.getAllMethods(), new TestMethodFilter()); - add(ScrollPaneFactory.createScrollPane(list)); - list.setCellRenderer(new ColoredListCellRenderer() { - - @Override - protected void customizeCellRenderer(JList jlist, Object obj, int i, boolean flag, boolean flag1) - { - PsiMethod psimethod = (PsiMethod)obj; - append(PsiFormatUtil.formatMethod(psimethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME, 0), StructureNodeRenderer.applyDeprecation(psimethod, SimpleTextAttributes.REGULAR_ATTRIBUTES)); - PsiClass psiclass1 = psimethod.getContainingClass(); - if(!MethodList.this.psiClass.equals(psiclass1)) { - append(" (" + psiclass1.getQualifiedName() + ')', StructureNodeRenderer.applyDeprecation(psiclass1, SimpleTextAttributes.GRAY_ATTRIBUTES)); - } - } - }); - list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - ListScrollingUtil.ensureSelectionExists(list); - } - - private void evaluate(PsiMethod methods[], Condition condition) - { - for(PsiMethod psimethod : methods) { - if(condition.value(psimethod)) model.add(psimethod); - } - - } - - public PsiMethod getSelected() - { - return (PsiMethod)list.getSelectedValue(); - } - - public static PsiMethod showDialog(PsiClass psiClass, JComponent component) - { - MethodList list = new MethodList(psiClass); - DialogBuilder builder = new DialogBuilder(component); - builder.setCenterPanel(list); - builder.setPreferredFocusComponent(list.list); - builder.setTitle("Choose Test Method"); - return builder.show() != 0 ? null : list.getSelected(); - } -}