diff --git a/java/openapi/src/com/intellij/ui/classFilter/ClassFilter.java b/java/openapi/src/com/intellij/ui/classFilter/ClassFilter.java index b93deafa1fe8..447743548290 100644 --- a/java/openapi/src/com/intellij/ui/classFilter/ClassFilter.java +++ b/java/openapi/src/com/intellij/ui/classFilter/ClassFilter.java @@ -38,6 +38,10 @@ public class ClassFilter implements JDOMExternalizable, Cloneable{ public String PATTERN = ""; @Attribute("enabled") public boolean ENABLED = true; + + @Attribute("include") + public boolean INCLUDE = true; + private Matcher myMatcher; // to speedup matching public ClassFilter() { @@ -58,6 +62,11 @@ public class ClassFilter implements JDOMExternalizable, Cloneable{ return ENABLED; } + @Transient + public boolean isInclude() { + return INCLUDE; + } + public void setPattern(String pattern) { if (pattern != null && !pattern.equals(PATTERN)) { PATTERN = pattern; @@ -68,6 +77,10 @@ public class ClassFilter implements JDOMExternalizable, Cloneable{ ENABLED = value; } + public void setInclude(boolean value) { + INCLUDE = value; + } + public String toString() { return getPattern(); } @@ -79,7 +92,11 @@ public class ClassFilter implements JDOMExternalizable, Cloneable{ @Override public void writeExternal(Element element) throws WriteExternalException { - DefaultJDOMExternalizer.writeExternal(this, element); + element.addContent(new Element("option").setAttribute("name", "PATTERN").setAttribute("value", PATTERN)); + element.addContent(new Element("option").setAttribute("name", "ENABLED").setAttribute("value", String.valueOf(ENABLED))); + if (!INCLUDE) { + element.addContent(new Element("option").setAttribute("name", "INCLUDE").setAttribute("value", "false")); + } } public boolean equals(Object o) { diff --git a/java/openapi/src/com/intellij/ui/classFilter/ClassFilterEditor.java b/java/openapi/src/com/intellij/ui/classFilter/ClassFilterEditor.java index 1d465e755e36..681ca18e912d 100644 --- a/java/openapi/src/com/intellij/ui/classFilter/ClassFilterEditor.java +++ b/java/openapi/src/com/intellij/ui/classFilter/ClassFilterEditor.java @@ -48,12 +48,14 @@ import java.util.LinkedList; import java.util.List; public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText { + private static final String IS_ACTIVE = "Is Active"; protected JBTable myTable = null; protected FilterTableModel myTableModel = null; protected final Project myProject; private final ClassFilter myChooserFilter; @Nullable private final String myPatternsHelpId; + private final boolean myExcludeAllowed; private String classDelimiter = "$"; public ClassFilterEditor(Project project) { @@ -65,8 +67,13 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText } public ClassFilterEditor(Project project, ClassFilter classFilter, @Nullable String patternsHelpId) { + this(project, classFilter, patternsHelpId, false); + } + + public ClassFilterEditor(Project project, ClassFilter classFilter, @Nullable String patternsHelpId, boolean excludeAllowed) { super(new BorderLayout()); myPatternsHelpId = patternsHelpId; + myExcludeAllowed = excludeAllowed; myTable = new JBTable(); final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTable) @@ -111,14 +118,27 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText myTable.setModel(myTableModel); myTable.setShowGrid(false); myTable.setIntercellSpacing(new Dimension(0, 0)); - myTable.setTableHeader(null); myTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); myTable.setColumnSelectionAllowed(false); myTable.setPreferredScrollableViewportSize(new Dimension(200, myTable.getRowHeight() * JBTable.PREFERRED_SCROLLABLE_VIEWPORT_HEIGHT_IN_ROWS)); TableColumnModel columnModel = myTable.getColumnModel(); TableColumn column = columnModel.getColumn(FilterTableModel.CHECK_MARK); - TableUtil.setupCheckboxColumn(column); + int preferredWidth; + if (!excludeAllowed) { + myTable.setTableHeader(null); + preferredWidth = 0; + } + else { + JTableHeader tableHeader = myTable.getTableHeader(); + final FontMetrics fontMetrics = tableHeader.getFontMetrics(tableHeader.getFont()); + preferredWidth = fontMetrics.stringWidth(IS_ACTIVE) + 20; + + TableColumn includeColumn = columnModel.getColumn(FilterTableModel.INCLUDE_MARK); + includeColumn.setCellRenderer(new EnabledCellRenderer(myTable.getDefaultRenderer(Boolean.class))); + TableUtil.setupCheckboxColumn(includeColumn, preferredWidth); + } + TableUtil.setupCheckboxColumn(column, preferredWidth); column.setCellRenderer(new EnabledCellRenderer(myTable.getDefaultRenderer(Boolean.class))); columnModel.getColumn(FilterTableModel.FILTER).setCellRenderer(new FilterCellRenderer()); @@ -177,6 +197,7 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText private final List myFilters = new LinkedList<>(); public static final int CHECK_MARK = 0; public static final int FILTER = 1; + public static final int INCLUDE_MARK = 2; public final void setFilters(com.intellij.ui.classFilter.ClassFilter[] filters) { myFilters.clear(); @@ -216,9 +237,23 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText } public int getColumnCount() { + if (myExcludeAllowed) { + return 3; + } return 2; } + @Override + public String getColumnName(int column) { + if (column == FILTER) { + return "Pattern"; + } + if (column == INCLUDE_MARK) { + return "Include"; + } + return IS_ACTIVE; + } + public Object getValueAt(int rowIndex, int columnIndex) { com.intellij.ui.classFilter.ClassFilter filter = myFilters.get(rowIndex); if (columnIndex == FILTER) { @@ -227,6 +262,9 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText if (columnIndex == CHECK_MARK) { return filter.isEnabled(); } + if (columnIndex == INCLUDE_MARK) { + return filter.isInclude(); + } return null; } @@ -238,12 +276,15 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText else if (columnIndex == CHECK_MARK) { filter.setEnabled(aValue == null || ((Boolean)aValue).booleanValue()); } + else if (columnIndex == INCLUDE_MARK) { + filter.setInclude(aValue == null || ((Boolean)aValue).booleanValue()); + } // fireTableCellUpdated(rowIndex, columnIndex); fireTableRowsUpdated(rowIndex, rowIndex); } public Class getColumnClass(int columnIndex) { - if (columnIndex == CHECK_MARK) { + if (columnIndex == CHECK_MARK || columnIndex == INCLUDE_MARK) { return Boolean.class; } return super.getColumnClass(columnIndex); diff --git a/plugins/coverage/src/com/intellij/coverage/IDEACoverageRunner.java b/plugins/coverage/src/com/intellij/coverage/IDEACoverageRunner.java index 588e94533f1c..75dc5bbcab1d 100644 --- a/plugins/coverage/src/com/intellij/coverage/IDEACoverageRunner.java +++ b/plugins/coverage/src/com/intellij/coverage/IDEACoverageRunner.java @@ -28,7 +28,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -69,11 +68,16 @@ public class IDEACoverageRunner extends JavaCoverageRunner { SimpleJavaParameters parameters, boolean collectLineInfo, boolean isSampling) { - appendCoverageArgument(sessionDataFilePath, patterns, parameters, collectLineInfo, isSampling, null); + appendCoverageArgument(sessionDataFilePath, patterns, null, parameters, collectLineInfo, isSampling, null); } - public void appendCoverageArgument(final String sessionDataFilePath, final String[] patterns, final SimpleJavaParameters javaParameters, - final boolean collectLineInfo, final boolean isSampling, @Nullable String sourceMapPath) { + public void appendCoverageArgument(final String sessionDataFilePath, + final String[] patterns, + final String[] excludePatterns, + final SimpleJavaParameters javaParameters, + final boolean collectLineInfo, + final boolean isSampling, + @Nullable String sourceMapPath) { StringBuilder argument = new StringBuilder("-javaagent:"); final String agentPath = PathUtil.getJarPathForClass(ProjectData.class); final String parentPath = handleSpacesInPath(agentPath, file -> { @@ -97,13 +101,11 @@ public class IDEACoverageRunner extends JavaCoverageRunner { write2file(tempFile, sourceMapPath); } if (patterns != null) { - for (String coveragePattern : patterns) { - coveragePattern = coveragePattern.replace("$", "\\$").replace(".", "\\.").replaceAll("\\*", ".*"); - if (!coveragePattern.endsWith(".*")) { //include inner classes - coveragePattern += "(\\$.*)*"; - } - write2file(tempFile, coveragePattern); - } + writePatterns(tempFile, patterns); + } + if (excludePatterns != null) { + write2file(tempFile, "-exclude"); + writePatterns(tempFile, excludePatterns); } argument.append(tempFile.getCanonicalPath()); } @@ -115,6 +117,16 @@ public class IDEACoverageRunner extends JavaCoverageRunner { javaParameters.getVMParametersList().add(argument.toString()); } + private static void writePatterns(File tempFile, String[] patterns) throws IOException { + for (String coveragePattern : patterns) { + coveragePattern = coveragePattern.replace("$", "\\$").replace(".", "\\.").replaceAll("\\*", ".*"); + if (!coveragePattern.endsWith(".*")) { //include inner classes + coveragePattern += "(\\$.*)*"; + } + write2file(tempFile, coveragePattern); + } + } + public String getPresentableName() { return "IntelliJ IDEA"; diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java index da9203930377..dc789645725a 100644 --- a/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java @@ -128,7 +128,7 @@ public class JavaCoverageEngine extends CoverageEngine { boolean tracingEnabled, boolean trackTestFolders, Project project) { - return createSuite(covRunner, name, coverageDataFileProvider, filters, lastCoverageTimeStamp, coverageByTestEnabled, + return createSuite(covRunner, name, coverageDataFileProvider, filters, null, lastCoverageTimeStamp, coverageByTestEnabled, tracingEnabled, trackTestFolders, project); } @@ -141,6 +141,7 @@ public class JavaCoverageEngine extends CoverageEngine { final JavaCoverageEnabledConfiguration javaConfig = (JavaCoverageEnabledConfiguration)config; return createSuite(covRunner, name, coverageDataFileProvider, javaConfig.getPatterns(), + javaConfig.getExcludePatterns(), new Date().getTime(), javaConfig.isTrackPerTestCoverage() && !javaConfig.isSampling(), !javaConfig.isSampling(), @@ -186,8 +187,7 @@ public class JavaCoverageEngine extends CoverageEngine { for (CoverageSuite coverageSuite : suite.getSuites()) { final JavaCoverageSuite javaSuite = (JavaCoverageSuite)coverageSuite; - final List packages = javaSuite.getCurrentSuitePackages(project); - if (isUnderFilteredPackages((PsiClassOwner)psiFile, packages)) { + if (javaSuite.isPackageFiltered(ReadAction.compute(() ->((PsiClassOwner)psiFile).getPackageName()))) { return true; } else { final List classes = javaSuite.getCurrentSuiteClasses(project); @@ -242,16 +242,6 @@ public class JavaCoverageEngine extends CoverageEngine { return ModuleRootManager.getInstance(module).getSourceRoots(rootType).stream().anyMatch(vFile -> !compilerManager.isExcludedFromCompilation(vFile)); } - public static boolean isUnderFilteredPackages(final PsiClassOwner javaFile, final List packages) { - final PsiPackage hisPackage = - ReadAction.compute(() -> JavaPsiFacade.getInstance(javaFile.getProject()).findPackage(javaFile.getPackageName())); - if (hisPackage == null) return false; - for (PsiPackage aPackage : packages) { - if (PsiTreeUtil.isAncestor(aPackage, hisPackage, false)) return true; - } - return false; - } - @Nullable public List collectSrcLinesForUntouchedFile(@NotNull final File classFile, @NotNull final CoverageSuitesBundle suite) { final List uncoveredLines = new ArrayList<>(); @@ -554,11 +544,12 @@ public class JavaCoverageEngine extends CoverageEngine { protected JavaCoverageSuite createSuite(CoverageRunner acceptedCovRunner, String name, CoverageFileProvider coverageDataFileProvider, String[] filters, + String[] excludePatterns, long lastCoverageTimeStamp, boolean coverageByTestEnabled, boolean tracingEnabled, boolean trackTestFolders, Project project) { - return new JavaCoverageSuite(name, coverageDataFileProvider, filters, lastCoverageTimeStamp, coverageByTestEnabled, tracingEnabled, + return new JavaCoverageSuite(name, coverageDataFileProvider, filters, excludePatterns, lastCoverageTimeStamp, coverageByTestEnabled, tracingEnabled, trackTestFolders, acceptedCovRunner, this, project); } diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageRunner.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageRunner.java index 27bdbd3e7ca0..d64e633e135d 100644 --- a/plugins/coverage/src/com/intellij/coverage/JavaCoverageRunner.java +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageRunner.java @@ -47,8 +47,13 @@ public abstract class JavaCoverageRunner extends CoverageRunner { public abstract void appendCoverageArgument(final String sessionDataFilePath, @Nullable final String[] patterns, final SimpleJavaParameters parameters, final boolean collectLineInfo, final boolean isSampling); - public void appendCoverageArgument(final String sessionDataFilePath, @Nullable final String[] patterns, final SimpleJavaParameters parameters, - final boolean collectLineInfo, final boolean isSampling, String sourceMapPath) { + public void appendCoverageArgument(final String sessionDataFilePath, + @Nullable final String[] patterns, + String[] excludePatterns, + final SimpleJavaParameters parameters, + final boolean collectLineInfo, + final boolean isSampling, + String sourceMapPath) { appendCoverageArgument(sessionDataFilePath, patterns, parameters, collectLineInfo, isSampling); } diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageSuite.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageSuite.java index 4e6e7147731d..91daed0b732b 100644 --- a/plugins/coverage/src/com/intellij/coverage/JavaCoverageSuite.java +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageSuite.java @@ -49,10 +49,12 @@ public class JavaCoverageSuite extends BaseCoverageSuite { @NonNls private static final String FILTER = "FILTER"; + private static final String EXCLUDED_FILTER = "EXCLUDED_FILTER"; @NonNls private static final String MERGE_SUITE = "MERGE_SUITE"; @NonNls private static final String COVERAGE_RUNNER = "RUNNER"; + private String[] myExcludePatterns; private final CoverageEngine myCoverageEngine; //read external only @@ -64,6 +66,7 @@ public class JavaCoverageSuite extends BaseCoverageSuite { public JavaCoverageSuite(final String name, final CoverageFileProvider coverageDataFileProvider, final String[] filters, + final String[] excludePatterns, final long lastCoverageTimeStamp, final boolean coverageByTestEnabled, final boolean tracingEnabled, @@ -76,14 +79,24 @@ public class JavaCoverageSuite extends BaseCoverageSuite { coverageRunner != null ? coverageRunner : CoverageRunner.getInstance(IDEACoverageRunner.class), project); myFilters = filters; + myExcludePatterns = excludePatterns; myCoverageEngine = coverageSupportProvider; } @NotNull public String[] getFilteredPackageNames() { - if (myFilters == null || myFilters.length == 0) return ArrayUtil.EMPTY_STRING_ARRAY; + return getPackageNames(myFilters); + } + + @NotNull + public String[] getExcludedPackageNames() { + return getPackageNames(myExcludePatterns); + } + + private static String[] getPackageNames(String[] filters) { + if (filters == null || filters.length == 0) return ArrayUtil.EMPTY_STRING_ARRAY; List result = new ArrayList<>(); - for (String filter : myFilters) { + for (String filter : filters) { if (filter.equals("*")) { result.add(""); //default package } @@ -94,9 +107,19 @@ public class JavaCoverageSuite extends BaseCoverageSuite { @NotNull public String[] getFilteredClassNames() { - if (myFilters == null) return ArrayUtil.EMPTY_STRING_ARRAY; + return getClassNames(myFilters); + } + + @NotNull + public String[] getExcludedClassNames() { + return getClassNames(myFilters); + } + + @NotNull + private static String[] getClassNames(final String[] filters) { + if (filters == null) return ArrayUtil.EMPTY_STRING_ARRAY; List result = new ArrayList<>(); - for (String filter : myFilters) { + for (String filter : filters) { if (!filter.equals("*") && !filter.endsWith(".*")) result.add(filter); } return ArrayUtil.toStringArray(result); @@ -106,13 +129,8 @@ public class JavaCoverageSuite extends BaseCoverageSuite { super.readExternal(element); // filters - final List children = element.getChildren(FILTER); - List filters = new ArrayList<>(); - //noinspection unchecked - for (Element child : ((Iterable)children)) { - filters.add(child.getValue()); - } - myFilters = filters.isEmpty() ? null : ArrayUtil.toStringArray(filters); + myFilters = readFilters(element, FILTER); + myExcludePatterns = readFilters(element, EXCLUDED_FILTER); // suite to merge mySuiteToMerge = element.getAttributeValue(MERGE_SUITE); @@ -122,20 +140,34 @@ public class JavaCoverageSuite extends BaseCoverageSuite { } } + private static String[] readFilters(Element element, final String tagName) { + final List children = element.getChildren(tagName); + List filters = new ArrayList<>(); + for (Element child : children) { + filters.add(child.getValue()); + } + return filters.isEmpty() ? null : ArrayUtil.toStringArray(filters); + } + public void writeExternal(final Element element) throws WriteExternalException { super.writeExternal(element); if (mySuiteToMerge != null) { element.setAttribute(MERGE_SUITE, mySuiteToMerge); } - if (myFilters != null) { - for (String filter : myFilters) { - final Element filterElement = new Element(FILTER); + writeFilters(element, myFilters, FILTER); + writeFilters(element, myExcludePatterns, EXCLUDED_FILTER); + final CoverageRunner coverageRunner = getRunner(); + element.setAttribute(COVERAGE_RUNNER, coverageRunner != null ? coverageRunner.getId() : "emma"); + } + + private static void writeFilters(Element element, final String[] filters, final String tagName) { + if (filters != null) { + for (String filter : filters) { + final Element filterElement = new Element(tagName); filterElement.setText(filter); element.addContent(filterElement); } } - final CoverageRunner coverageRunner = getRunner(); - element.setAttribute(COVERAGE_RUNNER, coverageRunner != null ? coverageRunner.getId() : "emma"); } @Nullable @@ -187,6 +219,9 @@ public class JavaCoverageSuite extends BaseCoverageSuite { } public boolean isPackageFiltered(final String packageFQName) { + for (String name : getExcludedPackageNames()) { + if (packageFQName.equals(name) || packageFQName.startsWith(name + ".")) return false; + } final String[] filteredPackageNames = getFilteredPackageNames(); for (final String packName : filteredPackageNames) { if (packName.equals(packageFQName) || packageFQName.startsWith(packName) && packageFQName.charAt(packName.length()) == '.') { diff --git a/plugins/coverage/src/com/intellij/execution/configurations/coverage/CoverageConfigurable.java b/plugins/coverage/src/com/intellij/execution/configurations/coverage/CoverageConfigurable.java index f09829db7eb5..47c94416eb64 100644 --- a/plugins/coverage/src/com/intellij/execution/configurations/coverage/CoverageConfigurable.java +++ b/plugins/coverage/src/com/intellij/execution/configurations/coverage/CoverageConfigurable.java @@ -86,7 +86,7 @@ public class CoverageConfigurable extends SettingsEditor { if (aClass.getContainingClass() != null) return false; return true; } - }); + }, null, true); } protected void addPatternFilter() { diff --git a/plugins/coverage/src/com/intellij/execution/configurations/coverage/JavaCoverageEnabledConfiguration.java b/plugins/coverage/src/com/intellij/execution/configurations/coverage/JavaCoverageEnabledConfiguration.java index dea9d62551b6..e40b68a5284a 100644 --- a/plugins/coverage/src/com/intellij/execution/configurations/coverage/JavaCoverageEnabledConfiguration.java +++ b/plugins/coverage/src/com/intellij/execution/configurations/coverage/JavaCoverageEnabledConfiguration.java @@ -87,6 +87,7 @@ public class JavaCoverageEnabledConfiguration extends CoverageEnabledConfigurati ((JavaCoverageRunner)runner).appendCoverageArgument(new File(path).getCanonicalPath(), getPatterns(), + getExcludePatterns(), javaParameters, isTrackPerTestCoverage() && !isSampling(), isSampling(), @@ -116,7 +117,19 @@ public class JavaCoverageEnabledConfiguration extends CoverageEnabledConfigurati if (myCoveragePatterns != null) { List patterns = new ArrayList<>(); for (ClassFilter coveragePattern : myCoveragePatterns) { - if (coveragePattern.isEnabled()) patterns.add(coveragePattern.getPattern()); + if (coveragePattern.isEnabled() && coveragePattern.isInclude()) patterns.add(coveragePattern.getPattern()); + } + return ArrayUtil.toStringArray(patterns); + } + return null; + } + + @Nullable + public String [] getExcludePatterns() { + if (myCoveragePatterns != null) { + List patterns = new ArrayList<>(); + for (ClassFilter coveragePattern : myCoveragePatterns) { + if (coveragePattern.isEnabled() && !coveragePattern.isInclude()) patterns.add(coveragePattern.getPattern()); } return ArrayUtil.toStringArray(patterns); } @@ -205,7 +218,7 @@ public class JavaCoverageEnabledConfiguration extends CoverageEnabledConfigurati if (myCoveragePatterns != null) { for (ClassFilter pattern : myCoveragePatterns) { @NonNls final Element patternElement = new Element(COVERAGE_PATTERN_ELEMENT_NAME); - DefaultJDOMExternalizer.writeExternal(pattern, patternElement); + pattern.writeExternal(patternElement); element.addContent(patternElement); } }