diff --git a/Source/SeaInk.Application/Exceptions/CrossedFrameException.cs b/Source/SeaInk.Application/Exceptions/CrossedFrameException.cs new file mode 100644 index 0000000..079c7be --- /dev/null +++ b/Source/SeaInk.Application/Exceptions/CrossedFrameException.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.Serialization; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Core.Exceptions; + +namespace SeaInk.Application.Exceptions +{ + [Serializable] + public class CrossedFrameException : SeaInkException + { + public CrossedFrameException(ITableIndex begin, ITableIndex current, Frame frame) + : base($"Index {current} should be in frame {frame} starting at {begin}") { } + + protected CrossedFrameException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/Exceptions/InvalidScaleComponentException.cs b/Source/SeaInk.Application/Exceptions/InvalidScaleComponentException.cs new file mode 100644 index 0000000..a423407 --- /dev/null +++ b/Source/SeaInk.Application/Exceptions/InvalidScaleComponentException.cs @@ -0,0 +1,16 @@ +using System; +using System.Runtime.Serialization; +using SeaInk.Core.Exceptions; + +namespace SeaInk.Application.Exceptions +{ + [Serializable] + public class InvalidScaleComponentException : SeaInkException + { + public InvalidScaleComponentException(string argName, int value) + : base($"{argName} must be positive. Value {value}") { } + + protected InvalidScaleComponentException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/Exceptions/MissingAssignmentComponentException.cs b/Source/SeaInk.Application/Exceptions/MissingAssignmentComponentException.cs new file mode 100644 index 0000000..f09337b --- /dev/null +++ b/Source/SeaInk.Application/Exceptions/MissingAssignmentComponentException.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.Serialization; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Core.Exceptions; + +namespace SeaInk.Application.Exceptions +{ + [Serializable] + public class MissingAssignmentComponentException : SeaInkException + { + public MissingAssignmentComponentException(AssignmentModel model) + : base($"There is no component in the table that represents an assignment {model}") { } + + protected MissingAssignmentComponentException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/Exceptions/MissingStudentComponentException.cs b/Source/SeaInk.Application/Exceptions/MissingStudentComponentException.cs new file mode 100644 index 0000000..20b21dd --- /dev/null +++ b/Source/SeaInk.Application/Exceptions/MissingStudentComponentException.cs @@ -0,0 +1,16 @@ +using System; +using System.Runtime.Serialization; +using SeaInk.Core.Exceptions; + +namespace SeaInk.Application.Exceptions +{ + [Serializable] + public class MissingStudentComponentException : SeaInkException + { + public MissingStudentComponentException() + : base("Table does not contain a component that represents student") { } + + protected MissingStudentComponentException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/Results/Failure.cs b/Source/SeaInk.Application/Results/Failure.cs deleted file mode 100644 index 358c7ec..0000000 --- a/Source/SeaInk.Application/Results/Failure.cs +++ /dev/null @@ -1,14 +0,0 @@ -using SeaInk.Utility.Extensions; - -namespace SeaInk.Application.Results -{ - public class Failure : IResult - { - public Failure(string message) - { - Message = message.ThrowIfNull(nameof(message)); - } - - public string Message { get; } - } -} \ No newline at end of file diff --git a/Source/SeaInk.Application/Results/IResult.cs b/Source/SeaInk.Application/Results/IResult.cs deleted file mode 100644 index dc776e3..0000000 --- a/Source/SeaInk.Application/Results/IResult.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace SeaInk.Application.Results -{ - public interface IResult { } -} \ No newline at end of file diff --git a/Source/SeaInk.Application/Results/Success.cs b/Source/SeaInk.Application/Results/Success.cs deleted file mode 100644 index af69b11..0000000 --- a/Source/SeaInk.Application/Results/Success.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SeaInk.Application.Results -{ - public class Success : IResult - { - public Success(T value) - { - Value = value; - } - - public T Value { get; } - } -} \ No newline at end of file diff --git a/Source/SeaInk.Application/SeaInk.Application.csproj b/Source/SeaInk.Application/SeaInk.Application.csproj index f735cf3..8c7b845 100644 --- a/Source/SeaInk.Application/SeaInk.Application.csproj +++ b/Source/SeaInk.Application/SeaInk.Application.csproj @@ -2,10 +2,29 @@ net5.0 - enable enable + 5 + True + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + .editorconfig + + + diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IDrawableLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IDrawableLayoutComponent.cs new file mode 100644 index 0000000..332a472 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IDrawableLayoutComponent.cs @@ -0,0 +1,9 @@ +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IDrawableLayoutComponent + { + void Draw(ITableIndex begin, ITableEditor editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IExpandableLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IExpandableLayoutComponent.cs new file mode 100644 index 0000000..5464815 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IExpandableLayoutComponent.cs @@ -0,0 +1,12 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IExpandableLayoutComponent + where TComponent : LayoutComponent + { + Result AddComponent(TComponent component, IScaledTableIndex begin, ITableEditor editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IReducibleLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IReducibleLayoutComponent.cs new file mode 100644 index 0000000..aaed811 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IReducibleLayoutComponent.cs @@ -0,0 +1,12 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IReducibleLayoutComponent + where TComponent : LayoutComponent + { + Result RemoveComponent(TComponent component, IScaledTableIndex begin, ITableEditor editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IRemovableComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IRemovableComponent.cs new file mode 100644 index 0000000..d1b6a03 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IRemovableComponent.cs @@ -0,0 +1,9 @@ +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IRemovableComponent + { + void Remove(ITableIndex begin, ITableEditor editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueGettingLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueGettingLayoutComponent.cs new file mode 100644 index 0000000..7080277 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueGettingLayoutComponent.cs @@ -0,0 +1,9 @@ +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IValueGettingLayoutComponent + { + T GetValue(ITableIndex begin, ITableDataProvider provider); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueRepresentingLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueRepresentingLayoutComponent.cs new file mode 100644 index 0000000..c0b3325 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueRepresentingLayoutComponent.cs @@ -0,0 +1,7 @@ +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IValueRepresentingLayoutComponent + { + T Value { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueSettingLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueSettingLayoutComponent.cs new file mode 100644 index 0000000..33001f7 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandInterfaces/IValueSettingLayoutComponent.cs @@ -0,0 +1,9 @@ +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandInterfaces +{ + public interface IValueSettingLayoutComponent + { + void SetValue(T value, ITableIndex begin, ITableEditor editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/AggregateValuesCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/AggregateValuesCommand.cs new file mode 100644 index 0000000..094add5 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/AggregateValuesCommand.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using FluentResults; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Successes; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class AggregateValuesCommand : ILayoutCommand + { + private readonly List _values = new List(); + private readonly ITableDataProvider _provider; + private readonly List _visitedComponents = new List(); + + public AggregateValuesCommand(ITableDataProvider provider) + { + _provider = provider; + } + + public IReadOnlyCollection Values => _values.AsReadOnly(); + + public Result Execute(LayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + var command = new GetValueCommand(_provider); + Result result = target.ExecuteCommand(new ComponentIgnoringCommand(command, _visitedComponents), begin, editor); + + while (result.IsSuccess) + { + _values.Add(command.Value.ThrowIfNull(nameof(command.Value))); + var success = (SuccessComponent)result.Successes.Single(); + _visitedComponents.Add(success.Component); + + result = target.ExecuteCommand(new ComponentIgnoringCommand(command, _visitedComponents), begin, editor); + } + + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/ComponentIgnoringCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/ComponentIgnoringCommand.cs new file mode 100644 index 0000000..909a03d --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/ComponentIgnoringCommand.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using FluentResults; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Errors; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Successes; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class ComponentIgnoringCommand : ILayoutCommand + { + private readonly ILayoutCommand _command; + private readonly IReadOnlyCollection _ignored; + + public ComponentIgnoringCommand(ILayoutCommand command, IReadOnlyCollection ignored) + { + _command = command; + _ignored = ignored.ToList(); + } + + public Result Execute(LayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + if (_ignored.Contains(target)) + return Result.Fail(new ComponentShouldBeIgnoredError(target)); + + Result result = target.ExecuteCommand(_command, begin, editor); + + return result.IsSuccess ? result.WithSuccess(new SuccessComponent(target)) : result; + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/ComponentRemoveCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/ComponentRemoveCommand.cs new file mode 100644 index 0000000..9b55da8 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/ComponentRemoveCommand.cs @@ -0,0 +1,28 @@ +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Errors; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class ComponentRemoveCommand : GenericLayoutCommand + { + private readonly IRemovableComponent _component; + + public ComponentRemoveCommand(IRemovableComponent component) + { + _component = component.ThrowIfNull(nameof(component)); + } + + protected override Result Execute(IRemovableComponent target, ITableIndex begin, ITableEditor? editor) + { + if (!target.Equals(_component)) + return Result.Fail(new InvalidComponentError(_component, target)); + + target.Remove(begin, editor.ThrowIfNull(nameof(editor))); + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/DrawAllCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/DrawAllCommand.cs new file mode 100644 index 0000000..24af75a --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/DrawAllCommand.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using FluentResults; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Successes; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class DrawAllCommand : ILayoutCommand + { + private readonly List _visitedComponents = new List(); + + public Result Execute(LayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + var command = new DrawComponentCommand(); + Result result = target.ExecuteCommand(new ComponentIgnoringCommand(command, _visitedComponents), begin, editor); + + while (result.IsSuccess) + { + var success = (SuccessComponent)result.Successes.Single(); + _visitedComponents.Add(success.Component); + result = target.ExecuteCommand(new ComponentIgnoringCommand(command, _visitedComponents), begin, editor); + } + + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/DrawComponentCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/DrawComponentCommand.cs new file mode 100644 index 0000000..d74e074 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/DrawComponentCommand.cs @@ -0,0 +1,18 @@ +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class DrawComponentCommand : GenericLayoutCommand + { + protected override Result Execute(IDrawableLayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + var index = new MergeScanningIndex(begin.Copy(), editor.ThrowIfNull(nameof(editor))); + target.Draw(index, editor!); + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/DrawSpecificComponentCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/DrawSpecificComponentCommand.cs new file mode 100644 index 0000000..d8c1eef --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/DrawSpecificComponentCommand.cs @@ -0,0 +1,25 @@ +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.Errors; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class DrawSpecificComponentCommand : DrawComponentCommand + { + private readonly IDrawableLayoutComponent _component; + + public DrawSpecificComponentCommand(IDrawableLayoutComponent component) + { + _component = component.ThrowIfNull(nameof(component)); + } + + protected override Result Execute(IDrawableLayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + return !_component.Equals(target) + ? Result.Fail(new InvalidComponentError(_component, target)) + : base.Execute(target, begin, editor); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/GetValueCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/GetValueCommand.cs new file mode 100644 index 0000000..563c6d3 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/GetValueCommand.cs @@ -0,0 +1,26 @@ +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class GetValueCommand : GenericLayoutCommand> + { + private readonly ITableDataProvider _provider; + + public GetValueCommand(ITableDataProvider provider) + { + _provider = provider.ThrowIfNull(nameof(provider)); + } + + public T? Value { get; private set; } + + protected override Result Execute(IValueGettingLayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + Value = target.GetValue(begin, _provider); + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/SetAssignmentProgressCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/SetAssignmentProgressCommand.cs new file mode 100644 index 0000000..d69e016 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/SetAssignmentProgressCommand.cs @@ -0,0 +1,29 @@ +using FluentResults; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Components; +using SeaInk.Application.TableLayout.Errors; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class SetAssignmentProgressCommand : GenericLayoutCommand + { + private readonly AssignmentProgressModel _value; + + public SetAssignmentProgressCommand(AssignmentProgressModel value) + { + _value = value.ThrowIfNull(nameof(value)); + } + + protected override Result Execute(AssignmentColumnComponent target, ITableIndex begin, ITableEditor? editor) + { + if (!target.Value.Equals(_value.Assignment)) + return Result.Fail(new InvalidRepresentingValue(_value.Assignment, target.Value)); + + target.SetValue(_value.Progress, begin, editor.ThrowIfNull(nameof(editor))); + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Commands/SetValueCommand.cs b/Source/SeaInk.Application/TableLayout/Commands/SetValueCommand.cs new file mode 100644 index 0000000..7c0e1ca --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Commands/SetValueCommand.cs @@ -0,0 +1,24 @@ +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Commands +{ + public class SetValueCommand : GenericLayoutCommand> + { + private readonly T _value; + + public SetValueCommand(T value) + { + _value = value.ThrowIfNull(nameof(value)); + } + + protected override Result Execute(IValueSettingLayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + target.SetValue(_value, begin, editor.ThrowIfNull(nameof(editor))); + return Result.Ok(); + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandsBase/GenericLayoutCommand.cs b/Source/SeaInk.Application/TableLayout/CommandsBase/GenericLayoutCommand.cs new file mode 100644 index 0000000..6aecf5d --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandsBase/GenericLayoutCommand.cs @@ -0,0 +1,21 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Errors; +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandsBase +{ + public abstract class GenericLayoutCommand : ILayoutCommand + { + public Result Execute(LayoutComponent target, ITableIndex begin, ITableEditor? editor) + { + return target switch + { + T typedComponent => Execute(typedComponent, begin, editor), + not T => Result.Fail(new InvalidCommandHandlerError(target)), + }; + } + + protected abstract Result Execute(T target, ITableIndex begin, ITableEditor? editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/CommandsBase/ILayoutCommand.cs b/Source/SeaInk.Application/TableLayout/CommandsBase/ILayoutCommand.cs new file mode 100644 index 0000000..44c75c2 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/CommandsBase/ILayoutCommand.cs @@ -0,0 +1,11 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; + +namespace SeaInk.Application.TableLayout.CommandsBase +{ + public interface ILayoutCommand + { + Result Execute(LayoutComponent target, ITableIndex begin, ITableEditor? editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Components/AssignmentColumnComponent.cs b/Source/SeaInk.Application/TableLayout/Components/AssignmentColumnComponent.cs new file mode 100644 index 0000000..105f1e5 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Components/AssignmentColumnComponent.cs @@ -0,0 +1,22 @@ +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Core.Models; + +namespace SeaInk.Application.TableLayout.Components +{ + public abstract class AssignmentColumnComponent : LayoutComponent, + IDrawableLayoutComponent, + IRemovableComponent, + IValueRepresentingLayoutComponent, + IValueGettingLayoutComponent, + IValueSettingLayoutComponent + { + public abstract AssignmentModel Value { get; } + public abstract void Remove(ITableIndex begin, ITableEditor editor); + public abstract void Draw(ITableIndex begin, ITableEditor editor); + public abstract AssignmentProgress GetValue(ITableIndex begin, ITableDataProvider provider); + public abstract void SetValue(AssignmentProgress value, ITableIndex begin, ITableEditor editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Components/AssignmentsComponent.cs b/Source/SeaInk.Application/TableLayout/Components/AssignmentsComponent.cs new file mode 100644 index 0000000..4331534 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Components/AssignmentsComponent.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.Commands; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.Components +{ + public class AssignmentsComponent : LayoutComponent, + IExpandableLayoutComponent, + IReducibleLayoutComponent + { + private readonly HorizontalStackLayoutComponent _stack; + + public AssignmentsComponent(IReadOnlyCollection components) + { + _stack = new HorizontalStackLayoutComponent(components); + } + + public override Frame Frame => _stack.Frame; + + public Result AddComponent(AssignmentColumnComponent component, IScaledTableIndex begin, ITableEditor editor) + { + return _stack.AddComponent(component, begin, editor); + } + + public Result RemoveComponent(AssignmentColumnComponent component, IScaledTableIndex begin, ITableEditor editor) + { + Result result = _stack.ExecuteCommand(new ComponentRemoveCommand(component), begin, editor); + return result.IsSuccess ? _stack.RemoveComponent(component, begin, editor) : result; + } + + public override bool Equals(LayoutComponent? other) + => other is AssignmentsComponent assignmentsComponent && assignmentsComponent._stack.Equals(_stack); + + public override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public override int GetHashCode() + => _stack.GetHashCode(); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Components/HeaderLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/Components/HeaderLayoutComponent.cs new file mode 100644 index 0000000..fcc9c8b --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Components/HeaderLayoutComponent.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using FluentResults; +using SeaInk.Application.Exceptions; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.Commands; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Components +{ + public class HeaderLayoutComponent : LayoutComponent, + IValueGettingLayoutComponent, + IValueSettingLayoutComponent + { + private readonly HorizontalStackLayoutComponent _stack; + + public HeaderLayoutComponent(IReadOnlyCollection components) + { + _stack = new HorizontalStackLayoutComponent(components); + } + + public override Frame Frame => _stack.Frame; + + public override bool Equals(LayoutComponent? other) + => _stack.Equals(other); + + public override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public override Result ExecuteCommand(ILayoutCommand command, ITableIndex begin, ITableEditor? editor) + { + Result baseResult = base.ExecuteCommand(command, begin, editor); + return baseResult.IsSuccess ? baseResult : _stack.ExecuteCommand(command, begin, editor); + } + + public TableRowModel GetValue(ITableIndex begin, ITableDataProvider provider) + { + var getStudentCommand = new GetValueCommand(provider); + + if (!_stack.ExecuteCommand(getStudentCommand, begin.Copy(), null).IsSuccess) + throw new MissingStudentComponentException(); + + var aggregateAssignmentProgressesCommand = new AggregateValuesCommand(provider); + _stack.ExecuteCommand(aggregateAssignmentProgressesCommand, begin.Copy(), null); + + return new TableRowModel( + getStudentCommand.Value.ThrowIfNull(nameof(StudentModel)), + aggregateAssignmentProgressesCommand.Values); + } + + public void SetValue(TableRowModel value, ITableIndex begin, ITableEditor editor) + { + var studentSetCommand = new SetValueCommand(value.Student); + + if (!_stack.ExecuteCommand(studentSetCommand, begin.Copy(), editor).IsSuccess) + throw new MissingStudentComponentException(); + + foreach (AssignmentProgressModel model in value.AssignmentProgresses) + { + var assignmentSetCommand = new SetAssignmentProgressCommand(model); + + if (!_stack.ExecuteCommand(assignmentSetCommand, begin.Copy(), editor).IsSuccess) + throw new MissingAssignmentComponentException(model.Assignment); + } + } + + public override int GetHashCode() + => _stack.GetHashCode(); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Components/LabelComponent.cs b/Source/SeaInk.Application/TableLayout/Components/LabelComponent.cs new file mode 100644 index 0000000..2bb24e3 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Components/LabelComponent.cs @@ -0,0 +1,31 @@ +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.Components +{ + public class LabelComponent : LayoutComponent, IDrawableLayoutComponent + { + private readonly string _value; + + public LabelComponent(string value) + { + _value = value; + } + + public override Frame Frame => new Frame(1, 1); + + public void Draw(ITableIndex begin, ITableEditor editor) + => editor.EnqueueWrite(begin, new[] { new[] { _value } }); + + public override bool Equals(LayoutComponent? other) + => other is LabelComponent labelComponent && labelComponent._value.Equals(_value); + + public override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public override int GetHashCode() + => _value.GetHashCode(); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Components/PlainAssignmentColumnComponent.cs b/Source/SeaInk.Application/TableLayout/Components/PlainAssignmentColumnComponent.cs new file mode 100644 index 0000000..890d596 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Components/PlainAssignmentColumnComponent.cs @@ -0,0 +1,43 @@ +using System.Globalization; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Core.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Components +{ + public class PlainAssignmentColumnComponent : AssignmentColumnComponent + { + public PlainAssignmentColumnComponent(AssignmentModel value) + { + Value = value.ThrowIfNull(nameof(value)); + } + + public override Frame Frame => new Frame(1, 1); + + public override AssignmentModel Value { get; } + + public override void Remove(ITableIndex begin, ITableEditor editor) + => editor.EnqueueDeleteColumn(begin.Column); + + public override void Draw(ITableIndex begin, ITableEditor editor) + => editor.EnqueueWrite(begin, new[] { new[] { Value.Title } }); + + public override AssignmentProgress GetValue(ITableIndex begin, ITableDataProvider provider) + => new AssignmentProgress(double.Parse(provider[begin])); + + public override void SetValue(AssignmentProgress value, ITableIndex begin, ITableEditor editor) + => editor.EnqueueWrite(begin, new[] { new[] { value.Points.ToString(CultureInfo.InvariantCulture) } }); + + public override bool Equals(LayoutComponent? other) + => other is PlainAssignmentColumnComponent plainAssignmentColumnComponent && + plainAssignmentColumnComponent.Value.Equals(Value); + + public override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public override int GetHashCode() + => Value.GetHashCode(); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Components/StudentsColumnComponent.cs b/Source/SeaInk.Application/TableLayout/Components/StudentsColumnComponent.cs new file mode 100644 index 0000000..65669a0 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Components/StudentsColumnComponent.cs @@ -0,0 +1,29 @@ +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.Components +{ + public class StudentsColumnComponent : LayoutComponent, + IValueGettingLayoutComponent, + IValueSettingLayoutComponent + { + public override Frame Frame => new Frame(1, 1); + + public StudentModel GetValue(ITableIndex begin, ITableDataProvider provider) + => new StudentModel(provider[begin]); + + public void SetValue(StudentModel value, ITableIndex begin, ITableEditor editor) + => editor.EnqueueWrite(begin, new[] { new[] { value.Name } }); + + public override bool Equals(LayoutComponent? other) + => other is StudentsColumnComponent; + + public override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public override int GetHashCode() + => 0; + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/ComponentsBase/CompositeLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/ComponentsBase/CompositeLayoutComponent.cs new file mode 100644 index 0000000..d245e65 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/ComponentsBase/CompositeLayoutComponent.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Linq; +using FluentResults; +using SeaInk.Application.TableLayout.CommandInterfaces; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Errors; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.ComponentsBase +{ + public abstract class CompositeLayoutComponent : LayoutComponent, + IExpandableLayoutComponent, + IReducibleLayoutComponent + where TComponent : LayoutComponent + { + private readonly List _components; + + protected CompositeLayoutComponent(IReadOnlyCollection components) + { + _components = components.ThrowIfNull(nameof(components)).ToList(); + } + + public IReadOnlyCollection Components => _components.AsReadOnly(); + + public Result AddComponent(TComponent component, IScaledTableIndex begin, ITableEditor editor) + { + if (_components.Contains(component)) + return Result.Fail(new NotContainedComponentError(component)); + + _components.Add(component); + return Result.Ok(); + } + + public Result RemoveComponent(TComponent component, IScaledTableIndex begin, ITableEditor editor) + => _components.Remove(component) ? Result.Fail(new NotContainedComponentError(component)) : Result.Ok(); + + public override Result ExecuteCommand(ILayoutCommand command, ITableIndex begin, ITableEditor? editor) + { + ITableIndex compositionIndex = begin.Copy(); + + foreach (TComponent component in Components) + { + Scale scale = GetScale(component); + var index = new ScaledTableIndex(scale, compositionIndex.Copy()); + + Result result = component.ExecuteCommand(command, index, editor); + if (result.IsSuccess) + return result; + + MoveIndexToNextComponent(compositionIndex, component); + } + + return base.ExecuteCommand(command, begin, editor); + } + + public override bool Equals(LayoutComponent? other) + { + if (other is not CompositeLayoutComponent otherComposite || + otherComposite.Components.Count != Components.Count) + return false; + + for (int i = 0; i < Components.Count; i++) + { + if (!otherComposite._components[i].Equals(_components[i])) + return false; + } + + return true; + } + + public sealed override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public sealed override int GetHashCode() + => _components.GetHashCode(); + + protected abstract void MoveIndexToNextComponent(ITableIndex index, TComponent component); + protected abstract Scale GetScale(TComponent component); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/ComponentsBase/HorizontalStackLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/ComponentsBase/HorizontalStackLayoutComponent.cs new file mode 100644 index 0000000..5f12931 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/ComponentsBase/HorizontalStackLayoutComponent.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Linq; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Application.Tools; + +namespace SeaInk.Application.TableLayout.ComponentsBase +{ + public class HorizontalStackLayoutComponent : CompositeLayoutComponent + where TComponent : LayoutComponent + { + public HorizontalStackLayoutComponent(IReadOnlyCollection components) + : base(components) { } + + public override Frame Frame => new Frame( + Components.Sum(c => c.Frame.Width), + LcmCounter.Count(Components.Select(c => c.Frame.Height).ToArray())); + + protected override void MoveIndexToNextComponent(ITableIndex index, TComponent component) + => index.MoveHorizontally(component.Frame.Width); + + protected override Scale GetScale(TComponent component) + => new Scale(1, Frame.Height / component.Frame.Height); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/ComponentsBase/LayoutComponent.cs b/Source/SeaInk.Application/TableLayout/ComponentsBase/LayoutComponent.cs new file mode 100644 index 0000000..6b24ba0 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/ComponentsBase/LayoutComponent.cs @@ -0,0 +1,19 @@ +using System; +using FluentResults; +using SeaInk.Application.TableLayout.CommandsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.ComponentsBase +{ + public abstract class LayoutComponent : IEquatable + { + public abstract Frame Frame { get; } + public abstract bool Equals(LayoutComponent? other); + public abstract override bool Equals(object? obj); + public abstract override int GetHashCode(); + + public virtual Result ExecuteCommand(ILayoutCommand command, ITableIndex begin, ITableEditor? editor) + => command.Execute(this, begin, editor); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/ComponentsBase/VerticalStackLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/ComponentsBase/VerticalStackLayoutComponent.cs new file mode 100644 index 0000000..058323d --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/ComponentsBase/VerticalStackLayoutComponent.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Linq; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Application.Tools; + +namespace SeaInk.Application.TableLayout.ComponentsBase +{ + public class VerticalStackLayoutComponent : CompositeLayoutComponent + where TComponent : LayoutComponent + { + public VerticalStackLayoutComponent(IReadOnlyCollection components) + : base(components) { } + + public override Frame Frame => new Frame( + LcmCounter.Count(Components.Select(c => c.Frame.Width).ToArray()), + Components.Sum(c => c.Frame.Height)); + + protected override void MoveIndexToNextComponent(ITableIndex index, TComponent component) + => index.MoveVertically(component.Frame.Height); + + protected override Scale GetScale(TComponent component) + => new Scale(Frame.Width / component.Frame.Width, 1); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Errors/CommandNotFinishedExecutionError.cs b/Source/SeaInk.Application/TableLayout/Errors/CommandNotFinishedExecutionError.cs new file mode 100644 index 0000000..2a892e9 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Errors/CommandNotFinishedExecutionError.cs @@ -0,0 +1,10 @@ +using FluentResults; + +namespace SeaInk.Application.TableLayout.Errors +{ + public class CommandNotFinishedExecutionError : Error + { + public CommandNotFinishedExecutionError() + : base("Command did not finish it's execution") { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Errors/ComponentShouldBeIgnoredError.cs b/Source/SeaInk.Application/TableLayout/Errors/ComponentShouldBeIgnoredError.cs new file mode 100644 index 0000000..ac8b80a --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Errors/ComponentShouldBeIgnoredError.cs @@ -0,0 +1,11 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; + +namespace SeaInk.Application.TableLayout.Errors +{ + public class ComponentShouldBeIgnoredError : Error + { + public ComponentShouldBeIgnoredError(LayoutComponent component) + : base($"Component {component} should be ignored") { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Errors/InvalidCommandHandlerError.cs b/Source/SeaInk.Application/TableLayout/Errors/InvalidCommandHandlerError.cs new file mode 100644 index 0000000..30f22e6 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Errors/InvalidCommandHandlerError.cs @@ -0,0 +1,11 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; + +namespace SeaInk.Application.TableLayout.Errors +{ + public class InvalidCommandHandlerError : Error + { + public InvalidCommandHandlerError(LayoutComponent component) + : base($"Passed component {component} is not of type {typeof(T)}") { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Errors/InvalidComponentError.cs b/Source/SeaInk.Application/TableLayout/Errors/InvalidComponentError.cs new file mode 100644 index 0000000..ac64bd4 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Errors/InvalidComponentError.cs @@ -0,0 +1,10 @@ +using FluentResults; + +namespace SeaInk.Application.TableLayout.Errors +{ + public class InvalidComponentError : Error + { + public InvalidComponentError(T expected, T received) + : base($"Received component {received} is not equal to expected component {expected}") { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Errors/InvalidRepresentingValue.cs b/Source/SeaInk.Application/TableLayout/Errors/InvalidRepresentingValue.cs new file mode 100644 index 0000000..52afbf1 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Errors/InvalidRepresentingValue.cs @@ -0,0 +1,10 @@ +using FluentResults; + +namespace SeaInk.Application.TableLayout.Errors +{ + public class InvalidRepresentingValue : Error + { + public InvalidRepresentingValue(T expected, T received) + : base($"Received represented value {received} not equal to expected value {expected}") { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Errors/NotContainedComponentError.cs b/Source/SeaInk.Application/TableLayout/Errors/NotContainedComponentError.cs new file mode 100644 index 0000000..2b756d8 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Errors/NotContainedComponentError.cs @@ -0,0 +1,11 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; + +namespace SeaInk.Application.TableLayout.Errors +{ + public class NotContainedComponentError : Error + { + public NotContainedComponentError(LayoutComponent component) + : base($"Component {component} is not contained in requested container") { } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/ITableDataProvider.cs b/Source/SeaInk.Application/TableLayout/ITableDataProvider.cs new file mode 100644 index 0000000..3e14786 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/ITableDataProvider.cs @@ -0,0 +1,11 @@ +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout +{ + public interface ITableDataProvider + { + Frame Frame { get; } + string this[ITableIndex index] { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/ITableEditor.cs b/Source/SeaInk.Application/TableLayout/ITableEditor.cs new file mode 100644 index 0000000..74117f6 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/ITableEditor.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout +{ + public interface ITableEditor + { + void EnqueueWrite(ITableIndex index, IReadOnlyCollection> data); + void EnqueueMerge(ITableIndex index, Frame frame); + + void EnqueueInsertColumn(int column); + void EnqueueInsertRow(int row); + + void EnqueueDeleteColumn(int column); + void EnqueueDeleteRow(int row); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Indices/IScaledTableIndex.cs b/Source/SeaInk.Application/TableLayout/Indices/IScaledTableIndex.cs new file mode 100644 index 0000000..deec46b --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Indices/IScaledTableIndex.cs @@ -0,0 +1,9 @@ +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.Indices +{ + public interface IScaledTableIndex : ITableIndex + { + Scale Scale { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Indices/ITableIndex.cs b/Source/SeaInk.Application/TableLayout/Indices/ITableIndex.cs new file mode 100644 index 0000000..293690f --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Indices/ITableIndex.cs @@ -0,0 +1,13 @@ +namespace SeaInk.Application.TableLayout.Indices +{ + public interface ITableIndex + { + int Column { get; } + int Row { get; } + + void MoveHorizontally(int value = 1); + void MoveVertically(int value = 1); + + ITableIndex Copy(); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Indices/LockedTableIndex.cs b/Source/SeaInk.Application/TableLayout/Indices/LockedTableIndex.cs new file mode 100644 index 0000000..b7ee022 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Indices/LockedTableIndex.cs @@ -0,0 +1,56 @@ +using SeaInk.Application.Exceptions; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Indices +{ + public class LockedTableIndex : ITableIndex + { + private readonly ITableIndex _index; + private readonly ITableIndex _begin; + private readonly Frame _frame; + + public LockedTableIndex(ITableIndex index, Frame frame) + { + _index = index.ThrowIfNull(nameof(index)); + _begin = index.Copy(); + _frame = frame.ThrowIfNull(nameof(frame)); + } + + private LockedTableIndex(ITableIndex index, ITableIndex begin, Frame frame) + { + _index = index; + _begin = begin; + _frame = frame; + } + + public int Column => _index.Column; + public int Row => _index.Row; + + public void MoveHorizontally(int value = 1) + { + _index.MoveHorizontally(value); + + int diff = _index.Column - _begin.Column; + + if (diff < 0 || diff >= _frame.Width) + throw new CrossedFrameException(_begin, _index, _frame); + } + + public void MoveVertically(int value = 1) + { + _index.MoveVertically(value); + + int diff = _index.Row - _begin.Row; + + if (diff < 0 || diff >= _frame.Height) + throw new CrossedFrameException(_begin, _index, _frame); + } + + public ITableIndex Copy() + => new LockedTableIndex(_index.Copy(), _begin, _frame); + + public override string ToString() + => _index.ToString() ?? string.Empty; + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Indices/MergeScanningIndex.cs b/Source/SeaInk.Application/TableLayout/Indices/MergeScanningIndex.cs new file mode 100644 index 0000000..62ccaeb --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Indices/MergeScanningIndex.cs @@ -0,0 +1,55 @@ +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.Indices +{ + public class MergeScanningIndex : ITableIndex + { + private readonly ITableIndex _index; + private readonly ITableEditor _editor; + private readonly Scale? _scale; + + public MergeScanningIndex(ITableIndex index, ITableEditor editor) + { + _index = index; + _editor = editor; + + if (index is IScaledTableIndex scaledTableIndex) + _scale = scaledTableIndex.Scale; + } + + public int Column => _index.Column; + public int Row => _index.Row; + + public void MoveHorizontally(int value = 1) + { + if (_scale is null) + { + _index.MoveHorizontally(value); + return; + } + + ITableIndex index = _index.Copy(); + _index.MoveHorizontally(value); + _editor.EnqueueMerge(index, new Frame(_scale.Horizontal, _scale.Vertical)); + } + + public void MoveVertically(int value = 1) + { + if (_scale is null) + { + _index.MoveVertically(value); + return; + } + + ITableIndex index = _index.Copy(); + _index.MoveVertically(value); + _editor.EnqueueMerge(index, new Frame(_scale.Horizontal, _scale.Vertical)); + } + + public ITableIndex Copy() + => new MergeScanningIndex(_index.Copy(), _editor); + + public override string ToString() + => _index.ToString() ?? string.Empty; + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Indices/ScaledTableIndex.cs b/Source/SeaInk.Application/TableLayout/Indices/ScaledTableIndex.cs new file mode 100644 index 0000000..18a72af --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Indices/ScaledTableIndex.cs @@ -0,0 +1,32 @@ +using SeaInk.Application.TableLayout.Models; + +namespace SeaInk.Application.TableLayout.Indices +{ + public class ScaledTableIndex : IScaledTableIndex + { + private readonly ITableIndex _index; + + public ScaledTableIndex(Scale scale, ITableIndex index) + { + Scale = scale; + _index = index; + } + + public Scale Scale { get; } + + public int Column => _index.Column; + public int Row => _index.Row; + + public void MoveHorizontally(int value = 1) + => _index.MoveHorizontally(value * Scale.Horizontal); + + public void MoveVertically(int value = 1) + => _index.MoveVertically(value * Scale.Vertical); + + public ITableIndex Copy() + => new ScaledTableIndex(Scale, _index.Copy()); + + public override string ToString() + => _index.ToString() ?? string.Empty; + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Indices/TableIndex.cs b/Source/SeaInk.Application/TableLayout/Indices/TableIndex.cs new file mode 100644 index 0000000..3839547 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Indices/TableIndex.cs @@ -0,0 +1,26 @@ +namespace SeaInk.Application.TableLayout.Indices +{ + public class TableIndex : ITableIndex + { + public TableIndex(int column, int row) + { + Column = column; + Row = row; + } + + public int Column { get; private set; } + public int Row { get; private set; } + + public void MoveHorizontally(int value = 1) + => Column += value; + + public void MoveVertically(int value = 1) + => Row += value; + + public ITableIndex Copy() + => new TableIndex(Column, Row); + + public override string ToString() + => $"C: {Column}, R: {Row}"; + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/AssignmentModel.cs b/Source/SeaInk.Application/TableLayout/Models/AssignmentModel.cs new file mode 100644 index 0000000..55eee43 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/AssignmentModel.cs @@ -0,0 +1,33 @@ +using System; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Models +{ + public sealed class AssignmentModel : IEquatable + { + public AssignmentModel(string title) + { + Title = title.ThrowIfNull(nameof(title)); + } + + public string Title { get; private init; } + public bool? IsMilestone { get; private init; } + + public DateTime? Deadline { get; private init; } + + public double? MinPoints { get; private init; } + public double? MaxPoints { get; private init; } + + public bool Equals(AssignmentModel? other) + => other is not null && other.Title.Equals(Title); + + public override bool Equals(object? obj) + => Equals(obj as AssignmentModel); + + public override int GetHashCode() + => Title.GetHashCode(); + + public override string ToString() + => Title; + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/AssignmentProgressModel.cs b/Source/SeaInk.Application/TableLayout/Models/AssignmentProgressModel.cs new file mode 100644 index 0000000..ad10829 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/AssignmentProgressModel.cs @@ -0,0 +1,17 @@ +using SeaInk.Core.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Models +{ + public class AssignmentProgressModel + { + public AssignmentProgressModel(AssignmentModel assignment, AssignmentProgress progress) + { + Assignment = assignment.ThrowIfNull(nameof(assignment)); + Progress = progress.ThrowIfNull(nameof(progress)); + } + + public AssignmentModel Assignment { get; } + public AssignmentProgress Progress { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/Frame.cs b/Source/SeaInk.Application/TableLayout/Models/Frame.cs new file mode 100644 index 0000000..3731b1f --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/Frame.cs @@ -0,0 +1,4 @@ +namespace SeaInk.Application.TableLayout.Models +{ + public record Frame(int Width, int Height); +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/Scale.cs b/Source/SeaInk.Application/TableLayout/Models/Scale.cs new file mode 100644 index 0000000..09111e7 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/Scale.cs @@ -0,0 +1,22 @@ +using SeaInk.Application.Exceptions; + +namespace SeaInk.Application.TableLayout.Models +{ + public class Scale + { + public Scale(int horizontal, int vertical) + { + if (horizontal <= 0) + throw new InvalidScaleComponentException(nameof(horizontal), horizontal); + + if (vertical <= 0) + throw new InvalidScaleComponentException(nameof(vertical), vertical); + + Horizontal = horizontal; + Vertical = vertical; + } + + public int Horizontal { get; } + public int Vertical { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/StudentModel.cs b/Source/SeaInk.Application/TableLayout/Models/StudentModel.cs new file mode 100644 index 0000000..90c621e --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/StudentModel.cs @@ -0,0 +1,14 @@ +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Models +{ + public class StudentModel + { + public StudentModel(string name) + { + Name = name.ThrowIfNull(nameof(name)); + } + + public string Name { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/TableModel.cs b/Source/SeaInk.Application/TableLayout/Models/TableModel.cs new file mode 100644 index 0000000..c715529 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/TableModel.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Models +{ + public class TableModel + { + public TableModel(IReadOnlyCollection rows) + { + Rows = rows.ThrowIfNull(nameof(rows)).ToList(); + } + + public IReadOnlyCollection Rows { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Models/TableRowModel.cs b/Source/SeaInk.Application/TableLayout/Models/TableRowModel.cs new file mode 100644 index 0000000..e43ff69 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Models/TableRowModel.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Linq; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout.Models +{ + public class TableRowModel + { + public TableRowModel(StudentModel student, IReadOnlyCollection assignmentProgresses) + { + Student = student.ThrowIfNull(nameof(student)); + AssignmentProgresses = assignmentProgresses.ThrowIfNull(nameof(student)).ToList(); + } + + public StudentModel Student { get; set; } + public ICollection AssignmentProgresses { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/Successes/SuccessComponent.cs b/Source/SeaInk.Application/TableLayout/Successes/SuccessComponent.cs new file mode 100644 index 0000000..0fe5c61 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/Successes/SuccessComponent.cs @@ -0,0 +1,16 @@ +using FluentResults; +using SeaInk.Application.TableLayout.ComponentsBase; + +namespace SeaInk.Application.TableLayout.Successes +{ + public class SuccessComponent : Success + { + public SuccessComponent(LayoutComponent component) + : base($"Command successfully executed on component {component}") + { + Component = component; + } + + public LayoutComponent Component { get; } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/TableLayout/TableLayoutComponent.cs b/Source/SeaInk.Application/TableLayout/TableLayoutComponent.cs new file mode 100644 index 0000000..e3b0fa4 --- /dev/null +++ b/Source/SeaInk.Application/TableLayout/TableLayoutComponent.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using SeaInk.Application.TableLayout.Components; +using SeaInk.Application.TableLayout.ComponentsBase; +using SeaInk.Application.TableLayout.Indices; +using SeaInk.Application.TableLayout.Models; +using SeaInk.Utility.Extensions; + +namespace SeaInk.Application.TableLayout +{ + public class TableLayoutComponent : LayoutComponent + { + private readonly HeaderLayoutComponent _header; + + public TableLayoutComponent(HeaderLayoutComponent header) + { + _header = header.ThrowIfNull(nameof(header)); + } + + public override Frame Frame => _header.Frame; + + public TableModel GetTable(ITableDataProvider provider) + { + int startRow = Frame.Height + 1; + var index = new TableIndex(1, startRow); + var rows = new List(); + + for (int i = startRow; i <= provider.Frame.Height; i++) + { + rows.Add(_header.GetValue(index.Copy(), provider)); + index.MoveVertically(); + } + + return new TableModel(rows); + } + + public void SetTable(TableModel table, ITableEditor editor) + { + int startRow = Frame.Height + 1; + var index = new TableIndex(1, startRow); + + foreach (TableRowModel row in table.Rows) + { + _header.SetValue(row, index.Copy(), editor); + index.MoveVertically(); + } + } + + public override bool Equals(LayoutComponent? other) + => other is TableLayoutComponent tableLayoutComponent && tableLayoutComponent._header.Equals(_header); + + public override bool Equals(object? obj) + => Equals(obj as LayoutComponent); + + public override int GetHashCode() + => _header.GetHashCode(); + } +} \ No newline at end of file diff --git a/Source/SeaInk.Application/Tools/LcmCounter.cs b/Source/SeaInk.Application/Tools/LcmCounter.cs new file mode 100644 index 0000000..a14c700 --- /dev/null +++ b/Source/SeaInk.Application/Tools/LcmCounter.cs @@ -0,0 +1,35 @@ +using System.Linq; + +namespace SeaInk.Application.Tools +{ + internal static class LcmCounter + { + public static int Count(params int[] values) + { + if (!values.Any()) + return 0; + + int ans = values[0]; + + for (int i = 1; i < values.Length; i++) + { + ans = (values[i] * ans) / Gcd(values[i], ans); + } + + return ans; + } + + private static int Gcd(int a, int b) + { + while (true) + { + if (b is 0) + return a; + + int a1 = a; + a = b; + b = a1 % b; + } + } + } +} \ No newline at end of file diff --git a/Source/SeaInk.Core/Exceptions/SeaInkException.cs b/Source/SeaInk.Core/Exceptions/SeaInkException.cs index 5742a31..2e3cbb7 100644 --- a/Source/SeaInk.Core/Exceptions/SeaInkException.cs +++ b/Source/SeaInk.Core/Exceptions/SeaInkException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; namespace SeaInk.Core.Exceptions { @@ -11,5 +12,8 @@ public SeaInkException(string? message) public SeaInkException(string? message, Exception? innerException) : base(message, innerException) { } + + protected SeaInkException(SerializationInfo info, StreamingContext context) + : base(info, context) { } } } \ No newline at end of file diff --git a/Source/SeaInk.Core/Models/AssignmentProgress.cs b/Source/SeaInk.Core/Models/AssignmentProgress.cs index 3bdf742..e0fb696 100644 --- a/Source/SeaInk.Core/Models/AssignmentProgress.cs +++ b/Source/SeaInk.Core/Models/AssignmentProgress.cs @@ -1,6 +1,4 @@ -using System; - namespace SeaInk.Core.Models { - public record AssignmentProgress(DateTime CompletionDate, double Points); + public record AssignmentProgress(double Points); } \ No newline at end of file