blob: 130b85bdbfc5798ff5722167487ec328de58b6d1 [file] [log] [blame]
/*
* User: anna
* Date: 27-Aug-2009
*/
package com.intellij.execution.coverage;
import com.intellij.coverage.*;
import com.intellij.coverage.listeners.CoverageListener;
import com.intellij.execution.CommonJavaRunConfigurationParameters;
import com.intellij.execution.Location;
import com.intellij.execution.RunConfigurationExtension;
import com.intellij.execution.configurations.*;
import com.intellij.execution.configurations.coverage.CoverageConfigurable;
import com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration;
import com.intellij.execution.configurations.coverage.JavaCoverageEnabledConfiguration;
import com.intellij.execution.junit.RefactoringListeners;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.listeners.RefactoringElementListenerComposite;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.util.ArrayUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Registers "Coverage" tab in Java run configurations
*/
public class CoverageJavaRunConfigurationExtension extends RunConfigurationExtension {
public void attachToProcess(@NotNull final RunConfigurationBase configuration, @NotNull ProcessHandler handler, RunnerSettings runnerSettings) {
CoverageDataManager.getInstance(configuration.getProject()).attachToProcess(handler, configuration, runnerSettings);
}
@Nullable
public SettingsEditor createEditor(@NotNull RunConfigurationBase configuration) {
return new CoverageConfigurable(configuration);
}
public String getEditorTitle() {
return CoverageEngine.getEditorTitle();
}
@NotNull
@Override
public String getSerializationId() {
return "coverage";
}
public void updateJavaParameters(RunConfigurationBase configuration, JavaParameters params, RunnerSettings runnerSettings) {
if (!isApplicableFor(configuration)) {
return;
}
final JavaCoverageEnabledConfiguration coverageConfig = JavaCoverageEnabledConfiguration.getFrom(configuration);
//noinspection ConstantConditions
coverageConfig.setCurrentCoverageSuite(null);
final CoverageRunner coverageRunner = coverageConfig.getCoverageRunner();
if (runnerSettings instanceof CoverageRunnerData && coverageRunner != null) {
final CoverageDataManager coverageDataManager = CoverageDataManager.getInstance(configuration.getProject());
coverageConfig.setCurrentCoverageSuite(coverageDataManager.addCoverageSuite(coverageConfig));
coverageConfig.appendCoverageArgument(params);
final Sdk jdk = params.getJdk();
if (jdk != null && JavaSdk.getInstance().isOfVersionOrHigher(jdk, JavaSdkVersion.JDK_1_7) && coverageRunner instanceof JavaCoverageRunner && !((JavaCoverageRunner)coverageRunner).isJdk7Compatible()) {
Notifications.Bus.notify(new Notification("Coverage", "Coverage instrumentation is not fully compatible with JDK 7",
coverageRunner.getPresentableName() +
" coverage instrumentation can lead to java.lang.VerifyError errors with JDK 7. If so, please try IDEA coverage runner.",
NotificationType.WARNING));
}
}
}
@Override
public void readExternal(@NotNull final RunConfigurationBase runConfiguration, @NotNull Element element) throws InvalidDataException {
if (!isApplicableFor(runConfiguration)) {
return;
}
//noinspection ConstantConditions
JavaCoverageEnabledConfiguration.getFrom(runConfiguration).readExternal(element);
}
@Override
public void writeExternal(@NotNull RunConfigurationBase runConfiguration, @NotNull Element element) throws WriteExternalException {
if (!isApplicableFor(runConfiguration)) {
return;
}
//noinspection ConstantConditions
JavaCoverageEnabledConfiguration.getFrom(runConfiguration).writeExternal(element);
}
@Override
public void extendCreatedConfiguration(@NotNull RunConfigurationBase runJavaConfiguration, @NotNull Location location) {
final JavaCoverageEnabledConfiguration coverageEnabledConfiguration = JavaCoverageEnabledConfiguration.getFrom(runJavaConfiguration);
assert coverageEnabledConfiguration != null;
if (runJavaConfiguration instanceof CommonJavaRunConfigurationParameters) {
coverageEnabledConfiguration.setUpCoverageFilters(((CommonJavaRunConfigurationParameters)runJavaConfiguration).getRunClass(),
((CommonJavaRunConfigurationParameters)runJavaConfiguration).getPackage());
}
}
@Override
public void cleanUserData(RunConfigurationBase runConfiguration) {
runConfiguration.putCopyableUserData(CoverageEnabledConfiguration.COVERAGE_KEY, null);
}
@Override
public void validateConfiguration(@NotNull RunConfigurationBase runJavaConfiguration, boolean isExecution)
throws RuntimeConfigurationException {
}
@Override
public RefactoringElementListener wrapElementListener(PsiElement element,
RunConfigurationBase configuration,
RefactoringElementListener listener) {
if (!isApplicableFor(configuration)) {
return listener;
}
final JavaCoverageEnabledConfiguration coverageEnabledConfiguration = JavaCoverageEnabledConfiguration.getFrom(configuration);
if (coverageEnabledConfiguration != null) {
final Project project = configuration.getProject();
final ClassFilter[] patterns = coverageEnabledConfiguration.getCoveragePatterns();
final String[] filters = getFilters(coverageEnabledConfiguration);
if (patterns != null) {
assert filters != null;
if (element instanceof PsiClass) {
final int idx = ArrayUtil.find(filters, ((PsiClass)element).getQualifiedName());
if (idx > -1) {
final RefactoringListeners.Accessor<PsiClass> accessor = new MyClassAccessor(project, patterns, idx, filters);
final RefactoringElementListener classListener = RefactoringListeners.getClassOrPackageListener(element, accessor);
if (classListener != null) {
listener = appendListener(listener, classListener);
}
}
} else if (element instanceof PsiPackage) {
final String qualifiedName = ((PsiPackage)element).getQualifiedName();
for (int i = 0, filtersLength = filters.length; i < filtersLength; i++) {
if (filters[i].startsWith(qualifiedName + ".")) {
final RefactoringElementListener packageListener;
if (filters[i].endsWith("*")) {
packageListener = RefactoringListeners.getListener((PsiPackage)element,
new MyPackageAccessor(project, patterns, i, filters));
} else {
packageListener = RefactoringListeners.getClassOrPackageListener(element,
new MyClassAccessor(project, patterns, i, filters));
}
if (packageListener != null) {
listener = appendListener(listener, packageListener);
}
}
}
}
}
}
return listener;
}
@Nullable
private static String[] getFilters(JavaCoverageEnabledConfiguration coverageEnabledConfiguration) {
final ClassFilter[] patterns = coverageEnabledConfiguration.getCoveragePatterns();
if (patterns != null) {
final List<String> filters = new ArrayList<String>();
for (ClassFilter classFilter : patterns) {
filters.add(classFilter.getPattern());
}
return ArrayUtil.toStringArray(filters);
}
return null;
}
private static RefactoringElementListener appendListener(RefactoringElementListener listener,
final RefactoringElementListener classOrPackageListener) {
if (listener == null) {
listener = new RefactoringElementListenerComposite();
} else if (!(listener instanceof RefactoringElementListenerComposite)) {
final RefactoringElementListenerComposite composite = new RefactoringElementListenerComposite();
composite.addListener(listener);
listener = composite;
}
((RefactoringElementListenerComposite)listener).addListener(classOrPackageListener);
return listener;
}
@Override
public boolean isListenerDisabled(RunConfigurationBase configuration, Object listener, RunnerSettings runnerSettings) {
if (listener instanceof CoverageListener) {
if (!(runnerSettings instanceof CoverageRunnerData)) return true;
final CoverageEnabledConfiguration coverageEnabledConfiguration = CoverageEnabledConfiguration.getOrCreate(configuration);
return !(coverageEnabledConfiguration.getCoverageRunner() instanceof IDEACoverageRunner) ||
!(coverageEnabledConfiguration.isTrackPerTestCoverage() && !coverageEnabledConfiguration.isSampling());
}
return false;
}
protected boolean isApplicableFor(@NotNull final RunConfigurationBase configuration) {
return CoverageEnabledConfiguration.isApplicableTo(configuration);
}
private static class MyPackageAccessor extends MyAccessor implements RefactoringListeners.Accessor<PsiPackage> {
private MyPackageAccessor(Project project, ClassFilter[] patterns, int idx, String[] filters) {
super(project, patterns, idx, filters);
}
public void setName(String qualifiedName) {
super.setName(qualifiedName + ".*");
}
public PsiPackage getPsiElement() {
final String name = getName();
return JavaPsiFacade.getInstance(getProject()).findPackage(name.substring(0, name.length() - ".*".length()));
}
public void setPsiElement(PsiPackage psiElement) {
setName(psiElement.getQualifiedName());
}
}
private static class MyClassAccessor extends MyAccessor implements RefactoringListeners.Accessor<PsiClass> {
private MyClassAccessor(Project project, ClassFilter[] patterns, int idx, String[] filters) {
super(project, patterns, idx, filters);
}
public PsiClass getPsiElement() {
return JavaPsiFacade.getInstance(getProject()).findClass(getName(), GlobalSearchScope.allScope(getProject()));
}
public void setPsiElement(PsiClass psiElement) {
setName(psiElement.getQualifiedName());
}
}
private static class MyAccessor {
private final Project myProject;
private final ClassFilter[] myPatterns;
private final int myIdx;
private final String[] myFilters;
private MyAccessor(Project project, ClassFilter[] patterns, int idx, String[] filters) {
myProject = project;
myPatterns = patterns;
myIdx = idx;
myFilters = filters;
}
public void setName(String qName) {
myPatterns[myIdx] = new ClassFilter(qName);
}
public String getName() {
return myFilters[myIdx];
}
public Project getProject() {
return myProject;
}
}
}