/*
 * 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.engine.discovery;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;

import java.util.Collection;

import org.junit.gen5.engine.DiscoveryFilter;
import org.junit.gen5.engine.EngineDiscoveryRequest;
import org.junit.gen5.engine.FilterResult;

/**
 * A collection of common builders for {@link EngineDiscoveryRequest} elements.
 *
 * @since 5.0
 */
class CommonFilter {

	@SafeVarargs
	public static <T> DiscoveryFilter<T> combine(DiscoveryFilter<T>... filters) {
		if (filters == null) {
			return alwaysIncluded();
		}
		else {
			return combine(asList(filters));
		}
	}

	public static <T> DiscoveryFilter<T> combine(Collection<DiscoveryFilter<T>> filters) {
		if (filters == null || filters.isEmpty()) {
			return alwaysIncluded();
		}
		else if (filters.size() == 1) {
			return filters.iterator().next();
		}
		else {
			return new AndAllDiscoveryFilter<>(filters);
		}
	}

	private static <T> DiscoveryFilter<T> alwaysIncluded() {
		return (any) -> FilterResult.included("Always included");
	}

	private static class AndAllDiscoveryFilter<T> implements DiscoveryFilter<T> {

		private final Collection<DiscoveryFilter<T>> filters;

		public AndAllDiscoveryFilter(Collection<DiscoveryFilter<T>> filters) {
			this.filters = filters;
		}

		@Override
		public FilterResult filter(T element) {
			// @formatter:off
            return this.filters.stream()
                    .map(filter -> filter.filter(element))
                    .filter(FilterResult::excluded)
                    .findFirst()
                    .orElse(FilterResult.included("Element was included by all filters."));
            // @formatter:on
		}

		@Override
		public String toString() {
			return this.filters.stream().map(Object::toString).map(s -> format("(%s)", s)).collect(joining(" and "));
		}
	}

}

@@ -779,16 +779,175 @@
 lter %7B%0A%0A
+%09@SuppressWarnings(%22rawtypes%22)%0A%09private static final DiscoveryFilter ALWAYS_INCLUDED_DISCOVERY_FILTER = (obj) -%3E FilterResult.included(%0A%09%09%22Always included%22);%0A%0A
 %09@SafeVa
@@ -1443,24 +1443,56 @@
 s);%0A%09%09%7D%0A%09%7D%0A%0A
+%09@SuppressWarnings(%22unchecked%22)%0A
 %09private sta
@@ -1551,56 +1551,60 @@
 rn (
-any) -%3E FilterResult.included(%22Always included%22)
+DiscoveryFilter%3CT%3E) ALWAYS_INCLUDED_DISCOVERY_FILTER
 ;%0A%09%7D

Introduce constant for the "always include" filter