package aQute.lib.io;

import static java.util.stream.Collectors.toList;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

import aQute.libg.glob.PathSet;

public class FileTree extends PathSet {
	private final List<File> files = new ArrayList<>();

	public FileTree() {}

	/**
	 * Can be used by subclasses to add specific files to the return value of
	 * {@link #getFiles(File, String)}.
	 * 
	 * @param file A file to include in the return value of
	 *            {@link #getFiles(File, String)}.
	 */
	protected void addFile(File file) {
		if (file == null) {
			return;
		}
		if (!files.contains(file)) {
			files.add(file);
		}
	}

	/**
	 * Add an Ant-style glob to the include patterns.
	 * 
	 * @param includes Add an Ant-style glob
	 */
	public void addIncludes(List<String> includes) {
		includes(includes);
	}

	/**
	 * Add an Ant-style glob to the include patterns.
	 * 
	 * @param includes Add an Ant-style glob
	 */
	public void addIncludes(String... includes) {
		include(includes);
	}

	/**
	 * Add an Ant-style glob to the exclude patterns.
	 * 
	 * @param excludes Add an Ant-style glob
	 */
	public void addExcludes(String... excludes) {
		exclude(excludes);
	}

	/**
	 * Add an Ant-style glob to the exclude patterns.
	 * 
	 * @param excludes Add an Ant-style glob
	 */
	public void addExcludes(List<String> excludes) {
		excludes(excludes);
	}

	/**
	 * Return a list of files using the specified baseDir and the configured
	 * include and exclude Ant-style glob expressions.
	 * 
	 * @param baseDir The base directory for locating files.
	 * @param defaultIncludes The default include patterns to use if no include
	 *            patterns were configured.
	 * @return A list of files.
	 * @throws IOException If an exception occurs.
	 */
	public List<File> getFiles(File baseDir, String... defaultIncludes) throws IOException {
		return getFiles(baseDir, files.isEmpty() ? matches(defaultIncludes) : matches());
	}

	/**
	 * Return a list of files using the specified baseDir and the configured
	 * include and exclude Ant-style glob expressions.
	 * 
	 * @param baseDir The base directory for locating files.
	 * @param defaultIncludes The default include patterns to use if no include
	 *            patterns were configured.
	 * @return A list of files.
	 * @throws IOException If an exception occurs.
	 */
	public List<File> getFiles(File baseDir, List<String> defaultIncludes) throws IOException {
		return getFiles(baseDir, files.isEmpty() ? matches(defaultIncludes) : matches());
	}

	private List<File> getFiles(File baseDir, Predicate<String> matches) throws IOException {
		Path basePath = baseDir.toPath();
		try (Stream<Path> walker = Files.walk(basePath)
			.skip(1)) {
			List<File> result = Stream.concat(files.stream(), //
				walker.filter(p -> matches.test(basePath.relativize(p)
					.toString()))
					.sorted()
					.map(Path::toFile))
				.distinct()
				.collect(toList());
			return result;
		}
	}
}

@@ -351,26 +351,10 @@
 ree 
-extends PathSet 
 %7B%0A
+
 %09pri
@@ -401,16 +401,63 @@
 ist%3C%3E();
+%0A%09private final PathSet%09%09paths%09= new PathSet();
 %0A%0A%09publi
@@ -994,24 +994,30 @@
 cludes) %7B%0A%09%09
+paths.
 includes(inc
@@ -1185,16 +1185,22 @@
 es) %7B%0A%09%09
+paths.
 include(
@@ -1367,24 +1367,30 @@
 cludes) %7B%0A%09%09
+paths.
 exclude(excl
@@ -1560,16 +1560,22 @@
 es) %7B%0A%09%09
+paths.
 excludes
@@ -2111,32 +2111,38 @@
 les.isEmpty() ? 
+paths.
 matches(defaultI
@@ -2144,32 +2144,38 @@
 aultIncludes) : 
+paths.
 matches());%0A%09%7D%0A%0A
@@ -2706,16 +2706,22 @@
 pty() ? 
+paths.
 matches(
@@ -2739,16 +2739,22 @@
 udes) : 
+paths.
 matches(

Refactor to avoid API leakage from PathSet