using FileSearch.Logic.Model.CriterionSchemas; using FileSearch.Logic.Model.Engine; using System.Collections.ObjectModel; namespace FileSearch.Logic { internal class FileSearcher { private readonly IList _additionalCriterion; private readonly EngineOptions _engineOptions; private readonly IList _expections; private bool _stop; private bool _pause; private Task> _currentTask; private DateTime _startTime; public FileSearcher(EngineOptions engineOptions, IEnumerable additionalCriterion) { if (engineOptions == null) throw new ArgumentNullException("engineOptions"); _engineOptions = engineOptions; _additionalCriterion = additionalCriterion.ToList(); this.RefreshTimer = TimeSpan.FromSeconds(1); _expections = new List(); } /// /// Получает или задает интервал времени для тайм-аута обратного вызова сопоставления. /// public TimeSpan RefreshTimer { get; set; } /// /// Получает время, в течение которого поисковая система работала над последней операцией. /// public TimeSpan OperatingTime { get; private set; } /// /// Получает время, в течение которого поисковая система работает. /// public TimeSpan CurrentTime { get; private set; } /// /// Получает список всех исключений поиска последней операции. /// public IList Exceptions { get { return new ReadOnlyCollection(_expections); } } /// /// Значение, указывающее, работает ли поисковая система. /// public bool IsRunning { get { return _currentTask != null; } } /// /// Получает список всех критериев, которые использовались в текущей или последней операции. /// public IList UsedCriteria { get; private set; } /// /// Запускает операцию поиска. /// /// Обратный вызов при обнаружении совпадений. /// Обратный вызов после завершения поиска. public void Start(Action> matchCallback, Action finishCallback) { this.OperatingTime = new TimeSpan(); _startTime = DateTime.UtcNow; _expections.Clear(); var timeout = new TimedCallback(this.RefreshTimer, matchCallback); _stop = false; _currentTask = Task.Factory.StartNew>(() => Search(timeout)); _currentTask.ContinueWith(t => { timeout.SetData(t.Result); }) .ContinueWith(t => { _currentTask = null; OperatingTime = DateTime.UtcNow - _startTime; finishCallback(); }); } /// /// Прерывает текущую операцию поиска. /// public void Stop() { if (IsRunning) { _pause = false; _stop = true; } } /// /// Приостанавливает текущую операцию поиска. /// public void Pause(Action state, bool update) { if (IsRunning) _pause = update; state(_pause); } private IList BuildCriteria() { // Разрешить только один IPostProcessingCriterion. В противном случае результаты будут странными. var criteria = CriteriaFactory.Build(_engineOptions).Union(_additionalCriterion).OrderBy(c => c is IPostProcessingCriterion).ThenBy(c => c.Weight).ToList(); UsedCriteria = new ReadOnlyCollection(criteria); return criteria; } private IList Search(object state) { var timer = (TimedCallback)state; var criteria = BuildCriteria(); var list = new List(64); var requiresPostProcessing = criteria.Any(c => c is IPostProcessingCriterion); foreach (var rootDirectory in _engineOptions.RootDirectories) { foreach (var fileSystemInfo in ListAllFileSystemInfo(rootDirectory, -1)) { var contexts = new Dictionary(); var isDir = fileSystemInfo is DirectoryInfo; var match = true; this.CurrentTime = DateTime.UtcNow - _startTime; //Приостановить цикл while (_pause) { Console.WriteLine(""); } try { foreach (var c in criteria) { var context = c.BuildContext(); // Проверьте, поддерживает ли критерий тип целевой файловой системы. if ((c.DirectorySupport && isDir) || (c.FileSupport && !isDir)) { if (c.IsMatch(fileSystemInfo, context)) { // Добавьте контекст, если он совпадает. if (context != null) contexts.Add(c.GetType(), context); } else { match = false; break; } } } } catch (Exception ex) { _expections.Add(SearchExceptionFactory.Build(fileSystemInfo, ex)); match = false; } if (match && !requiresPostProcessing) list.Add(new SearchResult(fileSystemInfo) { Metadata = null }); // Есть верно, и результат не важен. if (list.Count > 0 && !requiresPostProcessing && timer.DataNeeded) { timer.SetData(list); list = new List(64); } // Остановить цикл if (_stop) break; } // Остановить цикл if (_stop) break; } if (requiresPostProcessing) { // Выбираем последний, это самый интенсивный критерий. var resultLists = criteria.OfType().Single(); return resultLists.PostProcess().ToList(); } return list; } private IEnumerable ListAllFileSystemInfo(FileSystemInfo fileSystemInfo, int level) { var directoryInfo = fileSystemInfo as DirectoryInfo; var isRoot = level == -1; // Возвращает папку или, если это файл, всегда. Пропускает корневой уровень. if (!isRoot && (directoryInfo == null)) yield return fileSystemInfo; if (directoryInfo != null || isRoot) { FileSystemInfo[] infos = null; try { infos = directoryInfo.GetFileSystemInfos(); } catch (UnauthorizedAccessException ex) { _expections.Add(SearchExceptionFactory.Build(directoryInfo, ex)); } if (infos == null) yield break; foreach (var item in infos.SelectMany(s => ListAllFileSystemInfo(s, level + 1))) { if (_stop) yield break; yield return item; } } } } }