private T createVirtualEntity <T>(VirtualDirectory parentDirectory, string name)
            where T : BaseVirtualEntity
        {//todo: refactoring required
            Func <DirectoryEntry, Regex, IEnumerable <BaseEntry> > getInnerEntities;
            Func <long>      getNewEntryId;
            Func <BaseEntry> createEntry;
            Func <long, T>   getVirtualEntry;

            var parentDirectoryEntry = getDirectoryEntry(parentDirectory);
            var entryName            = VirtualPath.GetFileNameWithoutExtension(name);

            if (typeof(T) == typeof(VirtualFile))
            {
                getNewEntryId    = () => Indexer.GetNewFileId();
                getInnerEntities = (directoryEntry, searchString) => getDirectoryFiles(directoryEntry, false, searchString);
                createEntry      = () =>
                {
                    var extension = VirtualPath.GetFileExtension(name);
                    return(new FileEntry
                    {
                        Id = getNewEntryId(),
                        DirectoryId = parentDirectory.Id,
                        Name = entryName,
                        Extension = extension
                    });
                };
                getVirtualEntry = id => getVirtualFile(id) as T;
            }
            else if (typeof(T) == typeof(VirtualDirectory))
            {
                getNewEntryId    = () => Indexer.GetNewDirectoryId();
                getInnerEntities = (directoryEntry, searchString) => getNestedDirecories(directoryEntry, false, searchString);
                createEntry      = () => new DirectoryEntry
                {
                    Id          = getNewEntryId(),
                    DirectoryId = parentDirectory.Id,
                    Name        = entryName
                };
                getVirtualEntry = id => getVirtualDirectory(id) as T;
            }
            else
            {
                throw new InvalidOperationException();
            }

            var innerEntities = getInnerEntities(parentDirectoryEntry, makeRegexPattern(name));

            if (innerEntities.Any())
            {
                throw new InvalidOperationException($"Item with the same name exists in the directory {GetDirectoryName(parentDirectoryEntry.Id)}");
            }

            var newEntry = createEntry();

            _rawDataManager.Write(newEntry);
            Indexer.AddEntry(newEntry);
            var retv = getVirtualEntry(newEntry.Id);

            return(retv);
        }
        public void T7_GetFilenameWithoutExtensionTest()
        {
            var path     = "foo/bar/someFile.dat";
            var filename = VirtualPath.GetFileNameWithoutExtension(path);

            Assert.AreEqual("someFile", filename);
        }
        public void RenameFile(VirtualFile file, string newName)
        {
            var fileEntry       = getFileEntry(file);
            var parentDirectory = getVirtualDirectory(fileEntry.DirectoryId);

            if (!_locker.CanWrite(fileEntry))
            {
                throw new AccessViolationException("Unable to rename file: file is locked");
            }

            var name = VirtualPath.GetFileNameWithoutExtension(newName);

            VirtualPath.CheckRestrictedSymbols(name);

            if (fileEntry.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
            {
                return;
            }

            Indexer.TryGetParentDirectory(fileEntry, out var parentDirectoryEntry);
            if (getDirectoryFiles(parentDirectoryEntry, false, makeRegexPattern(name)).Any())
            {
                throw new InvalidOperationException($"Item with the same name exists in the directory {GetDirectoryName(parentDirectoryEntry.Id)}");
            }

            using (_locker.LockWriting(fileEntry))
                using (_locker.LockReading(fileEntry))
                {
                    var newExtension = VirtualPath.GetFileExtension(newName);
                    fileEntry.ModificationTime = DateTime.Now;
                    fileEntry.Name             = name;
                    fileEntry.Extension        = newExtension;
                    Cache.FileNames.Remove(fileEntry.Id);
                    _rawDataManager.Write(fileEntry);
                }

            _directoryWatcherSource.RaiseUpdated(parentDirectory, file);
        }
        public async Task <VirtualDirectory> CopyDirectory(VirtualDirectory directory, VirtualDirectory target, Action <ProgressArgs> progressCallback, CancellationToken cancellationToken)
        {
            var directoryEntry = getDirectoryEntry(directory);
            var targetEntry    = getDirectoryEntry(target);
            var directoryName  = VirtualPath.GetFileName(directoryEntry.Name);//get name from full path

            if (directoryEntry.DirectoryId == targetEntry.Id)
            {
                directoryName = $"Copy {directoryName}";
            }

            var idx = 0;

            while (target.GetDirectories(false, directoryName).Any())
            {
                var name = VirtualPath.GetFileNameWithoutExtension(directoryName);
                directoryName = $"{name} ({++idx})";
            }

            VirtualDirectory createdDirectory = null;
            var srcDirsQueue            = new Queue <VirtualDirectory>(new[] { directory });
            var parentTargetDirectories = new Dictionary <VirtualDirectory, VirtualDirectory>//key: dir from the queue, val: parent dir where should be created nested directory
            {
                [directory] = target
            };

            var totalFiles     = 1;
            var processedFiles = 0;

            while (srcDirsQueue.Any() && !cancellationToken.IsCancellationRequested)
            {
                var currentSourceDirectory = srcDirsQueue.Dequeue();
                var currentTargetDirectory = parentTargetDirectories[currentSourceDirectory].CreateDirectory(currentSourceDirectory.Name);

                _directoryWatcherSource.RaiseCreated(parentTargetDirectories[currentSourceDirectory], currentTargetDirectory);

                if (currentSourceDirectory == directory)//this should happen during the first iteration
                {
                    createdDirectory = currentTargetDirectory;
                }

                var nestedDirectories = currentSourceDirectory.GetDirectories();
                foreach (var nestedDirectory in nestedDirectories)
                {
                    srcDirsQueue.Enqueue(nestedDirectory);
                    parentTargetDirectories[nestedDirectory] = currentTargetDirectory;
                }

                var directoryFiles = currentSourceDirectory.GetFiles().ToList();
                totalFiles += directoryFiles.Count;
                foreach (var directoryFile in directoryFiles)
                {
                    processedFiles++;
                    var processedFilesLocal = processedFiles;
                    var totalFilesLocal     = totalFiles;
                    await directoryFile.CopyTo(currentTargetDirectory, args =>
                    {
                        var progress = (float)args.Progress *processedFilesLocal / totalFilesLocal * 100;
                        progressCallback?.Invoke(new CopyProgressArgs((int)progress, args.Message)
                        {
                            Operation = Operation.Copying
                        });
                    }, cancellationToken);
                }
            }

            return(createdDirectory);
        }
        public async Task <VirtualFile> CopyFile(VirtualFile file, VirtualDirectory targetDirectory, Action <ProgressArgs> progressCallback, CancellationToken cancellationToken)
        {
            var fileEntry = getFileEntry(file);
            var filename  = VirtualPath.GetFileName(file.Name);//get filename from full path

            if (fileEntry.DirectoryId == targetDirectory.Id)
            {
                filename = $"Copy {filename}";
            }

            var idx = 0;

            while (targetDirectory.GetFiles(false, filename).Any())
            {
                var name = VirtualPath.GetFileNameWithoutExtension(filename);
                var ext  = VirtualPath.GetFileExtension(filename);
                filename = $"{name} ({++idx}).{ext}";
            }

            var createdFile = targetDirectory.CreateFile(filename);

            using (var newFileStream = createdFile.Open(FileMode.Open, FileAccess.Write))
                using (var sourceFileStream = file.Open(FileMode.Open, FileAccess.Read))
                {
                    newFileStream.SetLength(sourceFileStream.Length);
                    var restBytes       = sourceFileStream.Length;
                    var buffer          = BufferHelper.GetBuffer(sourceFileStream);
                    var currentProgress = -1;
                    while (restBytes > 0 && !cancellationToken.IsCancellationRequested)
                    {
                        try
                        {
                            var count = (int)(restBytes > buffer.Length ? buffer.Length : restBytes);
                            var read  = await sourceFileStream.ReadAsync(buffer, 0, count, cancellationToken);

                            await newFileStream.WriteAsync(buffer, 0, count, cancellationToken);

                            restBytes -= read;
                        }
                        catch (TaskCanceledException)
                        {
                        }

                        //report progress
                        var progress = sourceFileStream.GetProgress();
                        if (progress == currentProgress)
                        {
                            continue;
                        }

                        currentProgress = progress;
                        var message      = VirtualPath.GetFileName(createdFile.Name);
                        var progressArgs = new CopyProgressArgs(progress, message)
                        {
                            Operation = Operation.Copying
                        };
                        _synchronizationContext.Post(x => progressCallback?.Invoke((ProgressArgs)x), progressArgs);
                    }
                }

            if (!cancellationToken.IsCancellationRequested)
            {
                return(createdFile);
            }

            Remove(createdFile);
            _directoryWatcherSource.RaiseDeleted(targetDirectory, file);

            return(createdFile);
        }