This commit is contained in:
2022-01-07 12:03:56 +01:00
parent 414235ecdd
commit 9956fb4c26
56 changed files with 213 additions and 202 deletions

View File

@@ -0,0 +1,24 @@
namespace FileTime.Core.Command
{
public class CommandExecutor
{
private readonly List<ICommandHandler> _commandHandlers;
public CommandExecutor(IEnumerable<ICommandHandler> commandHandlers)
{
_commandHandlers = commandHandlers.ToList();
}
public void ExecuteCommand(ICommand command)
{
if (command is IExecutableCommand executableCommand)
{
executableCommand.Execute();
}
else
{
_commandHandlers.Find(c => c.CanHandle(command))?.Execute(command);
}
}
}
}

View File

@@ -0,0 +1,62 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class CopyCommand : ITransportationCommand
{
public IList<IAbsolutePath> Sources { get; } = new List<IAbsolutePath>();
public IContainer? Target { get; set; }
public TransportMode TransportMode { get; set; } = TransportMode.Merge;
public PointInTime SimulateCommand(PointInTime delta)
{
throw new NotImplementedException();
}
public void Execute(Action<IAbsolutePath, IAbsolutePath> copy)
{
DoCopy(Sources, Target, TransportMode, copy);
}
private void DoCopy(IEnumerable<IAbsolutePath> sources, IContainer target, TransportMode transportMode, Action<IAbsolutePath, IAbsolutePath> copy)
{
foreach (var source in sources)
{
var item = source.ContentProvider.GetByPath(source.Path);
if (item is IContainer container)
{
var targetContainer = target.Containers.FirstOrDefault(d => d.Name == container.Name) ?? (target.CreateContainer(container.Name)!);
var childDirectories = container.Containers.Select(d => new AbsolutePath(item.Provider, d.FullName!));
var childFiles = container.Elements.Select(f => new AbsolutePath(item.Provider, f.FullName!));
DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode, copy);
}
else if (item is IElement element)
{
var targetName = element.Name;
if (transportMode == TransportMode.Merge)
{
for (var i = 0; target.IsExists(targetName); i++)
{
targetName = element.Name + (i == 0 ? "_" : $"_{i}");
}
}
else if (transportMode == TransportMode.Skip && target.IsExists(targetName))
{
continue;
}
var targetPath = target.FullName + Constants.SeparatorChar + targetName;
copy(new AbsolutePath(source.ContentProvider, element.FullName!), new AbsolutePath(target.Provider, targetPath));
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class CreateContainerCommand : ICommand
{
public PointInTime SimulateCommand(PointInTime delta)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,12 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class CreateElementCommand : ICommand
{
public PointInTime SimulateCommand(PointInTime delta)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,41 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class DeleteCommand : IExecutableCommand
{
public IList<IAbsolutePath> ItemsToDelete { get; } = new List<IAbsolutePath>();
public PointInTime SimulateCommand(PointInTime delta)
{
throw new NotImplementedException();
}
public void Execute()
{
foreach (var item in ItemsToDelete)
{
DoDelete(item.ContentProvider.GetByPath(item.Path)!);
}
}
private void DoDelete(IItem item)
{
if (item is IContainer container)
{
foreach (var child in container.Items)
{
DoDelete(child);
child.Delete();
}
item.Delete();
}
else if(item is IElement element)
{
element.Delete();
}
}
}
}

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public interface ICommand
{
PointInTime SimulateCommand(PointInTime moment);
}
}

View File

@@ -0,0 +1,8 @@
namespace FileTime.Core.Command
{
public interface ICommandHandler
{
bool CanHandle(object command);
void Execute(object command);
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.Command
{
public interface IExecutableCommand : ICommand
{
void Execute();
}
}

View File

@@ -0,0 +1,11 @@
using FileTime.Core.Models;
namespace FileTime.Core.Command
{
public interface ITransportationCommand : ICommand
{
IList<IAbsolutePath> Sources { get; }
IContainer Target { get; set;}
TransportMode TransportMode { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class MoveCommand : ITransportationCommand
{
public IList<IAbsolutePath> Sources { get; } = new List<IAbsolutePath>();
public IContainer? Target { get; set; }
public TransportMode TransportMode { get; set; } = TransportMode.Merge;
public PointInTime SimulateCommand(PointInTime delta)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,9 @@
namespace FileTime.Core.Command
{
public enum TransportMode
{
Merge,
Overwrite,
Skip
}
}

View File

@@ -0,0 +1,168 @@
using FileTime.Core.Models;
namespace FileTime.Core.Components
{
public class Tab
{
private IItem? currentSelectedItem;
private IContainer currentLocation;
public IContainer CurrentLocation
{
get => currentLocation;
private set
{
if (currentLocation != value)
{
if (currentLocation != null)
{
currentLocation.Refreshed -= HandleCurrentLocationRefresh;
}
currentLocation = value;
CurrentLocationChanged?.Invoke(this, EventArgs.Empty);
CurrentSelectedItem = CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null;
currentLocation.Refreshed += HandleCurrentLocationRefresh;
}
}
}
public IItem? CurrentSelectedItem
{
get => currentSelectedItem;
private set
{
if (currentSelectedItem != value && (currentLocation.Items.Contains(value) || value == null))
{
currentSelectedItem = value;
CurrentSelectedIndex = GetItemIndex(value);
}
}
}
public int CurrentSelectedIndex { get; private set; }
public event EventHandler CurrentLocationChanged;
public Tab(IContainer currentPath)
{
CurrentLocation = currentPath;
CurrentSelectedItem = CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null;
}
private void HandleCurrentLocationRefresh(object? sender, EventArgs e)
{
var currentSelectedName = CurrentSelectedItem?.FullName;
if (currentSelectedName != null)
{
CurrentSelectedItem = CurrentLocation.Items.FirstOrDefault(i => i.FullName == currentSelectedName) ?? currentLocation.Items.FirstOrDefault();
}
}
public void SelectFirstItem()
{
if (CurrentLocation.Items.Count > 0)
{
CurrentSelectedItem = CurrentLocation.Items[0];
}
}
public void SelectLastItem()
{
if (CurrentLocation.Items.Count > 0)
{
CurrentSelectedItem = CurrentLocation.Items[CurrentLocation.Items.Count - 1];
}
}
public void SelectPreviousItem(int skip = 0)
{
var possibleItemsToSelect = CurrentLocation.Items.Take(CurrentSelectedIndex).Reverse().Skip(skip).ToList();
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = CurrentLocation.Items.ToList();
SelectItem(possibleItemsToSelect);
}
public void SelectNextItem(int skip = 0)
{
var possibleItemsToSelect = CurrentLocation.Items.Skip(CurrentSelectedIndex + 1 + skip).ToList();
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = CurrentLocation.Items.Reverse().ToList();
SelectItem(possibleItemsToSelect);
}
private void SelectItem(IEnumerable<IItem> currentPossibleItems)
{
if (!currentPossibleItems.Any()) return;
if (CurrentSelectedItem != null)
{
CurrentLocation.Refresh();
IItem? newSelectedItem = null;
foreach (var item in currentPossibleItems)
{
if (CurrentLocation.Items.FirstOrDefault(i => i.Name == item.Name) is var possibleNewSelectedItem
&& possibleNewSelectedItem is not null)
{
newSelectedItem = possibleNewSelectedItem;
break;
}
}
CurrentSelectedItem = newSelectedItem ?? (CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null);
}
else
{
CurrentSelectedItem = CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null;
}
}
public void GoUp()
{
var lastCurrentLocation = CurrentLocation;
var parent = CurrentLocation.GetParent();
if (parent is not null)
{
if (lastCurrentLocation is VirtualContainer lastCurrentVirtualContainer)
{
CurrentLocation = lastCurrentVirtualContainer.CloneVirtualChainFor(parent, v => v.IsPermanent);
CurrentSelectedItem = lastCurrentVirtualContainer.GetRealContainer();
}
else
{
CurrentLocation = parent;
CurrentSelectedItem = lastCurrentLocation;
}
}
}
public void Open()
{
if (currentSelectedItem is IContainer childContainer)
{
if (CurrentLocation is VirtualContainer currentVirtuakContainer)
{
CurrentLocation = currentVirtuakContainer.CloneVirtualChainFor(childContainer, v => v.IsPermanent);
}
else
{
CurrentLocation = childContainer;
}
}
}
public void OpenContainer(IContainer container) => CurrentLocation = container;
private int GetItemIndex(IItem? item)
{
if (item == null) return -1;
for (var i = 0; i < CurrentLocation.Items.Count; i++)
{
if (CurrentLocation.Items[i] == item) return i;
}
return -1;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.Extensions
{
public static class GeneralExtensions
{
public static TResult Map<T, TResult>(this T obj, Func<T, TResult> map) => map(obj);
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,17 @@
using FileTime.Core.Providers;
namespace FileTime.Core.Models
{
public class AbsolutePath : IAbsolutePath
{
public IContentProvider ContentProvider { get; }
public string Path { get; }
public AbsolutePath(IContentProvider contentProvider, string path)
{
ContentProvider = contentProvider;
Path = path;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.Models
{
public static class Constants
{
public const char SeparatorChar = '/';
}
}

View File

@@ -0,0 +1,10 @@
using FileTime.Core.Providers;
namespace FileTime.Core.Models
{
public interface IAbsolutePath
{
IContentProvider ContentProvider { get; }
string Path { get; }
}
}

View File

@@ -0,0 +1,19 @@
namespace FileTime.Core.Models
{
public interface IContainer : IItem
{
IReadOnlyList<IItem> Items { get; }
IReadOnlyList<IContainer> Containers { get; }
IReadOnlyList<IElement> Elements { get; }
void Refresh();
IContainer? GetParent();
IItem? GetByPath(string path);
IContainer CreateContainer(string name);
IElement CreateElement(string name);
bool IsExists(string name);
event EventHandler? Refreshed;
}
}

View File

@@ -0,0 +1,8 @@
namespace FileTime.Core.Models
{
public interface IElement : IItem
{
bool IsSpecial { get; }
string GetPrimaryAttributeText();
}
}

View File

@@ -0,0 +1,13 @@
using FileTime.Core.Providers;
namespace FileTime.Core.Models
{
public interface IItem
{
string Name { get; }
string? FullName { get; }
bool IsHidden { get; }
IContentProvider Provider { get; }
void Delete();
}
}

View File

@@ -0,0 +1,122 @@
using FileTime.Core.Providers;
namespace FileTime.Core.Models
{
public class VirtualContainer : IContainer
{
private readonly List<Func<IEnumerable<IContainer>, IEnumerable<IContainer>>> _containerTransformators;
private readonly List<Func<IEnumerable<IElement>, IEnumerable<IElement>>> _elementTransformators;
public IContainer BaseContainer { get; }
public bool IsPermanent { get; }
public bool IsTransitive { get; }
public string? VirtualContainerName { get; }
public IReadOnlyList<IItem> Items { get; private set; }
public IReadOnlyList<IContainer> Containers { get; private set; }
public IReadOnlyList<IElement> Elements { get; private set; }
public string Name => BaseContainer.Name;
public string? FullName => BaseContainer.FullName;
public bool IsHidden => BaseContainer.IsHidden;
public IContentProvider Provider => BaseContainer.Provider;
public event EventHandler? Refreshed
{
add => BaseContainer.Refreshed += value;
remove => BaseContainer.Refreshed -= value;
}
public VirtualContainer(
IContainer baseContainer,
List<Func<IEnumerable<IContainer>, IEnumerable<IContainer>>> containerTransformators,
List<Func<IEnumerable<IElement>, IEnumerable<IElement>>> elementTransformators,
bool isPermanent = false,
bool isTransitive = false,
string? virtualContainerName = null)
{
BaseContainer = baseContainer;
_containerTransformators = containerTransformators;
_elementTransformators = elementTransformators;
InitItems();
IsPermanent = isPermanent;
IsTransitive = isTransitive;
VirtualContainerName = virtualContainerName;
}
private void InitItems()
{
Containers = _containerTransformators.Aggregate(BaseContainer.Containers.AsEnumerable(), (a, t) => t(a)).ToList().AsReadOnly();
Elements = _elementTransformators.Aggregate(BaseContainer.Elements.AsEnumerable(), (a, t) => t(a)).ToList().AsReadOnly();
Items = Containers.Cast<IItem>().Concat(Elements).ToList().AsReadOnly();
}
public IItem? GetByPath(string path) => BaseContainer.GetByPath(path);
public IContainer? GetParent() => BaseContainer.GetParent();
public void Refresh()
{
BaseContainer.Refresh();
InitItems();
}
public IContainer GetRealContainer() =>
BaseContainer is VirtualContainer virtualContainer ? virtualContainer.GetRealContainer() : BaseContainer;
public bool HasWithName(string name) =>
VirtualContainerName == name
|| (BaseContainer is VirtualContainer virtualContainer
&& virtualContainer.HasWithName(name));
public IContainer ExceptWithName(string name)
{
if (BaseContainer is VirtualContainer virtualBaseContainer && virtualBaseContainer.VirtualContainerName == name)
{
return new VirtualContainer(
virtualBaseContainer.ExceptWithName(name),
_containerTransformators,
_elementTransformators,
IsPermanent,
IsTransitive,
VirtualContainerName);
}
else if (VirtualContainerName == name)
{
return BaseContainer;
}
return this;
}
public IContainer CloneVirtualChainFor(IContainer container, Func<VirtualContainer, bool> predicate)
{
var baseContainer = BaseContainer is VirtualContainer baseVirtualContainer
? baseVirtualContainer.CloneVirtualChainFor(container, predicate)
: container;
return predicate(this)
? new VirtualContainer(
baseContainer,
_containerTransformators,
_elementTransformators,
IsPermanent,
IsTransitive,
VirtualContainerName)
: baseContainer;
}
public IContainer CreateContainer(string name) => BaseContainer.CreateContainer(name);
public IElement CreateElement(string name) => BaseContainer.CreateElement(name);
public bool IsExists(string name) => BaseContainer.IsExists(name);
public void Delete() => BaseContainer.Delete();
}
}

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Models;
namespace FileTime.Core.Providers
{
public interface IContentProvider : IContainer
{
IReadOnlyList<IContainer> RootContainers { get; }
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.StateManagement
{
public class ElementCreationStates
{
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.Timeline
{
public class ContainerSnapshot
{
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.ObjectModel;
using FileTime.Core.Providers;
namespace FileTime.Core.Timeline
{
public class PointInTime
{
private readonly Dictionary<IContentProvider, RootSnapshot> snapshots = new();
public IReadOnlyDictionary<IContentProvider, RootSnapshot> Snapshots => new Lazy<IReadOnlyDictionary<IContentProvider, RootSnapshot>>(() => new ReadOnlyDictionary<IContentProvider, RootSnapshot>(snapshots)).Value;
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.Timeline
{
public class RootSnapshot
{
}
}