package potoxml;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Этот класс предназначен для поиска файлов
 *
 * @author Стаценко Владимир http://www.vova-prog.narod.ru
 */
public class FileFinder
{

	//классы для работы с регулярными выражениями
	private Pattern p = null;
	private Matcher m = null;
	//общий размер найденных файлов
	private long totalLength = 0;
	//общее количество найденных файлов
	private long filesNumber = 0;
	//общее количество просмотренных директорий
	private long directoriesNumber = 0;
	//константы для определения объектов, которые нужно найти
	private final int FILES = 0;
	private final int DIRECTORIES = 1;
	private final int ALL = 2;

	/**
	 * Создает новые экземпляры FileFinder
	 */
	public FileFinder()
	{
	}

	/**
	 * Этот метод выполняет поиск всех объектов (файлов и директорий), начиная с
	 * заданной директории (startPath)
	 *
	 * @param startPath Начальная директория поиска
	 * @return Список (List) найденных объектов
	 * @throws java.lang.Exception если возникли ошибки в процессе поиска
	 */
	public List findAll(String startPath) throws Exception
	{
		return find(startPath, "", ALL);
	}

	/**
	 * Этот метод выполняет поиск объектов (файлов и директорий), которые
	 * соответствуют заданному регулярному выражению (mask), начиная с заданной
	 * директории (startPath)
	 *
	 * @param startPath Начальная директория поиска
	 * @param mask регулярное выражение, которому должны соответствовать имена
	 * найденный объектов
	 * @throws java.lang.Exception если возникли ошибки в процессе поиска
	 * @return Список (List) найденных объектов
	 */
	public List findAll(String startPath, String mask)
			throws Exception
	{
		return find(startPath, mask, ALL);
	}

	/**
	 * Этот метод выполняет поиск всех файлов, начиная с заданной директории
	 * (startPath)
	 *
	 * @param startPath Начальная директория поиска
	 * @return Список (List) найденных объектов
	 * @throws java.lang.Exception если возникли ошибки в процессе поиска
	 */
	public List findFiles(String startPath)
			throws Exception
	{
		return find(startPath, "", FILES);
	}

	/**
	 * Этот метод выполняет поиск файлов, которые соответствуют заданному
	 * регулярному выражению (mask), начиная с заданной директории (startPath)
	 *
	 * @param startPath Начальная директория поиска
	 * @param mask регулярное выражение, которому должны соответствовать имена
	 * найденный объектов
	 * @throws java.lang.Exception если возникли ошибки в процессе поиска
	 * @return Список (List) найденных объектов
	 */
	public List findFiles(String startPath, String mask)
			throws Exception
	{
		return find(startPath, mask, FILES);
	}

	/**
	 * Этот метод выполняет поиск всех директорий (папок), начиная с заданной
	 * директории (startPath)
	 *
	 * @param startPath Начальная директория поиска
	 * @return Список (List) найденных объектов
	 * @throws java.lang.Exception если возникли ошибки в процессе поиска
	 */
	public List findDirectories(String startPath)
			throws Exception
	{
		return find(startPath, "", DIRECTORIES);
	}

	/**
	 * Этот метод выполняет поиск директорий (папок), которые соответствуют
	 * заданному регулярному выражению (mask), начиная с заданной директории
	 * (startPath)
	 *
	 * @param startPath Начальная директория поиска
	 * @param mask регулярное выражение, которому должны соответствовать имена
	 * найденный объектов
	 * @throws java.lang.Exception если возникли ошибки в процессе поиска
	 * @return Список (List) найденных объектов
	 */
	public List findDirectories(String startPath, String mask)
			throws Exception
	{
		return find(startPath, mask, DIRECTORIES);
	}

	/**
	 * Возвращает суммарный размер найденных файлов
	 *
	 * @return размер найденных файлов (байт)
	 */
	public long getDirectorySize()
	{
		return totalLength;
	}

	/**
	 * Возвращает общее количество найденных файлов
	 *
	 * @return количество найденных файлов
	 */
	public long getFilesNumber()
	{
		return filesNumber;
	}

	/**
	 * Возвращает общее количество найденных директорий (папок)
	 *
	 * @return количество найденных директорий (папок)
	 */
	public long getDirectoriesNumber()
	{
		return directoriesNumber;
	}

	/*
	 Проверяет, соответствует ли имя файла заданному
	 регулярному выражению. Возвращает true, если найденный
	 объект соответствует регулярному выражению, false - в
	 противном случае.
	 */
	private boolean accept(String name)
	{
		//если регулярное выражение не задано...
		if (p == null)
		{
			//...значит объект подходит
			return true;
		}
		//создаем Matcher
		m = p.matcher(name);
		//выполняем проверку
		if (m.matches())
		{
			return true;
		} else
		{
			return false;
		}
	}

	/*
	 Этот метод выполняет начальные установки поиска.
	 Затем вызывает метод search для выполнения поиска.
	 */
	private List find(String startPath, String mask, int objectType)
			throws Exception
	{
		//проверка параметров
		if (startPath == null || mask == null)
		{
			throw new Exception("Ошибка: не заданы параметры поиска");
		}
		File topDirectory = new File(startPath);
		if (!topDirectory.exists())
		{
			throw new Exception("Ошибка: указанный путь не существует");
		}
		//если задано регулярное выражение, создаем Pattern
		if (!mask.isEmpty())
		{
			p = Pattern.compile(mask, Pattern.CASE_INSENSITIVE | Pattern.COMMENTS); //Pattern.UNIcomment_CASE
		}
		//обнуляем все счетчики
		filesNumber = 0;
		directoriesNumber = 0;
		totalLength = 0;
		//создаем список результатов
		ArrayList res = new ArrayList(100);

		//выполняем поиск
		search(topDirectory, res, objectType);

		//присваиваем null шаблону, т.к. при следующем вызове find...
		//регулярное выражение может быть не задано
		p = null;
		//возвращаем результат
		return res;
	}

	/*
	 Этот метод выполняет поиск объектов заданного типа.
	 Если, в процессе поиска, встречает вложенную директорию
	 (папку), то рекурсивно вызывает сам себя.
	 Результаты поиска сохраняются в параметре res.
	 Текущая директория - topDirectory.
	 Тип объекта (файл или директория) - objectType.
	 */
	private void search(File topDirectory, List res, int objectType)
	{
		//получаем список всех объектов в текущей директории
		File[] list = topDirectory.listFiles();
		//просматриваем все объекты по-очереди
		for (int i = 0; i < list.length; i++)
		{
			//если это директория (папка)...
			if (list[i].isDirectory())
			{
				//...выполняем проверку на соответствие типу объекта
				// и регулярному выражению...
				if (objectType != FILES && accept(list[i].getName()))
				{
					//...добавляем текущий объект в список результатов,
					//и обновляем значения счетчиков
					directoriesNumber++;
					res.add(list[i]);
				}
				//выполняем поиск во вложенных директориях
				search(list[i], res, objectType);
			} //если это файл
			else
			{
				//...выполняем проверку на соответствие типу объекта
				// и регулярному выражению...
				if (objectType != DIRECTORIES && accept(list[i].getName()))
				{
					//...добавляем текущий объект в список результатов,
					//и обновляем значения счетчиков
					filesNumber++;
					totalLength += list[i].length();
					res.add(list[i]);
				}
			}
		}
	}
}