/*
 * Copyright 2015-2023 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 v2.0 which
 * accompanies this distribution and is available at
 *
 * https://www.eclipse.org/legal/epl-v20.html
 */

package org.junit.jupiter.api;

import static org.apiguardian.api.API.Status.MAINTAINED;

import java.net.URI;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.apiguardian.api.API;
import org.junit.platform.commons.util.Preconditions;

/**
 * A {@code DynamicContainer} is a container generated at runtime.
 *
 * <p>It is composed of a {@linkplain DynamicNode#getDisplayName display name}
 * and an {@link Iterable} or {@link Stream} of {@link DynamicNode DynamicNodes}.
 *
 * <p>Instances of {@code DynamicContainer} must be generated by factory methods
 * annotated with {@link TestFactory @TestFactory}.
 *
 * @since 5.0
 * @see #dynamicContainer(String, Iterable)
 * @see #dynamicContainer(String, Stream)
 * @see TestFactory
 * @see DynamicTest
 */
@API(status = MAINTAINED, since = "5.3")
public class DynamicContainer extends DynamicNode {

	/**
	 * Factory for creating a new {@code DynamicContainer} for the supplied display
	 * name and collection of dynamic nodes.
	 *
	 * <p>The collection of dynamic nodes must not contain {@code null} elements.
	 *
	 * @param displayName the display name for the dynamic container; never
	 * {@code null} or blank
	 * @param dynamicNodes collection of dynamic nodes to execute;
	 * never {@code null}
	 * @see #dynamicContainer(String, Stream)
	 */
	public static DynamicContainer dynamicContainer(String displayName, Iterable<? extends DynamicNode> dynamicNodes) {
		return dynamicContainer(displayName, null, StreamSupport.stream(dynamicNodes.spliterator(), false));
	}

	/**
	 * Factory for creating a new {@code DynamicContainer} for the supplied display
	 * name and stream of dynamic nodes.
	 *
	 * <p>The stream of dynamic nodes must not contain {@code null} elements.
	 *
	 * @param displayName the display name for the dynamic container; never
	 * {@code null} or blank
	 * @param dynamicNodes stream of dynamic nodes to execute;
	 * never {@code null}
	 * @see #dynamicContainer(String, Iterable)
	 */
	public static DynamicContainer dynamicContainer(String displayName, Stream<? extends DynamicNode> dynamicNodes) {
		return dynamicContainer(displayName, null, dynamicNodes);
	}

	/**
	 * Factory for creating a new {@code DynamicContainer} for the supplied display
	 * name, custom test source {@link URI}, and stream of dynamic nodes.
	 *
	 * <p>The stream of dynamic nodes must not contain {@code null} elements.
	 *
	 * @param displayName the display name for the dynamic container; never
	 * {@code null} or blank
	 * @param testSourceUri a custom test source URI for the dynamic container;
	 * may be {@code null} if the framework should generate the test source based
	 * on the {@code @TestFactory} method
	 * @param dynamicNodes stream of dynamic nodes to execute; never {@code null}
	 * @since 5.3
	 * @see #dynamicContainer(String, Iterable)
	 */
	public static DynamicContainer dynamicContainer(String displayName, URI testSourceUri,
			Stream<? extends DynamicNode> dynamicNodes) {

		return new DynamicContainer(displayName, testSourceUri, dynamicNodes);
	}

	private final Stream<? extends DynamicNode> children;

	private DynamicContainer(String displayName, URI testSourceUri, Stream<? extends DynamicNode> children) {
		super(displayName, testSourceUri);
		Preconditions.notNull(children, "children must not be null");
		this.children = children;
	}

	/**
	 * Get the {@link Stream} of {@link DynamicNode DynamicNodes} associated
	 * with this {@code DynamicContainer}.
	 */
	public Stream<? extends DynamicNode> getChildren() {
		return children;
	}

}
