/*
 * Copyright 2015-2016 the original author or authors.
 *
 * 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
 */

package org.junit.gen5.launcher.main;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import org.junit.gen5.engine.*;
import org.junit.gen5.launcher.*;

/**
 * Represents the root of all discovered {@link TestEngine} and their {@link TestDescriptor}s.
 */
class Root {

	private final Map<TestEngine, TestDescriptor> testEngineDescriptors = new LinkedHashMap<>();

	/**
	 * Add an {@code engine}'s discovered root {@code testDescriptor}
	 */
	void add(TestEngine engine, TestDescriptor testDescriptor) {
		testEngineDescriptors.put(engine, testDescriptor);
	}

	Iterable<TestEngine> getTestEngines() {
		return testEngineDescriptors.keySet();
	}

	Collection<TestDescriptor> getEngineDescriptors() {
		return testEngineDescriptors.values();
	}

	TestDescriptor getTestDescriptorFor(TestEngine testEngine) {
		return testEngineDescriptors.get(testEngine);
	}

	void applyPostDiscoveryFilters(TestDiscoveryRequest discoveryRequest) {
		TestDescriptor.Visitor filteringVisitor = (descriptor, remove) -> {
			if (!descriptor.isTest())
				return;

			if (isExcluded(discoveryRequest, descriptor)) {
				remove.run();
			}
		};
		acceptInAllTestEngines(filteringVisitor);
	}

	/**
	 * Prune all branches in the tree of {@link TestDescriptor} that do not have executable tests.
	 * If a {@link TestEngine} ends up with no {@link TestDescriptor}s after pruning, it will be removed.
	 */
	void prune() {
		TestDescriptor.Visitor pruningVisitor = (descriptor, remove) -> {
			if (descriptor.isRoot() || descriptor.hasTests())
				return;
			remove.run();
		};
		acceptInAllTestEngines(pruningVisitor);
		pruneEmptyTestEngines();
	}

	private boolean isExcluded(TestDiscoveryRequest discoveryRequest, TestDescriptor descriptor) {
		return Filter.composeFilters(discoveryRequest.getPostDiscoveryFilters()).filter(descriptor).excluded();
	}

	private void acceptInAllTestEngines(TestDescriptor.Visitor visitor) {
		testEngineDescriptors.values().stream().forEach(testEngine -> testEngine.accept(visitor));
	}

	private void pruneEmptyTestEngines() {
		testEngineDescriptors.values().removeIf(testEngine -> testEngine.getChildren().isEmpty());
	}

}

@@ -346,16 +346,76 @@
 .main;%0A%0A
+import static org.junit.gen5.engine.Filter.composeFilters;%0A%0A
 import j
@@ -674,16 +674,197 @@
 Root %7B%0A%0A
+%09private static final TestDescriptor.Visitor REMOVE_DESCRIPTORS_WITHOUT_TESTS = (descriptor, remove) -%3E %7B%0A%09%09if (!descriptor.isRoot() && !descriptor.hasTests())%0A%09%09%09remove.run();%0A%09%7D;%0A
 %09private
@@ -1515,24 +1515,31 @@
 equest) %7B%0A%09%09
+Filter%3C
 TestDescript
@@ -1544,70 +1544,140 @@
 ptor
-.Visitor filteringVisitor = (descriptor, remove) -%3E %7B%0A%09%09%09if
+%3E postDiscoveryFilter = composeFilters(discoveryRequest.getPostDiscoveryFilters());%0A%09%09TestDescriptor.Visitor removeExcludedTests =
  (
-!
 desc
@@ -1686,31 +1686,22 @@
 ptor
-.isTest())%0A%09%09%09%09return;%0A
+, remove) -%3E %7B
 %0A%09%09%09
@@ -1718,36 +1718,43 @@
 uded
+Test
 (d
-iscoveryRequest, descripto
+escriptor, postDiscoveryFilte
 r)) 
@@ -1812,24 +1812,27 @@
 nes(
-filteringVisitor
+removeExcludedTests
 );%0A%09
@@ -2065,200 +2065,63 @@
 %7B%0A%09%09
-TestDescriptor.Visitor pruningVisitor = (descriptor, remove) -%3E %7B%0A%09%09%09if (descriptor.isRoot() %7C%7C descriptor.hasTests())%0A%09%09%09%09return;%0A%09%09%09remove.run();%0A%09%09%7D;%0A%09%09acceptInAllTestEngines(pruningVisitor
+acceptInAllTestEngines(REMOVE_DESCRIPTORS_WITHOUT_TESTS
 );%0A%09
@@ -2181,129 +2181,119 @@
 uded
+Test
 (TestD
-iscoveryRequest discoveryRequest, TestDescriptor descriptor) %7B%0A%09%09return Filter.composeFilters(discoveryRequest.getP
+escriptor descriptor, Filter%3CTestDescriptor%3E postDiscoveryFilter) %7B%0A%09%09return descriptor.isTest() && p
 ostD
@@ -2306,20 +2306,16 @@
 ryFilter
-s())
 .filter(

Refactor code for readability.