diff --git a/src/it/projects/tree-excluded/expected-included-survives.txt b/src/it/projects/tree-excluded/expected-included-survives.txt
new file mode 100644
index 000000000..b61e0a3ba
--- /dev/null
+++ b/src/it/projects/tree-excluded/expected-included-survives.txt
@@ -0,0 +1,4 @@
+org.apache.maven.its.dependency:test:jar:1.0-SNAPSHOT
+\- org.apache.maven:maven-project:jar:2.0.6:compile
+ \- org.apache.maven:maven-artifact-manager:jar:2.0.6:compile
+ \- org.apache.maven.wagon:wagon-provider-api:jar:1.0-beta-2:compile
diff --git a/src/it/projects/tree-excluded/expected-v4.txt b/src/it/projects/tree-excluded/expected-v4.txt
index 82cd95252..ff6840584 100644
--- a/src/it/projects/tree-excluded/expected-v4.txt
+++ b/src/it/projects/tree-excluded/expected-v4.txt
@@ -7,7 +7,4 @@ org.apache.maven.its.dependency:test:jar:1.0-SNAPSHOT
| +- org.apache.maven:maven-repository-metadata:jar:2.0.6:compile
| \- org.apache.maven.wagon:wagon-provider-api:jar:1.0-beta-2:compile
+- org.apache.maven:maven-plugin-registry:jar:2.0.6:compile
- +- org.apache.maven:maven-artifact:jar:2.0.6:compile
- \- org.codehaus.plexus:plexus-container-default:jar:1.0-alpha-9-stable-1:compile
- +- junit:junit:jar:3.8.1:compile
- \- classworlds:classworlds:jar:1.1:compile
+ \- org.apache.maven:maven-artifact:jar:2.0.6:compile
diff --git a/src/it/projects/tree-excluded/expected.txt b/src/it/projects/tree-excluded/expected.txt
index 38be1dd0d..ff6840584 100644
--- a/src/it/projects/tree-excluded/expected.txt
+++ b/src/it/projects/tree-excluded/expected.txt
@@ -7,7 +7,4 @@ org.apache.maven.its.dependency:test:jar:1.0-SNAPSHOT
| +- org.apache.maven:maven-repository-metadata:jar:2.0.6:compile
| \- org.apache.maven.wagon:wagon-provider-api:jar:1.0-beta-2:compile
+- org.apache.maven:maven-plugin-registry:jar:2.0.6:compile
- +- org.apache.maven:maven-artifact:jar:2.0.6:compile
- \- org.codehaus.plexus:plexus-container-default:jar:1.0-alpha-9-stable-1:compile
- +- junit:junit:jar:3.8.1:compile
- \- classworlds:classworlds:jar:1.1-alpha-2:compile
+ \- org.apache.maven:maven-artifact:jar:2.0.6:compile
diff --git a/src/it/projects/tree-excluded/invoker.properties b/src/it/projects/tree-excluded/invoker.properties
index afb73d253..476a9d5ed 100644
--- a/src/it/projects/tree-excluded/invoker.properties
+++ b/src/it/projects/tree-excluded/invoker.properties
@@ -15,4 +15,11 @@
# specific language governing permissions and limitations
# under the License.
-invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:tree
+invoker.goals.1 = ${project.groupId}:${project.artifactId}:${project.version}:tree
+invoker.userPropertiesFile.1 = test.properties
+
+invoker.goals.2 = ${project.groupId}:${project.artifactId}:${project.version}:tree
+invoker.userPropertiesFile.2 = test-included-excluded.properties
+
+invoker.goals.3 = ${project.groupId}:${project.artifactId}:${project.version}:tree
+invoker.userPropertiesFile.3 = test-included-survives.properties
diff --git a/src/it/projects/tree-excluded/test-included-excluded.properties b/src/it/projects/tree-excluded/test-included-excluded.properties
new file mode 100644
index 000000000..fad197e38
--- /dev/null
+++ b/src/it/projects/tree-excluded/test-included-excluded.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+outputFile = target/included-excluded.txt
+includes = junit:junit
+excludes = org.codehaus.plexus:*
diff --git a/src/it/projects/tree-excluded/test-included-survives.properties b/src/it/projects/tree-excluded/test-included-survives.properties
new file mode 100644
index 000000000..63e2b17dc
--- /dev/null
+++ b/src/it/projects/tree-excluded/test-included-survives.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+outputFile = target/included-survives.txt
+includes = :wagon*
+excludes = org.codehaus.plexus:*
diff --git a/src/it/projects/tree-excluded/verify.groovy b/src/it/projects/tree-excluded/verify.groovy
index e9d8d8135..6908ce3fd 100644
--- a/src/it/projects/tree-excluded/verify.groovy
+++ b/src/it/projects/tree-excluded/verify.groovy
@@ -25,4 +25,9 @@ def expected = mavenVersion.startsWith('4.') ? "expected-v4.txt" : "expected.txt
assertThat(new File(basedir, "target/tree.txt"))
.hasSameTextualContentAs(new File(basedir, expected))
+assertThat(new File(basedir, "target/included-excluded.txt").text).isEmpty()
+
+assertThat(new File(basedir, "target/included-survives.txt"))
+ .hasSameTextualContentAs(new File(basedir, "expected-included-survives.txt"))
+
return true
diff --git a/src/main/java/org/apache/maven/plugins/dependency/tree/PruningDependencyNodeVisitor.java b/src/main/java/org/apache/maven/plugins/dependency/tree/PruningDependencyNodeVisitor.java
new file mode 100644
index 000000000..380d2873e
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/dependency/tree/PruningDependencyNodeVisitor.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.maven.plugins.dependency.tree;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+import org.apache.maven.shared.dependency.graph.filter.DependencyNodeFilter;
+import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
+
+/**
+ * A dependency node visitor that delegates accepted nodes and prunes rejected nodes and their descendants.
+ */
+final class PruningDependencyNodeVisitor implements DependencyNodeVisitor {
+ private final DependencyNodeVisitor visitor;
+
+ private final DependencyNodeFilter filter;
+
+ private final Dequeorg.apache.* will match all artifacts whose group id starts with
* org.apache., and :::*-SNAPSHOT will match all snapshot artifacts.
*
org.apache.* will match all artifacts whose group id starts with
* org.apache., and :::*-SNAPSHOT will match all snapshot artifacts.
*
+ * A matching artifact and its entire dependency subtree are removed from the serialized dependency tree.
+ * Exclusions are applied before {@link #includes} and take precedence.
*
* @see StrictPatternExcludesArtifactFilter
* @since 2.0-alpha-6
@@ -333,11 +335,16 @@ private String serializeDependencyTree(DependencyNode theRootNode) {
// TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
visitor = new BuildingDependencyNodeVisitor(visitor);
- DependencyNodeFilter filter = createDependencyNodeFilter();
+ DependencyNodeFilter includesFilter = createIncludesDependencyNodeFilter();
+ DependencyNodeFilter excludesFilter = createExcludesDependencyNodeFilter();
- if (filter != null) {
+ if (includesFilter != null) {
CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
- DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor(collectingVisitor, filter);
+ DependencyNodeVisitor firstPassVisitor =
+ new FilteringDependencyNodeVisitor(collectingVisitor, includesFilter);
+ if (excludesFilter != null) {
+ firstPassVisitor = new PruningDependencyNodeVisitor(firstPassVisitor, excludesFilter);
+ }
theRootNode.accept(firstPassVisitor);
DependencyNodeFilter secondPassFilter =
@@ -345,6 +352,10 @@ private String serializeDependencyTree(DependencyNode theRootNode) {
visitor = new FilteringDependencyNodeVisitor(visitor, secondPassFilter);
}
+ if (excludesFilter != null) {
+ visitor = new PruningDependencyNodeVisitor(visitor, excludesFilter);
+ }
+
theRootNode.accept(visitor);
return writer.toString();
@@ -397,27 +408,25 @@ private GraphTokens toGraphTokens(String theTokens) {
*
* @return the dependency node filter, or null if none required
*/
- private DependencyNodeFilter createDependencyNodeFilter() {
- List