Add ClassnameMatcher in preparation to pass into Instrumenter for filtering am: bbe2e9a9b2 am: 99916bb64e am: 3969781f7e
am: 1442ac24eb

Change-Id: Idaf9214f456c6c3ca73fb10c926786b8d10b4713
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java
index 2cb16fe..cbd6a62 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java
@@ -120,16 +120,14 @@
 			if (!inclNoLocationClasses && !hasSourceLocation(protectionDomain)) {
 				return false;
 			}
-			if (exclClassloader.matches(loader.getClass().getName())) {
+			if (exclClassloader.apply(loader.getClass().getName())) {
 				return false;
 			}
 		}
 
 		return !classname.startsWith(AGENT_PREFIX) &&
-
-		includes.matches(classname) &&
-
-		!excludes.matches(classname);
+				includes.apply(classname) &&
+				!excludes.apply(classname);
 	}
 
 	/**
diff --git a/org.jacoco.core.test/src/org/jacoco/core/matcher/IncludeExcludeMatcherTest.java b/org.jacoco.core.test/src/org/jacoco/core/matcher/IncludeExcludeMatcherTest.java
new file mode 100644
index 0000000..e026895
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/matcher/IncludeExcludeMatcherTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Jeffry Gaston - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.matcher;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class IncludeExcludeMatcherTest {
+
+	@Test
+	public void testEmpty() {
+		assertTrue(new IncludeExcludeMatcher<String>().apply("includeMe"));
+	}
+
+	@Test
+	public void testSingleExclude() {
+		IncludeExcludeMatcher<String> matcher = new IncludeExcludeMatcher<String>()
+				.exclude(new WildcardMatcher("excluded"));
+		assertTrue(matcher.apply("included"));
+		assertFalse(matcher.apply("excluded"));
+	}
+
+	@Test
+	public void testMultipleExcludes() {
+		IncludeExcludeMatcher<String> matcher = new IncludeExcludeMatcher<String>().exclude(
+				new WildcardMatcher("excluded"))
+						.exclude(new WildcardMatcher("excluded2"));
+		assertTrue(matcher.apply("included"));
+		assertFalse(matcher.apply("excluded"));
+		assertFalse(matcher.apply("excluded2"));
+	}
+
+	@Test
+	public void testSingleInclude() {
+		IncludeExcludeMatcher<String> matcher = new IncludeExcludeMatcher<String>()
+				.include(new WildcardMatcher("include me"));
+		assertTrue(matcher.apply("include me"));
+		assertFalse(matcher.apply("not me"));
+	}
+
+	@Test
+	public void testIncludesAndExcludes() {
+		IncludeExcludeMatcher<String> matcher = new IncludeExcludeMatcher<String>()
+				.include(new WildcardMatcher("inclusion1"))
+				.include(new WildcardMatcher("me too"))
+				.exclude(new WildcardMatcher("not me"))
+				.exclude(new WildcardMatcher("nope"));
+		assertTrue(matcher.apply("inclusion1"));
+		assertTrue(matcher.apply("me too"));
+		assertFalse(matcher.apply("not me"));
+		assertFalse(matcher.apply("nope"));
+		assertFalse(matcher.apply("other"));
+	}
+
+	@Test
+	public void testExcludedInclusion() {
+		IncludeExcludeMatcher<String> matcher = new IncludeExcludeMatcher<String>()
+				.include(new WildcardMatcher("a"))
+				.exclude(new WildcardMatcher("a"));
+		assertFalse(matcher.apply("a"));
+		assertFalse(matcher.apply("b"));
+	}
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/matcher/WildcardMatcherTest.java b/org.jacoco.core.test/src/org/jacoco/core/matcher/WildcardMatcherTest.java
index 0d0a9c0..baf1dc2 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/matcher/WildcardMatcherTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/matcher/WildcardMatcherTest.java
@@ -20,53 +20,53 @@
 
 	@Test
 	public void testEmpty() {
-		assertTrue(new WildcardMatcher("").matches(""));
-		assertFalse(new WildcardMatcher("").matches("abc"));
+		assertTrue(new WildcardMatcher("").apply(""));
+		assertFalse(new WildcardMatcher("").apply("abc"));
 	}
 
 	@Test
 	public void testExact() {
-		assertTrue(new WildcardMatcher("abc/def.txt").matches("abc/def.txt"));
+		assertTrue(new WildcardMatcher("abc/def.txt").apply("abc/def.txt"));
 	}
 
 	@Test
 	public void testCaseSensitive() {
-		assertFalse(new WildcardMatcher("abcdef").matches("abcDef"));
-		assertFalse(new WildcardMatcher("ABCDEF").matches("AbCDEF"));
+		assertFalse(new WildcardMatcher("abcdef").apply("abcDef"));
+		assertFalse(new WildcardMatcher("ABCDEF").apply("AbCDEF"));
 	}
 
 	@Test
 	public void testQuote() {
-		assertFalse(new WildcardMatcher("rst.xyz").matches("rstAxyz"));
-		assertTrue(new WildcardMatcher("(x)+").matches("(x)+"));
+		assertFalse(new WildcardMatcher("rst.xyz").apply("rstAxyz"));
+		assertTrue(new WildcardMatcher("(x)+").apply("(x)+"));
 	}
 
 	@Test
 	public void testWildcards() {
-		assertTrue(new WildcardMatcher("*").matches(""));
-		assertTrue(new WildcardMatcher("*").matches("java/lang/Object"));
-		assertTrue(new WildcardMatcher("*Test").matches("jacoco/MatcherTest"));
-		assertTrue(new WildcardMatcher("Matcher*").matches("Matcher"));
-		assertTrue(new WildcardMatcher("Matcher*").matches("MatcherTest"));
-		assertTrue(new WildcardMatcher("a*b*a").matches("a-b-b-a"));
-		assertFalse(new WildcardMatcher("a*b*a").matches("alaska"));
-		assertTrue(new WildcardMatcher("Hello?orld").matches("HelloWorld"));
-		assertFalse(new WildcardMatcher("Hello?orld").matches("HelloWWWorld"));
-		assertTrue(new WildcardMatcher("?aco*").matches("jacoco"));
+		assertTrue(new WildcardMatcher("*").apply(""));
+		assertTrue(new WildcardMatcher("*").apply("java/lang/Object"));
+		assertTrue(new WildcardMatcher("*Test").apply("jacoco/MatcherTest"));
+		assertTrue(new WildcardMatcher("Matcher*").apply("Matcher"));
+		assertTrue(new WildcardMatcher("Matcher*").apply("MatcherTest"));
+		assertTrue(new WildcardMatcher("a*b*a").apply("a-b-b-a"));
+		assertFalse(new WildcardMatcher("a*b*a").apply("alaska"));
+		assertTrue(new WildcardMatcher("Hello?orld").apply("HelloWorld"));
+		assertFalse(new WildcardMatcher("Hello?orld").apply("HelloWWWorld"));
+		assertTrue(new WildcardMatcher("?aco*").apply("jacoco"));
 	}
 
 	@Test
 	public void testMultiExpression() {
-		assertTrue(new WildcardMatcher("Hello:World").matches("World"));
-		assertTrue(new WildcardMatcher("Hello:World").matches("World"));
-		assertTrue(new WildcardMatcher("*Test:*Foo").matches("UnitTest"));
+		assertTrue(new WildcardMatcher("Hello:World").apply("World"));
+		assertTrue(new WildcardMatcher("Hello:World").apply("World"));
+		assertTrue(new WildcardMatcher("*Test:*Foo").apply("UnitTest"));
 	}
 
 	@Test
 	public void testDollar() {
-		assertTrue(new WildcardMatcher("*$*").matches("java/util/Map$Entry"));
+		assertTrue(new WildcardMatcher("*$*").apply("java/util/Map$Entry"));
 		assertTrue(new WildcardMatcher("*$$$*")
-				.matches("org/example/Enity$$$generated123"));
+				.apply("org/example/Enity$$$generated123"));
 	}
 
 }
diff --git a/org.jacoco.core/src/org/jacoco/core/matcher/ClassnameMatcher.java b/org.jacoco.core/src/org/jacoco/core/matcher/ClassnameMatcher.java
new file mode 100644
index 0000000..939bd5f
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/matcher/ClassnameMatcher.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Jeffry Gaston - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.jacoco.core.matcher;
+
+import java.util.List;
+
+import org.objectweb.asm.ClassReader;
+
+/**
+ * A ClassnameMatcher matches ClassReader objects based on their class name
+ */
+public class ClassnameMatcher implements Predicate<ClassReader> {
+	private IncludeExcludeMatcher<String> matcher = new IncludeExcludeMatcher<String>();
+
+	/**
+	 * Includes the given pattern from the matches of this matcher
+	 * @param pattern to include
+	 * @return this object (for chaining)
+	 */
+	public ClassnameMatcher include(String pattern) {
+		matcher.include(new WildcardMatcher(pattern));
+		return this;
+	}
+
+	/**
+	 * Adds the given patterns as inclusions for this matcher
+	 * @param patterns patterns to include
+	 * @return this object (for chaining)
+	 */
+	public ClassnameMatcher include(List<String> patterns) {
+		for (String pattern : patterns) {
+			include(pattern);
+		}
+		return this;
+	}
+
+	/**
+	 * As the given pattern as an exclusion for this matcher
+	 * @param pattern pattern to exclude
+	 * @return this object (for chaining)
+	 */
+	public ClassnameMatcher exclude(String pattern) {
+		matcher.exclude(new WildcardMatcher(pattern));
+		return this;
+	}
+
+	/**
+	 * As the given patterns as exclusions for this matcher
+	 * @param patterns patterns to include
+	 * @return this object (for chaining)
+	 */
+	public ClassnameMatcher exclude(List<String> patterns) {
+		for (String pattern : patterns) {
+			exclude(pattern);
+		}
+		return this;
+	}
+
+
+	/**
+	 * Tells whether this matcher matches this class reader
+	 * @param classReader the reader to match
+	 * @return whether this matcher matches
+	 */
+	@Override
+	public boolean apply(ClassReader classReader) {
+		return matcher.apply(classReader.getClassName().replaceAll("/", "."));
+	}
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/matcher/IncludeExcludeMatcher.java b/org.jacoco.core/src/org/jacoco/core/matcher/IncludeExcludeMatcher.java
new file mode 100644
index 0000000..50f1d88
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/matcher/IncludeExcludeMatcher.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Jeffry Gaston - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.matcher;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An IncludeExcludeMatcher matches a given input if
+ * at least one inclusion matches and no exclusions match.
+ */
+public class IncludeExcludeMatcher<T> implements Predicate<T> {
+	private List<Predicate<T>> inclusions = new ArrayList<Predicate<T>>();
+	private List<Predicate<T>> exclusions = new ArrayList<Predicate<T>>();
+
+	/**
+	 * Includes the given matcher
+	 * @param inclusion new matcher to include
+	 * @return this object (for chaining several calls)
+	 */
+	public IncludeExcludeMatcher include(Predicate<T> inclusion) {
+		inclusions.add(inclusion);
+		return this;
+	}
+
+	/**
+	 * Excludes a given matcher
+	 * @param exclusion
+	 * @return this object (for chaining several calls)
+	 */
+	public IncludeExcludeMatcher exclude(Predicate<T> exclusion) {
+		exclusions.add(exclusion);
+		return this;
+	}
+
+	/**
+	 * Tells whether this matcher matches this string
+	 * @param input the string match
+	 * @return whether the matcher matches
+	 */
+	@Override
+	public boolean apply(T input) {
+		// doesn't match if an exclusion matches
+		for (Predicate<T> exclusion : exclusions) {
+			if (exclusion.apply(input)) {
+				return false;
+			}
+		}
+		// does match if an inclusion matches
+		for (Predicate<T> inclusion : inclusions) {
+			if (inclusion.apply(input)) {
+				return true;
+			}
+		}
+		// no match; choose a default based on whether any includes were given
+		return (inclusions.size() == 0);
+	}
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/matcher/Predicate.java b/org.jacoco.core/src/org/jacoco/core/matcher/Predicate.java
new file mode 100644
index 0000000..14d084b
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/matcher/Predicate.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Jeffry Gaston - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.matcher;
+
+/**
+ * A simple interface telling whether a predicate matches an input.
+ * Once the minimum supported version of Java is Java 1.8, then this can be replaced with the
+ * built-in Java predicate.
+ * While it could work to add a dependency on a library providing a similar interface, we prefer
+ * to keep the number of dependencies low, to avoid forcing other dependencies onto
+ * any builds that depend on Jacoco.
+ */
+public interface Predicate<T> {
+	boolean apply(final T s);
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/matcher/WildcardMatcher.java b/org.jacoco.core/src/org/jacoco/core/matcher/WildcardMatcher.java
index 9e7bd87..6ae20c4 100644
--- a/org.jacoco.core/src/org/jacoco/core/matcher/WildcardMatcher.java
+++ b/org.jacoco.core/src/org/jacoco/core/matcher/WildcardMatcher.java
@@ -18,7 +18,7 @@
  * Multiple expressions can be separated with a colon (:). In this case the
  * expression matches if at least one part matches.
  */
-public class WildcardMatcher {
+public class WildcardMatcher implements Predicate<String> {
 
 	private final Pattern pattern;
 
@@ -67,7 +67,8 @@
 	 *            string to test
 	 * @return <code>true</code>, if the expression matches
 	 */
-	public boolean matches(final String s) {
+	@Override
+	public boolean apply(final String s) {
 		return pattern.matcher(s).matches();
 	}
 
diff --git a/org.jacoco.report/src/org/jacoco/report/check/Rule.java b/org.jacoco.report/src/org/jacoco/report/check/Rule.java
index acf9058..f424e81 100644
--- a/org.jacoco.report/src/org/jacoco/report/check/Rule.java
+++ b/org.jacoco.report/src/org/jacoco/report/check/Rule.java
@@ -116,7 +116,7 @@
 	}
 
 	boolean matches(final String name) {
-		return includesMatcher.matches(name) && !excludesMatcher.matches(name);
+		return includesMatcher.apply(name) && !excludesMatcher.apply(name);
 	}
 
 }