/// <summary> /// Создает Iterator для перебора файлов в папке, имена которых удовлетворяют заданной маске (<paramref name="patternNamesOfFilesMustMatch"/>). /// Работает рекурсивно - то есть перебирает и файлы во всех подпапках указанной папки. /// Note: генерирует исключение (<see cref="InvalidOperationException"/>), если папка, по которой производится обход, меняется во время обхода (итераторы не thread-safe, я бы их лучше не показывал, но это явно зафиксированное требования). /// </summary> /// <param name="folderToEnumerateFilesIn">Папка, в которой следует искать файлы.</param> /// <param name="patternNamesOfFilesMustMatch">Маска для поиска файлов. Звездочка означает любую последовательность символов. Знак вопроса - один любой символ.</param> /// <returns>Iterator для перебора файлов в папке, имена которых удовлетворяют заданной маске (<paramref name="patternNamesOfFilesMustMatch"/>)</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="ObjectDisposedException"></exception> public IEnumerator <FileInfo> EnumerateFilesUnderFolder(string folderToEnumerateFilesIn, string patternNamesOfFilesMustMatch) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(folderToEnumerateFilesIn, "folderToEnumerateFilesIn"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(patternNamesOfFilesMustMatch, "patternNamesOfFilesMustMatch"); Monitor.Enter(_operationExecutionCriticalSection); try { ThrowIfDisposed(); var nodeResolvingResult = _nodeResolver.ResolveFolderNodeByPath(folderToEnumerateFilesIn); var folder = new FolderInfo(nodeResolvingResult.ResolvedNode, folderToEnumerateFilesIn); var enumerator = new FolderContentsEnumerator(folder, patternNamesOfFilesMustMatch, _folderEnumeratorRegistry, this); _folderEnumeratorRegistry.RegisterEnumerator(enumerator); return(enumerator); } catch (InvalidPathException) { throw new FolderNotFoundException( "Не удалось найти папку по указанному пути (\"{0}\")".FormatWith(folderToEnumerateFilesIn)); } finally { Monitor.Exit(_operationExecutionCriticalSection); } }
/// <summary> /// Открывает указанный файл для записи и чтения. /// </summary> /// <param name="fullPathForFile">Путь, указывающий открываемый файл.</param> /// <returns>Поток данных файла, допускающий чтение и запись данных</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileLockedException"></exception> /// <exception cref="ObjectDisposedException"></exception> public DataStreamReadableWritable OpenFileForWriting(string fullPathForFile) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fullPathForFile, "fullPathForFile"); try { NodeWithSurroundingsResolvingResult <FileNode> nodeResolvingResult; DataStreamStructureBuilder dataStreamStructureBuilder = GetDataStreamStructureBuilderResolvingTheFile(fullPathForFile, out nodeResolvingResult); Guid lockId = _lockingManager.AcquireLock(nodeResolvingResult, LockKind.Write); var stream = new DataStream(dataStreamStructureBuilder, AddressingSystemParameters.Default.BlockSize, _lockingManager, lockId); var nodeUpdatingStream = new DataStreamNodeUpdating(stream, nodeResolvingResult.ResolvedNode, _fileSystemNodeStorage); return(nodeUpdatingStream); } catch (InvalidPathException) { throw new FileNotFoundException("Файл не найден по указанному пути (\"{0}\")".FormatWith(fullPathForFile)); } catch (CannotAcquireLockException) { throw new FileLockedException( "Не удалось открыть файл \"{0}\" с блокировкой на запись: он уже заблокирован на чтение или запись." .FormatWith(fullPathForFile)); } }
/// <summary> /// /// </summary> /// <param name="fileSystem"></param> /// <param name="exportingFolderPath"></param> /// <param name="virtualDestinationFolder"></param> /// <param name="taskToken"></param> /// <returns></returns> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="CannotGetImportedFolderStructureException"></exception> /// <exception cref="FolderNotFoundException"></exception> internal static ReadOnlyCollection <FileSystemTaskResult> ImportFolderFromRealFileSystem( this VirtualFileSystem fileSystem, string exportingFolderPath, string virtualDestinationFolder, IFileSystemCancellableTaskToken taskToken) { if (fileSystem == null) { throw new ArgumentNullException("fileSystem"); } MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(exportingFolderPath, "exportingFolderPath"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(virtualDestinationFolder, "virtualDestinationFolder"); FolderAddressable folderAddressable = CreateFileSystemObjectStructureFromFolder(exportingFolderPath); int totalNumberOfFilesToTraverse = folderAddressable.GetTotalFileCount(); // Note: можно уменьшить связность классов, передав сюда через интерфейс фабрику, которая уж знает, как сделать нужного Visitor-а. ImportingAddressableObjectVisitor visitor = new ImportingAddressableObjectVisitor(fileSystem, exportingFolderPath, virtualDestinationFolder, new RealFileContentsBufferFactory(), taskToken, totalNumberOfFilesToTraverse); if (!fileSystem.FolderExists(virtualDestinationFolder)) { throw new FolderNotFoundException("Не удалось найти папку \"{0}\", в которую следует произвести копирование/импорт.".FormatWith(virtualDestinationFolder)); } folderAddressable.Accept(visitor); var results = visitor.GetResult(); return(results); }
/// <summary> /// Удаляет пустую папку, расположенную по указанному пути. Непустые папки в этой версии удалить вызовом одного метода /// нельзя. /// </summary> /// <param name="folderPath">Путь, указывающий, какую папку надо удалить.</param> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="FolderNotEmptyException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ObjectDisposedException"></exception> public void DeleteFolder(string folderPath) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(folderPath, "folderPath"); if (_pathBuilder.PointsToRoot(folderPath)) { throw new InvalidPathException("Не получилось удалить папку: нельзя удалить корень файловой системы."); } try { string folderName; var parentFolderResolvingResult = _nodeResolver.ResolveParentFolderNodeByPath(folderPath, out folderName); FolderNode folderToRemoveDirectoryFrom = parentFolderResolvingResult.ResolvedNode; var allFolders = _nodeResolver.GetAllFoldersFrom(folderToRemoveDirectoryFrom); FolderNode folderToRemove = allFolders.FirstOrDefault(folderNode => _namesComparer.Equals(folderNode.Name, folderName)); if (folderToRemove == null) { throw new FolderNotFoundException("Не найдена папка для удаления (\"0\")".FormatWith(folderPath)); } if ((folderToRemove.FileReferencesStreamDefinition.StreamLengthInBytes > 0) || (folderToRemove.FolderReferencesStreamDefinition.StreamLengthInBytes > 0)) { throw new FolderNotEmptyException("Не удалось удалить папку: она не пуста"); // Note: блокировки не проверяю. В пустой папке блокировать нечего. } var idsOfFolderUnderGivenOne = _nodeResolver.GetIdsOfAllFoldersUnderGivenOne(folderPath); // TODO: посмотреть на исключения этого метода. _blockReferenceListsEditor.TakeOutABlockFromBlockReferenceList(folderToRemoveDirectoryFrom, folderToRemove.DiskBlockIndex, folderToRemoveDirectoryFrom.FolderReferencesStreamDefinition); _freeBlockManager.MarkBlocksAsFree(new[] { folderToRemove.FileReferencesStreamDefinition.ContentsBlockIndex, folderToRemove.FolderReferencesStreamDefinition.ContentsBlockIndex, folderToRemove.DiskBlockIndex }); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(parentFolderResolvingResult.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(parentFolderResolvingResult.ResolvedNode.Id); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(folderToRemove.Id); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(idsOfFolderUnderGivenOne); } catch (CannotResolvePathException) { throw new FolderNotFoundException("Не удалось найти папку по указанному пути (\"{0}\")".FormatWith(folderPath)); } catch (InvalidPathException) { throw new FolderNotFoundException("Не удалось найти папку по указанному пути (\"{0}\")".FormatWith(folderPath)); } }
/// <summary> /// Возвращает сведения о всех подпапках в указанной директории (<paramref name="folderToGetSubfoldersOf"/>). Работает без рекурсии. /// </summary> /// <param name="folderToGetSubfoldersOf">Папка, поддиректории которой надо вернуть.</param> /// <returns>Сведения о всех папках в указанной директории (<paramref name="folderToGetSubfoldersOf"/>)</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="ObjectDisposedException"></exception> public ReadOnlyCollection <FolderInfo> GetAllFoldersFrom(string folderToGetSubfoldersOf) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(folderToGetSubfoldersOf, "folderToGetSubfoldersOf"); lock (_operationExecutionCriticalSection) { ThrowIfDisposed(); return(_nodeResolver.GetAllFoldersFrom(folderToGetSubfoldersOf)); } }
/// <summary> /// Переименовывает указанный файл. /// </summary> /// <param name="fullPathForFile">Путь, указывающий файл для переименования.</param> /// <param name="newFileName">Новое имя файла (без пути).</param> /// <exception cref="FileAlreadyExistsException"></exception> /// <exception cref="InvalidPathException"></exception> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidNameException"></exception> /// <exception cref="FileLockedException"></exception> public void RenameFile(string fullPathForFile, string newFileName) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fullPathForFile, "fullPathForFile"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(newFileName, "newFileName"); if (_pathBuilder.PointsToRoot(fullPathForFile)) { throw new InvalidPathException("Файл не был переименован: имя файла, который следует переименовать, не может указывать на корень файловой системы"); } _nameValidator.Validate(newFileName); try { string fileName; var parentFolderResolvingResult = _nodeResolver.ResolveParentFolderNodeByPath(fullPathForFile, out fileName); string lastPartOfFullPath = _pathBuilder.GetFileOrFolderName(fullPathForFile); if (_namesComparer.Equals(newFileName, lastPartOfFullPath)) { return; } ReadOnlyCollection <FileNode> files; FileNode fileNode = this.ResolveFileNodeThrowingUserFriendlyErrors(fullPathForFile, parentFolderResolvingResult, fileName, "переименовать", out files); if (files.Any(node => _namesComparer.Equals(newFileName, node.Name))) { throw new FileAlreadyExistsException("Не удалось переименовать файл: файл с именем \"{0}\" уже существует в папке, где вы производите переименование".FormatWith(newFileName)); } fileNode.Name = newFileName; _fileSystemNodeStorage.WriteNode(fileNode); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(parentFolderResolvingResult.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(parentFolderResolvingResult.ResolvedNode.Id); } catch (CannotResolvePathException) { throw new FileNotFoundException("Не удалось найти файл {0}".FormatWith(fullPathForFile)); } catch (InvalidPathException) { throw new FileNotFoundException("Не удалось найти файл {0}".FormatWith(fullPathForFile)); } }
/// <summary> /// Копирует указанный файл. /// </summary> /// <param name="fullPathForFileToBeCopied">Полный путь (от корня), указывающий файл, который следует скопировать.</param> /// <param name="newPathThatWillPointToTheCopyOfFile">Полный путь (от корня), указывающий, где будет находиться и как называться копия файла.</param> /// <param name="taskToken"></param> /// <returns>Описание копии файла.</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileLockedException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidPathException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="FileAlreadyExistsException"></exception> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="MaximumFileCountReachedException"></exception> /// <exception cref="TaskCancelledException"></exception> internal FileInfo CopyFile(string fullPathForFileToBeCopied, string newPathThatWillPointToTheCopyOfFile, IFileSystemCancellableTaskToken taskToken) { if (taskToken == null) { throw new ArgumentNullException("taskToken"); } MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fullPathForFileToBeCopied, "fullPathForFileToBeCopied"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(newPathThatWillPointToTheCopyOfFile, "newPathThatWillPointToTheCopyOfFile"); if (_namesComparer.Equals(fullPathForFileToBeCopied, newPathThatWillPointToTheCopyOfFile)) { return (new FileInfo(_nodeResolver.ResolveFileNodeByPath(newPathThatWillPointToTheCopyOfFile).ResolvedNode, newPathThatWillPointToTheCopyOfFile)); } lock (_operationExecutionCriticalSection) { ThrowIfDisposed(); // так себе проверка на этот раз. } // Note: не смотрю, хватит ли там места. Хотя отрицательный ответ на такой вопрос мог бы стать поводом, чтобы ничего не копировать. try { CopyFileContents(taskToken, fullPathForFileToBeCopied, newPathThatWillPointToTheCopyOfFile); return (new FileInfo(_nodeResolver.ResolveFileNodeByPath(newPathThatWillPointToTheCopyOfFile).ResolvedNode, newPathThatWillPointToTheCopyOfFile)); } catch (Exception) { try { this.DeleteFile(newPathThatWillPointToTheCopyOfFile); } catch (InvalidPathException) { } catch (ObjectDisposedException) { throw new ObjectDisposedException("Работа с файловой системой была вами завершена принудительным вызовом метода Dispose() - до того, как был докопирован файл. Рекомендуется удалить следующий файл при очередном запуске системы (простите - в текущей версии ничего более удобного не сделано): \"{0}\"".FormatWith(newPathThatWillPointToTheCopyOfFile)); } catch (FileNotFoundException) { } throw; } }
/// <summary> /// /// </summary> /// <param name="path"></param> /// <returns></returns> /// <exception cref="ArgumentException"></exception> internal string GetFileOrFolderName(string path) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(path, "fullPath"); if (_nameComparer.Equals(_rootPath, path)) { return(path); } if (path.IndexOf(_directorySeparator) < 0) { return(path); } return(path.Remove(0, path.LastIndexOf(_directorySeparator) + 1)); }
/// <summary> /// Делает из абсолютного пути к папке <paramref name="fullPathToTurnIntoRelative"/>, ее путь относительно папки <paramref name="fullPathForFolderToGetRelativePathAgainst"/>. /// </summary> /// <param name="fullPathForFolderToGetRelativePathAgainst"></param> /// <param name="fullPathToTurnIntoRelative"></param> /// <returns></returns> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> internal string GetRelativePath(string fullPathForFolderToGetRelativePathAgainst, string fullPathToTurnIntoRelative) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty("fullPathForFolderToGetRelativePathAgainst", fullPathForFolderToGetRelativePathAgainst); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty("fullPathToTurnIntoRelative", fullPathToTurnIntoRelative); // Note: ordinal ignore case - выносить надо отсюда. Такие вещи надо настраивать в каком-то одном месте. if (!fullPathToTurnIntoRelative.StartsWith(fullPathForFolderToGetRelativePathAgainst, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Чтобы получить путь одной папки относительно другой, необходимо, чтобы та папка, относительно которой считается путь, была родительской папкой (прямо или косвенно) для той, которой считается путь."); } return (fullPathToTurnIntoRelative .Substring(fullPathForFolderToGetRelativePathAgainst.Length, fullPathToTurnIntoRelative.Length - fullPathForFolderToGetRelativePathAgainst.Length) .TrimStart(new[] { _directorySeparator })); }
/// <summary> /// /// </summary> /// <param name="path"></param> /// <returns></returns> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="ArgumentNullException"></exception> public ReadOnlyCollection <FileInfo> GetAllFilesFrom(string path) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(path, "path"); try { var nodeResolvingResult = this.ResolveFolderNodeByPath(path); FolderNode parentFolder = nodeResolvingResult.ResolvedNode; return(this.GetAllFilesFrom(parentFolder).Select(file => new FileInfo(file, _pathBuilder.CombinePaths(path, file.Name))).ToList().AsReadOnly()); } catch (InvalidPathException exception) { throw new FolderNotFoundException("Не удалось получить файлы, содержащиеся в папке \"{0}\". {1}".FormatWith(path, exception.Message)); } }
/// <summary> /// Создает новый файл по указанному пути. /// Примечание: в текущей версии не создает недостающие папки. /// </summary> /// <param name="fullPathToFile">Полный путь (включая имя файла), указывающий, где будет находиться новый файл.</param> /// <returns>Сведения о только что созданном файле.</returns> /// <exception cref="InvalidPathException">Если указанный путь невалиден.</exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderNotFoundException">Если путь (<paramref name="fullPathToFile"/>), за исключением последней части (имени нового файла), указывает на несуществующую папку.</exception> /// <exception cref="FileAlreadyExistsException"></exception> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="MaximumFileCountReachedException"></exception> /// <exception cref="ObjectDisposedException"></exception> public FileInfo CreateFile(string fullPathToFile) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fullPathToFile, "fullPathToFile"); if (_pathBuilder.PointsToRoot(fullPathToFile)) { throw new InvalidPathException("Не удалось создать файл: в качестве пути к файлу нельзя указать корень файловой системы"); } _pathValidator.Validate(fullPathToFile); try { string fileName; var nodeResolvingResult = _nodeResolver.ResolveParentFolderNodeByPath(fullPathToFile, out fileName); FolderNode parentFolder = nodeResolvingResult.ResolvedNode; var newFileInfo = new FileInfo(this.CreateFile(parentFolder, fileName), fullPathToFile); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(nodeResolvingResult.FoldersPassedWhileResolving); return(newFileInfo); } catch (InsufficientSpaceException) { throw new InsufficientSpaceException("Недостаточно свободного места на диске для создания файла."); } catch (MaximumFileSizeReachedException) { throw new MaximumFileCountReachedException("Не удалось добавить файл: папка, в которую вы добавляете файл, не может вместить их больше, чем уже вмещает."); } catch (NoFreeBlocksException) { throw new InsufficientSpaceException("Недостаточно свободного места на диске для создания файла."); } catch (InvalidPathException) { throw new FolderNotFoundException("Не получилось создать файл по указанному пути (\"{0}\"). Не найдена папка, в которой следует создать файл. (В текущей версии недостающие папки не создаются автоматически)".FormatWith(fullPathToFile)); } catch (CannotResolvePathException) { throw new FolderNotFoundException("Не получилось создать файл по указанному пути (\"{0}\"). Не найдена папка, в которой следует создать файл. (В текущей версии недостающие папки не создаются автоматически)".FormatWith(fullPathToFile)); } }
/// <summary> /// /// </summary> /// <param name="parentFolder"></param> /// <param name="newFolderName"></param> /// <returns></returns> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="MaximumFileSizeReachedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderAlreadyExistsException"></exception> private FolderNode CreateFolder(FolderNode parentFolder, string newFolderName) { MethodArgumentValidator.ThrowIfNull(parentFolder, "parentFolder"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(newFolderName, "newFolderName"); var subfolders = _nodeResolver.GetAllFoldersFrom(parentFolder); var folderWithSameName = subfolders.FirstOrDefault(subfolder => _namesComparer.Equals(subfolder.Name, newFolderName)); if (folderWithSameName != null) { throw new FolderAlreadyExistsException("Подпапка с именем \"{0}\" уже существует в папке \"{1}\"".FormatWith(newFolderName, parentFolder.Name)); } var freeBlocks = _freeBlockManager.AcquireFreeBlocks(3); int blockToStoreFileReferencesIn = freeBlocks[0]; int blockToStoreFolderNodeIn = freeBlocks[1]; int blockToStoreSubfolderReferencesIn = freeBlocks[2]; var fileReferencesStreamDefinition = new DataStreamDefinition(blockToStoreFileReferencesIn, 0); var folderReferencesStreamDefinition = new DataStreamDefinition(blockToStoreSubfolderReferencesIn, 0); try { // Note: может стать последней каплей для переполненного диска, и тогда блоки придется отпустить _blockReferenceListsEditor.AddBlockReference(blockToStoreFolderNodeIn, parentFolder.FolderReferencesStreamDefinition, parentFolder); } catch (Exception) { _freeBlockManager.MarkBlocksAsFree(freeBlocks); throw; } var creationTime = DateTime.UtcNow; var newNode = new FolderNode(newFolderName, Guid.NewGuid(), blockToStoreFolderNodeIn, parentFolder.DiskBlockIndex, fileReferencesStreamDefinition, folderReferencesStreamDefinition, creationTime, Guid.NewGuid()); _fileSystemNodeStorage.WriteNode(newNode); return(newNode); }
/// <summary> /// Удаляет указанный файл. /// </summary> /// <param name="filePath">Путь, указывающий удаляемый файл.</param> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileLockedException"></exception> /// <exception cref="InvalidPathException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ObjectDisposedException"></exception> public void DeleteFile(string filePath) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(filePath, "filePath"); if (_pathBuilder.PointsToRoot(filePath)) { throw new InvalidPathException("Не получилось удалить файл: нельзя удалить корень файловой системы."); } try { string fileName; var parentFolderResolvingResult = _nodeResolver.ResolveParentFolderNodeByPath(filePath, out fileName); ReadOnlyCollection <FileNode> files; FolderNode folderToRemoveFileFrom = parentFolderResolvingResult.ResolvedNode; FileNode fileToRemove = ResolveFileNodeThrowingUserFriendlyErrors(filePath, parentFolderResolvingResult, fileName, "удалить", out files); // очистка всех данных. using (var dataStream = this.OpenFileForWriting(filePath)) { dataStream.Truncate(); } // TODO: поглядеть на исключения этого метода. _blockReferenceListsEditor.TakeOutABlockFromBlockReferenceList(folderToRemoveFileFrom, fileToRemove.DiskBlockIndex, folderToRemoveFileFrom.FileReferencesStreamDefinition); _freeBlockManager.MarkBlockAsFree(fileToRemove.DiskBlockIndex); _freeBlockManager.MarkBlockAsFree(fileToRemove.FileContentsStreamDefinition.ContentsBlockIndex); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(parentFolderResolvingResult.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(parentFolderResolvingResult.ResolvedNode.Id); } catch (CannotResolvePathException) { throw new FileNotFoundException("Не удалось найти файл {0}".FormatWith(filePath)); } catch (InvalidPathException) { throw new FileNotFoundException("Не удалось найти файл {0}".FormatWith(filePath)); } }
/// <summary> /// Возвращает сведения о всех подпапках в указанной директории (<paramref name="folderToGetSubfoldersOf"/>). /// </summary> /// <param name="folderToGetSubfoldersOf">Папка, поддиректории которой надо вернуть.</param> /// <returns>Сведения о всех папках в указанной директории (<paramref name="folderToGetSubfoldersOf"/>)</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderNotFoundException"></exception> public ReadOnlyCollection <FolderInfo> GetAllFoldersFrom(string folderToGetSubfoldersOf) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(folderToGetSubfoldersOf, "folderToGetSubfoldersOf"); try { NodeWithSurroundingsResolvingResult <FolderNode> nodeResolvingResult = this.ResolveFolderNodeByPath(folderToGetSubfoldersOf); FolderNode parentFolder = nodeResolvingResult.ResolvedNode; return(this.GetAllFoldersFrom(parentFolder) .Select(folderNode => new FolderInfo(folderNode, _pathBuilder.CombinePaths(folderToGetSubfoldersOf, folderNode.Name))) .ToList() .AsReadOnly()); } catch (InvalidPathException) { throw new FolderNotFoundException("Не удалось найти папку по указанному пути (\"{0}\")".FormatWith(folderToGetSubfoldersOf)); } }
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="FileAlreadyExistsException"></exception> /// <exception cref="NoFreeBlocksException"></exception> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="MaximumFileSizeReachedException"></exception> private FileNode CreateFile(FolderNode parentFolder, string fileName) { MethodArgumentValidator.ThrowIfNull(parentFolder, "parentFolder"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fileName, "fileName"); var filesInTheFolder = _nodeResolver.GetAllFilesFrom(parentFolder); var fileWithSameName = filesInTheFolder.FirstOrDefault(file => _namesComparer.Equals(file.Name, fileName)); if (fileWithSameName != null) { throw new FileAlreadyExistsException("Файл с именем \"{0}\" уже существует в папке \"{1}\"".FormatWith(fileName, parentFolder.Name)); } var freeBlocks = _freeBlockManager.AcquireFreeBlocks(2); int blockToStoreDefinitionIn = freeBlocks[0]; int blockToStoreFileNodeIn = freeBlocks[1]; var fileContentsStreamDefinition = new DataStreamDefinition(blockToStoreDefinitionIn, 0); try { // Note: это добавление ссылки может стать последней каплей для заполненного диска, тогда блоки придется отпустить _blockReferenceListsEditor.AddBlockReference(blockToStoreFileNodeIn, parentFolder.FileReferencesStreamDefinition, parentFolder); } catch (Exception) { _freeBlockManager.MarkBlocksAsFree(freeBlocks); throw; } var creationTime = DateTime.UtcNow; var fileNode = new FileNode(fileName, Guid.NewGuid(), blockToStoreFileNodeIn, fileContentsStreamDefinition, creationTime, creationTime, Guid.NewGuid()); _fileSystemNodeStorage.WriteNode(fileNode); return(fileNode); }
/// <summary> /// Note: case sensitive /// </summary> /// <param name="stringToCheck"></param> /// <param name="characters"></param> /// <param name="characterFound"></param> /// <returns></returns> public static bool ContainsAny(this string stringToCheck, IEnumerable <char> characters, out Nullable <char> characterFound) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(stringToCheck, "stringToCheck"); MethodArgumentValidator.ThrowIfNull(characters, "characters"); var charactersSeenSoFar = new HashSet <char>(); var charactersToCheckFor = new HashSet <char>(characters); foreach (char character in stringToCheck) { if (!charactersSeenSoFar.Contains(character) && charactersToCheckFor.Contains(character)) { characterFound = character; return(true); } charactersSeenSoFar.Add(character); } characterFound = null; return(false); }
internal PathBuilder( char directorySeparator, IPathValidator pathValidator, string rootPath, IEqualityComparer <string> nameComparer) { if (pathValidator == null) { throw new ArgumentNullException("pathValidator"); } if (nameComparer == null) { throw new ArgumentNullException("nameComparer"); } MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(rootPath, "rootPath"); _directorySeparator = directorySeparator; _pathValidator = pathValidator; _rootPath = rootPath; _nameComparer = nameComparer; }
/// <summary> /// Переименовывает папку. /// </summary> /// <param name="fullPathForFolder">Путь, указывающий папку, которую следует переименовать.</param> /// <param name="newFolderName">Новое имя папки (только имя - без пути)</param> /// <exception cref="FolderAlreadyExistsException"></exception> /// <exception cref="InvalidPathException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="FolderLockedException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidNameException"></exception> public void RenameFolder(string fullPathForFolder, string newFolderName) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fullPathForFolder, "fullPathForFolder"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(newFolderName, "newFolderName"); if (_pathBuilder.PointsToRoot(fullPathForFolder)) { throw new InvalidPathException("Папка не была переименована: имя папки не может указывать на корень файловой системы"); } _nameValidator.Validate(newFolderName); try { string folderName; var parentFolderResolvingResult = _nodeResolver.ResolveParentFolderNodeByPath(fullPathForFolder, out folderName); string lastPartOfFullPath = _pathBuilder.GetFileOrFolderName(newFolderName); if (_namesComparer.Equals(folderName, lastPartOfFullPath)) { return; } FolderNode parentFolderNode = parentFolderResolvingResult.ResolvedNode; ReadOnlyCollection <FolderNode> folders = _nodeResolver.GetAllFoldersFrom(parentFolderNode); FolderNode folderNode = folders.FirstOrDefault(node => _namesComparer.Equals(node.Name, folderName)); if (folderNode == null) { throw new FolderNotFoundException("Не найдена папка для переименования (\"{0}\")".FormatWith(fullPathForFolder)); } if (_lockingManager.IsNodeLockedForRenamesAndMoves(folderNode)) { throw new FolderLockedException("Не удалось переименовать папку. В ней (или в одной из ее поддиректорий) есть файлы, которые сейчас открыты."); } if (folders.Any(node => _namesComparer.Equals(newFolderName, node.Name))) { throw new FolderAlreadyExistsException("Не удалось переименовать папку: папка с именем \"{0}\" уже существует в директории, где вы производите переименование".FormatWith(newFolderName)); } var idsOfFolderUnderGivenOne = _nodeResolver.GetIdsOfAllFoldersUnderGivenOne(fullPathForFolder); folderNode.Name = newFolderName; _fileSystemNodeStorage.WriteNode(folderNode); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(parentFolderResolvingResult.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(parentFolderResolvingResult.ResolvedNode.Id); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(idsOfFolderUnderGivenOne); } catch (CannotResolvePathException) { throw new FileNotFoundException("Не удалось найти папку по указанному пути (\"{0}\")".FormatWith(fullPathForFolder)); } catch (InvalidPathException) { throw new FileNotFoundException("Не удалось найти папку по указанному пути (\"{0}\")".FormatWith(fullPathForFolder)); } }
/// <summary> /// Перемещает файл в указанную папку. /// </summary> /// <param name="currentPathForFile">Полный путь (от корня) к файлу, который следует переместить.</param> /// <param name="pathToFolderThatMustContainTheFile">Полный путь к папке, в которую нужно переместить указанный файл (<paramref name="currentPathForFile"/>).</param> /// <returns>Описание перемещенного файла, взятое с учетом его нового местоположения.</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileLockedException"></exception> /// <exception cref="FileAlreadyExistsException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="MaximumFileCountReachedException"></exception> /// <exception cref="InsufficientSpaceException"></exception> public FileInfo MoveFile(string currentPathForFile, string pathToFolderThatMustContainTheFile) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(currentPathForFile, "currentPathForFile"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(pathToFolderThatMustContainTheFile, "pathToFolderThatMustContainTheFile"); try { var newParentNodeResolvingResult = _nodeResolver.ResolveFolderNodeByPath(pathToFolderThatMustContainTheFile); FolderNode filesNewHome = newParentNodeResolvingResult.ResolvedNode; string fileName; var currentParentFolder = _nodeResolver.ResolveParentFolderNodeByPath(currentPathForFile, out fileName); ReadOnlyCollection <FileNode> allFilesInTheFolder; FolderNode folderToRemoveFileFrom = currentParentFolder.ResolvedNode; FileNode fileToMove = this.ResolveFileNodeThrowingUserFriendlyErrors(currentPathForFile, currentParentFolder, fileName, "переместить", out allFilesInTheFolder); var allFilesInDestinationFolder = _nodeResolver.GetAllFilesFrom(filesNewHome); // перемещение в то место, где файл и так уже находится. if (newParentNodeResolvingResult.ResolvedNode.Id.Equals(currentParentFolder.ResolvedNode.Id)) { return(new FileInfo(allFilesInDestinationFolder.First(fileNode => _namesComparer.Equals(fileNode.Name, fileName)), currentPathForFile)); } if (allFilesInDestinationFolder.Any(fileNode => _namesComparer.Equals(fileNode.Name, fileName))) { throw new FileAlreadyExistsException("Перемещение файла невозможно: в папке \"{0}\" уже существует файл с именем \"{1}\"".FormatWith(pathToFolderThatMustContainTheFile, fileName)); } // сначала добавляем ссылку в новую папку, потом удаляем ссылку из старой (на добавление может не хватить места, удалить же можно всегда). _blockReferenceListsEditor.AddBlockReference(fileToMove.DiskBlockIndex, filesNewHome.FileReferencesStreamDefinition, filesNewHome); // TODO: поглядеть на исключения этого метода. _blockReferenceListsEditor.TakeOutABlockFromBlockReferenceList(folderToRemoveFileFrom, fileToMove.DiskBlockIndex, folderToRemoveFileFrom.FileReferencesStreamDefinition); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(newParentNodeResolvingResult.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(newParentNodeResolvingResult.ResolvedNode.Id); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(currentParentFolder.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(currentParentFolder.ResolvedNode.Id); allFilesInDestinationFolder = _nodeResolver.GetAllFilesFrom(filesNewHome); return(new FileInfo( allFilesInDestinationFolder.Single(fileNode => _namesComparer.Equals(fileNode.Name, fileName)), _pathBuilder.CombinePaths(pathToFolderThatMustContainTheFile, fileName))); } catch (InsufficientSpaceException) { throw new InsufficientSpaceException("Не удалось переместить файл из \"{0}\" в \"{1}\" - не хватает места на диске для проведения операции.".FormatWith(currentPathForFile, pathToFolderThatMustContainTheFile)); } catch (MaximumFileSizeReachedException) { throw new MaximumFileCountReachedException("Не удалось переместить файл \"{0}\". Папка \"{1}\" не может вместить больше файлов, чем уже вмещает".FormatWith(currentPathForFile, pathToFolderThatMustContainTheFile)); } catch (CannotResolvePathException exception) { throw new FolderNotFoundException("Не удалось найти папку, где находится перемещаемый файл, или папку, в которую его следует переместить. {0}".FormatWith(exception.Message)); } catch (InvalidPathException exception) { throw new FolderNotFoundException("Не удалось найти папку, где находится перемещаемый файл, или папку, в которую его следует переместить. {0}".FormatWith(exception.Message)); } }
/// <exception cref="InvalidPathException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="MaximumFolderCountReachedException"></exception> /// <exception cref="ObjectDisposedException"></exception> internal ReadOnlyCollection <FileSystemTaskResult> CopyFolder(string fullPathForFolderToCopy, string destinationPathForFolderAndItsContents, IFileSystemCancellableTaskToken taskToken) { if (taskToken == null) { throw new ArgumentNullException("taskToken"); } MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(fullPathForFolderToCopy, "fullPathForFolderToCopy"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(destinationPathForFolderAndItsContents, "destinationPathForFolderAndItsContents"); // TODO: выделить общее у копированиея папок и импорта - там много. var allFilesToBeCopied = new List <FileInfo>(); CreateFoldersNeededToCopy(fullPathForFolderToCopy, destinationPathForFolderAndItsContents, allFilesToBeCopied); var copyTaskResults = new List <FileSystemTaskResult>(); int filesCopied = 0; int percentage = 0; foreach (FileInfo fileInfo in allFilesToBeCopied) { try { if (taskToken.HasBeenCancelled) { copyTaskResults.Add(new FileTaskResult(fileInfo.ToFileAddressable(), null, "Задача снята.")); } else { string relativePathToFile = _pathBuilder.GetRelativePath(fullPathForFolderToCopy, fileInfo.FullPath); string destinationPath = this.PathBuilder.CombinePaths(destinationPathForFolderAndItsContents, relativePathToFile); var tokenWrapper = new TaskTokenPartialWrapper(taskToken); var copiedFileInfo = this.CopyFile(fileInfo.FullPath, destinationPath, tokenWrapper); copyTaskResults.Add(new FileTaskResult(fileInfo.ToFileAddressable(), copiedFileInfo.ToFileAddressable(), String.Empty)); } } catch (TaskCancelledException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (FileNotFoundException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (FileLockedException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (ObjectDisposedException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (InvalidPathException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (FolderNotFoundException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (FileAlreadyExistsException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (InsufficientSpaceException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } catch (MaximumFileCountReachedException exception) { copyTaskResults.Add(CreateCopyTaskFailureFromException(fileInfo, exception)); } finally { // Note: дурацкая часть по высчету прогресса везде общая. Надо выносить. filesCopied++; int newPercentage = (filesCopied * 100) / allFilesToBeCopied.Count; if (newPercentage != percentage) { percentage = newPercentage; taskToken.ReportProgressChange(newPercentage); } } } return(copyTaskResults.AsReadOnly()); }
/// <summary> /// Перемещает папку в указанную директорию. /// </summary> /// <param name="currentPathForFolder">Полный путь (от корня) к папке, которую следует переместить.</param> /// <param name="pathToFolderThatWillContainTheFolderMoved">Полный путь к папке, в которую нужно переместить указанную директорию (<paramref name="currentPathForFolder"/>).</param> /// <returns>Описание перемещенной папки, взятое с учетом ее нового местоположения.</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="FolderLockedException"></exception> /// <exception cref="FolderAlreadyExistsException"></exception> /// <exception cref="FolderNotFoundException"></exception> /// <exception cref="MaximumFolderCountReachedException"></exception> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="InvalidOperationException">Если пытаетесь переместить папку в одну из ее собственных поддиректорий.</exception> public FolderInfo MoveFolder(string currentPathForFolder, string pathToFolderThatWillContainTheFolderMoved) { MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(currentPathForFolder, "cucurrentPathForFolderrrentPathForFile"); MethodArgumentValidator.ThrowIfStringIsNullOrEmpty(pathToFolderThatWillContainTheFolderMoved, "pathToFolderThatWillContainTheFolderMoved"); try { var newParentNodeResolvingResult = _nodeResolver.ResolveFolderNodeByPath(pathToFolderThatWillContainTheFolderMoved); FolderNode foldersNewHome = newParentNodeResolvingResult.ResolvedNode; string folderName; var currentParentFolder = _nodeResolver.ResolveParentFolderNodeByPath(currentPathForFolder, out folderName); FolderNode folderToRemoveDirectoryFrom = currentParentFolder.ResolvedNode; var directoriesInFolder = _nodeResolver.GetAllFoldersFrom(folderToRemoveDirectoryFrom); FolderNode folderToMove = directoriesInFolder.FirstOrDefault(fileNode => _namesComparer.Equals(fileNode.Name, folderName)); if (folderToMove == null) { throw new FolderNotFoundException("Не найдена папка для перемещения (\"{0}\")".FormatWith(currentPathForFolder)); } if (newParentNodeResolvingResult.FoldersPassedWhileResolving.Any(folderNode => folderNode.Id.Equals(folderToMove.Id))) { throw new InvalidOperationException("Операция перемещения папки \"{0}\" в \"{1}\" отменена: невозможно переместить папку в одну из ее же поддиректорий.".FormatWith(currentPathForFolder, pathToFolderThatWillContainTheFolderMoved)); } if (_lockingManager.IsNodeLockedForRenamesAndMoves(folderToMove)) { throw new FolderLockedException("Не удалось переместить папку (\"{0}\"): она или любая из ее поддиректорий содержит файлы открытые сейчас для чтения или записи.".FormatWith(currentPathForFolder)); } var allSubfoldersOfDestinationFolder = _nodeResolver.GetAllFoldersFrom(foldersNewHome); var idsOfAllSubfolders = _nodeResolver.GetIdsOfAllFoldersUnderGivenOne(currentPathForFolder); // перемещение в то место, где файл и так уже находится.) if (newParentNodeResolvingResult.ResolvedNode.Id.Equals(currentParentFolder.ResolvedNode.Id)) { return(new FolderInfo(allSubfoldersOfDestinationFolder.First(fileNode => _namesComparer.Equals(fileNode.Name, folderName)), currentPathForFolder)); } if (allSubfoldersOfDestinationFolder.Any(fileNode => _namesComparer.Equals(fileNode.Name, folderName))) { throw new FolderAlreadyExistsException("Перемещение папки невозможно: в директории \"{0}\" уже существует папка с именем \"{1}\"".FormatWith(pathToFolderThatWillContainTheFolderMoved, folderName)); } // сначала добавляем ссылку в новую папку, потом удаляем ссылку из старой (на добавление может не хватить места, удалить же можно всегда). _blockReferenceListsEditor.AddBlockReference(folderToMove.DiskBlockIndex, foldersNewHome.FolderReferencesStreamDefinition, foldersNewHome); // TODO: поглядеть на исключения этого метода. _blockReferenceListsEditor.TakeOutABlockFromBlockReferenceList(folderToRemoveDirectoryFrom, folderToMove.DiskBlockIndex, folderToRemoveDirectoryFrom.FolderReferencesStreamDefinition); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(newParentNodeResolvingResult.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(newParentNodeResolvingResult.ResolvedNode.Id); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(currentParentFolder.FoldersPassedWhileResolving); _folderEnumeratorRegistry.InvalidateEnumeratorsForFolder(currentParentFolder.ResolvedNode.Id); _folderEnumeratorRegistry.InvalidateEnumeratorsFor(idsOfAllSubfolders); allSubfoldersOfDestinationFolder = _nodeResolver.GetAllFoldersFrom(foldersNewHome); return(new FolderInfo( allSubfoldersOfDestinationFolder.Single(folderNode => _namesComparer.Equals(folderNode.Name, folderName)), _pathBuilder.CombinePaths(pathToFolderThatWillContainTheFolderMoved, folderName))); } catch (InsufficientSpaceException) { throw new InsufficientSpaceException("Не удалось переместить папку из \"{0}\" в \"{1}\" - не хватает места на диске для проведения операции.".FormatWith(currentPathForFolder, pathToFolderThatWillContainTheFolderMoved)); } catch (MaximumFileSizeReachedException) { throw new MaximumFolderCountReachedException("Не удалось переместить папку \"{0}\". Папка \"{1}\" не может вместить больше файлов, чем уже вмещает".FormatWith(currentPathForFolder, pathToFolderThatWillContainTheFolderMoved)); } catch (CannotResolvePathException exception) { throw new FolderNotFoundException("Не удалось найти папку, где находится перемещаемый файл, или папку, в которую его следует переместить. {0}".FormatWith(exception.Message)); } catch (InvalidPathException exception) { throw new FolderNotFoundException("Не удалось найти папку, где находится перемещаемый файл, или папку, в которую его следует переместить. {0}".FormatWith(exception.Message)); } }