From 9cdb198b1e2dfcdd1622c88cb5c4aef27179856c Mon Sep 17 00:00:00 2001 From: Aptivi CEO Date: Tue, 10 Sep 2024 15:55:02 +0300 Subject: [PATCH] add - prt - Added calendar components (pt. 1) --- We've added all calendar components as classes. Now, we need to implement their properties, but we need to make sure that unsupported ones are not parsed. --- Type: add Breaking: False Doc Required: False Backport Required: False Part: 1/2 --- .../Parsers/VCalendarConstants.cs | 13 +- .../Parsers/VCalendarParser.cs | 265 +++++++++++++++++- VisualCard.Calendar/Parts/Calendar.cs | 30 +- VisualCard.Calendar/Parts/CalendarAlarm.cs | 137 +++++++++ VisualCard.Calendar/Parts/CalendarDaylight.cs | 137 +++++++++ VisualCard.Calendar/Parts/CalendarEvent.cs | 7 + VisualCard.Calendar/Parts/CalendarFreeBusy.cs | 137 +++++++++ VisualCard.Calendar/Parts/CalendarJournal.cs | 137 +++++++++ VisualCard.Calendar/Parts/CalendarStandard.cs | 137 +++++++++ VisualCard.Calendar/Parts/CalendarTimeZone.cs | 151 ++++++++++ VisualCard.Calendar/Parts/CalendarTodo.cs | 144 ++++++++++ 11 files changed, 1287 insertions(+), 8 deletions(-) create mode 100644 VisualCard.Calendar/Parts/CalendarAlarm.cs create mode 100644 VisualCard.Calendar/Parts/CalendarDaylight.cs create mode 100644 VisualCard.Calendar/Parts/CalendarFreeBusy.cs create mode 100644 VisualCard.Calendar/Parts/CalendarJournal.cs create mode 100644 VisualCard.Calendar/Parts/CalendarStandard.cs create mode 100644 VisualCard.Calendar/Parts/CalendarTimeZone.cs create mode 100644 VisualCard.Calendar/Parts/CalendarTodo.cs diff --git a/VisualCard.Calendar/Parsers/VCalendarConstants.cs b/VisualCard.Calendar/Parsers/VCalendarConstants.cs index 4bc4833..eaa849e 100644 --- a/VisualCard.Calendar/Parsers/VCalendarConstants.cs +++ b/VisualCard.Calendar/Parsers/VCalendarConstants.cs @@ -22,8 +22,8 @@ namespace VisualCard.Calendar.Parsers internal static class VCalendarConstants { // Mandatory for each vCalendar - internal const string _beginText = _beginSpecifier + ":VCALENDAR"; - internal const string _endText = _endSpecifier + ":VCALENDAR"; + internal const string _beginText = _beginSpecifier + ":" + _objectVCalendarSpecifier; + internal const string _endText = _endSpecifier + ":" + _objectVCalendarSpecifier; internal const string _beginSpecifier = "BEGIN"; internal const string _endSpecifier = "END"; internal const string _versionSpecifier = "VERSION"; @@ -31,6 +31,15 @@ internal static class VCalendarConstants // Object specifiers internal const string _objectVCalendarSpecifier = "VCALENDAR"; internal const string _objectVEventSpecifier = "VEVENT"; + internal const string _objectVTodoSpecifier = "VTODO"; + + // Object specifiers (vCalendar 2.0) + internal const string _objectVJournalSpecifier = "VJOURNAL"; + internal const string _objectVFreeBusySpecifier = "VFREEBUSY"; + internal const string _objectVTimeZoneSpecifier = "VTIMEZONE"; + internal const string _objectVStandardSpecifier = "VSTANDARD"; + internal const string _objectVDaylightSpecifier = "VDAYLIGHT"; + internal const string _objectVAlarmSpecifier = "VALARM"; // Misc vCalendar constants internal const string _spaceBreak = " "; diff --git a/VisualCard.Calendar/Parsers/VCalendarParser.cs b/VisualCard.Calendar/Parsers/VCalendarParser.cs index 06e1a29..1026683 100644 --- a/VisualCard.Calendar/Parsers/VCalendarParser.cs +++ b/VisualCard.Calendar/Parsers/VCalendarParser.cs @@ -36,7 +36,7 @@ namespace VisualCard.Calendar.Parsers /// /// The base vCalendar parser /// - [DebuggerDisplay("vCalendar contact, version {CalendarVersion.ToString()}, {CalendarContent.Length} lines")] + [DebuggerDisplay("vCalendar parser, version {CalendarVersion.ToString()}, {CalendarContent.Length} lines")] internal class VCalendarParser { private readonly Version calendarVersion = new(); @@ -54,7 +54,7 @@ internal class VCalendarParser calendarVersion; /// - /// Parses a vCalendar contact + /// Parses a vCalendar representation /// /// A strongly-typed instance holding information about the calendar public Parts.Calendar Parse() @@ -299,10 +299,61 @@ internal void ValidateCalendar(Parts.Calendar calendar) calendar.CalendarVersion.Major == 2 ? [VCalendarConstants._uidSpecifier, VCalendarConstants._dateStampSpecifier] : [VCalendarConstants._uidSpecifier]; + string[] expectedTodoFields = expectedEventFields; + string[] expectedAlarmFields = [VCalendarConstants._actionSpecifier, VCalendarConstants._triggerSpecifier]; foreach (var eventInfo in calendar.events) { if (!ValidateComponent(ref expectedEventFields, out string[] actualEventFields, eventInfo)) throw new InvalidDataException($"The following keys [{string.Join(", ", expectedEventFields)}] are required in the event representation. Got [{string.Join(", ", actualEventFields)}]."); + foreach (var alarmInfo in eventInfo.Alarms) + { + if (!ValidateComponent(ref expectedAlarmFields, out string[] actualAlarmFields, alarmInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedAlarmFields)}] are required in the alarm representation. Got [{string.Join(", ", actualAlarmFields)}]."); + } + } + foreach (var todoInfo in calendar.Todos) + { + if (!ValidateComponent(ref expectedTodoFields, out string[] actualTodoFields, todoInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedTodoFields)}] are required in the todo representation. Got [{string.Join(", ", actualTodoFields)}]."); + foreach (var alarmInfo in todoInfo.Alarms) + { + if (!ValidateComponent(ref expectedAlarmFields, out string[] actualAlarmFields, alarmInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedAlarmFields)}] are required in the alarm representation. Got [{string.Join(", ", actualAlarmFields)}]."); + } + } + + // Continue if we have a calendar with version 2.0 + if (calendar.CalendarVersion.Major < 2) + return; + string[] expectedJournalFields = expectedEventFields; + string[] expectedFreeBusyFields = expectedEventFields; + string[] expectedTimeZoneFields = [VCalendarConstants._tzidSpecifier]; + string[] expectedStandardFields = [VCalendarConstants._dateStartSpecifier, VCalendarConstants._tzOffsetFromSpecifier, VCalendarConstants._tzOffsetToSpecifier]; + string[] expectedDaylightFields = expectedStandardFields; + foreach (var journalInfo in calendar.Journals) + { + if (!ValidateComponent(ref expectedJournalFields, out string[] actualJournalFields, journalInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedJournalFields)}] are required in the journal representation. Got [{string.Join(", ", actualJournalFields)}]."); + } + foreach (var freebusyInfo in calendar.FreeBusyList) + { + if (!ValidateComponent(ref expectedFreeBusyFields, out string[] actualFreeBusyFields, freebusyInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedFreeBusyFields)}] are required in the freebusy representation. Got [{string.Join(", ", actualFreeBusyFields)}]."); + } + foreach (var timezoneInfo in calendar.TimeZones) + { + if (!ValidateComponent(ref expectedTimeZoneFields, out string[] actualTimeZoneFields, timezoneInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedTimeZoneFields)}] are required in the timezone representation. Got [{string.Join(", ", actualTimeZoneFields)}]."); + foreach (var standardInfo in timezoneInfo.StandardTimeList) + { + if (!ValidateComponent(ref expectedStandardFields, out string[] actualStandardFields, standardInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedStandardFields)}] are required in the standard representation. Got [{string.Join(", ", actualStandardFields)}]."); + } + foreach (var daylightInfo in timezoneInfo.DaylightTimeList) + { + if (!ValidateComponent(ref expectedDaylightFields, out string[] actualDaylightFields, daylightInfo)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedDaylightFields)}] are required in the daylight representation. Got [{string.Join(", ", actualDaylightFields)}]."); + } } } @@ -357,6 +408,13 @@ private Parts.Calendar GetCalendarInheritedInstance(string type) return type switch { VCalendarConstants._objectVEventSpecifier => new CalendarEvent(CalendarVersion), + VCalendarConstants._objectVTodoSpecifier => new CalendarTodo(CalendarVersion), + VCalendarConstants._objectVJournalSpecifier => new CalendarJournal(CalendarVersion), + VCalendarConstants._objectVFreeBusySpecifier => new CalendarFreeBusy(CalendarVersion), + VCalendarConstants._objectVTimeZoneSpecifier => new CalendarTimeZone(CalendarVersion), + VCalendarConstants._objectVStandardSpecifier => new CalendarStandard(CalendarVersion), + VCalendarConstants._objectVDaylightSpecifier => new CalendarDaylight(CalendarVersion), + VCalendarConstants._objectVAlarmSpecifier => new CalendarAlarm(CalendarVersion), _ => throw new ArgumentException($"Invalid type {type}"), }; } @@ -367,23 +425,220 @@ private void SaveLastSubPart(Parts.Calendar? subpart, ref Parts.Calendar part) return; switch (part.GetType().Name) { - case nameof(Calendar): + case nameof(Parts.Calendar): switch (subpart.GetType().Name) { - case nameof(Calendar): + case nameof(Parts.Calendar): throw new ArgumentException("Can't nest calendar inside calendar"); case nameof(CalendarEvent): part.events.Add((CalendarEvent)subpart); break; + case nameof(CalendarTodo): + part.todos.Add((CalendarTodo)subpart); + break; + case nameof(CalendarJournal): + part.journals.Add((CalendarJournal)subpart); + break; + case nameof(CalendarFreeBusy): + part.freeBusyList.Add((CalendarFreeBusy)subpart); + break; + case nameof(CalendarTimeZone): + part.timeZones.Add((CalendarTimeZone)subpart); + break; + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside calendar"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside calendar"); + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm inside calendar"); } break; case nameof(CalendarEvent): switch (subpart.GetType().Name) { - case nameof(Calendar): + case nameof(Parts.Calendar): throw new ArgumentException("Can't nest calendar inside event"); case nameof(CalendarEvent): throw new ArgumentException("Can't nest event inside event"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside event"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside event"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside event"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside event"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside event"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside event"); + case nameof(CalendarAlarm): + ((CalendarEvent)part).alarms.Add((CalendarAlarm)subpart); + break; + } + break; + case nameof(CalendarTodo): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside to-do"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside to-do"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside to-do"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside to-do"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside to-do"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside to-do"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside to-do"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside to-do"); + case nameof(CalendarAlarm): + ((CalendarTodo)part).alarms.Add((CalendarAlarm)subpart); + break; + } + break; + case nameof(CalendarJournal): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside journal"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside journal"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside journal"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside journal"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside journal"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside journal"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside journal"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside journal"); + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm info inside journal"); + } + break; + case nameof(CalendarFreeBusy): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside free/busy info"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside free/busy info"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside free/busy info"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside free/busy info"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside free/busy info"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside free/busy info"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside free/busy info"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside free/busy info"); + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm info inside free/busy info"); + } + break; + case nameof(CalendarTimeZone): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside time zone"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside time zone"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside time zone"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside time zone"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside time zone"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside time zone"); + case nameof(CalendarStandard): + ((CalendarTimeZone)part).standards.Add((CalendarStandard)subpart); + break; + case nameof(CalendarDaylight): + ((CalendarTimeZone)part).daylights.Add((CalendarDaylight)subpart); + break; + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm info inside time zone"); + } + break; + case nameof(CalendarStandard): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside standard time info"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside standard time info"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside standard time info"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside standard time info"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside standard time info"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside standard time info"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside standard time info"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside standard time info"); + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm info inside standard time info"); + } + break; + case nameof(CalendarDaylight): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside daylight time info"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside daylight time info"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside daylight time info"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside daylight time info"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside daylight time info"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside daylight time info"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside daylight time info"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside daylight time info"); + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm info inside daylight time info"); + } + break; + case nameof(CalendarAlarm): + switch (subpart.GetType().Name) + { + case nameof(Parts.Calendar): + throw new ArgumentException("Can't nest calendar inside alarm info"); + case nameof(CalendarEvent): + throw new ArgumentException("Can't nest event inside alarm info"); + case nameof(CalendarTodo): + throw new ArgumentException("Can't nest to-do inside alarm info"); + case nameof(CalendarJournal): + throw new ArgumentException("Can't nest journal inside alarm info"); + case nameof(CalendarFreeBusy): + throw new ArgumentException("Can't nest free/busy inside alarm info"); + case nameof(CalendarTimeZone): + throw new ArgumentException("Can't nest time zone info inside alarm info"); + case nameof(CalendarStandard): + throw new ArgumentException("Can't nest standard time info inside alarm info"); + case nameof(CalendarDaylight): + throw new ArgumentException("Can't nest daylight time info inside alarm info"); + case nameof(CalendarAlarm): + throw new ArgumentException("Can't nest alarm info inside alarm info"); } break; } diff --git a/VisualCard.Calendar/Parts/Calendar.cs b/VisualCard.Calendar/Parts/Calendar.cs index fabe3df..5bb9b06 100644 --- a/VisualCard.Calendar/Parts/Calendar.cs +++ b/VisualCard.Calendar/Parts/Calendar.cs @@ -39,6 +39,10 @@ namespace VisualCard.Calendar.Parts public class Calendar : IEquatable { internal readonly List events = []; + internal readonly List todos = []; + internal readonly List journals = []; + internal readonly List freeBusyList = []; + internal readonly List timeZones = []; private readonly Version version; private readonly Dictionary> partsArray = []; private readonly Dictionary strings = []; @@ -57,11 +61,35 @@ public class Calendar : IEquatable GetString(CalendarStringsEnum.Uid); /// - /// Unique ID for this card + /// Event list /// public CalendarEvent[] Events => [.. events]; + /// + /// To-do list + /// + public CalendarTodo[] Todos => + [.. todos]; + + /// + /// Journal list + /// + public CalendarJournal[] Journals => + [.. journals]; + + /// + /// Free/busy list + /// + public CalendarFreeBusy[] FreeBusyList => + [.. freeBusyList]; + + /// + /// Time zone list + /// + public CalendarTimeZone[] TimeZones => + [.. timeZones]; + /// /// Gets a part array from a specified key /// diff --git a/VisualCard.Calendar/Parts/CalendarAlarm.cs b/VisualCard.Calendar/Parts/CalendarAlarm.cs new file mode 100644 index 0000000..a699720 --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarAlarm.cs @@ -0,0 +1,137 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar alarm version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarAlarm : Calendar, IEquatable + { + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVAlarmSpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarAlarm)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarAlarm other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarAlarm source, CalendarAlarm target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarAlarm a, CalendarAlarm b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarAlarm a, CalendarAlarm b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarAlarm(Version version) : + base(version) + { } + } +} diff --git a/VisualCard.Calendar/Parts/CalendarDaylight.cs b/VisualCard.Calendar/Parts/CalendarDaylight.cs new file mode 100644 index 0000000..1c36926 --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarDaylight.cs @@ -0,0 +1,137 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar daylight time version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarDaylight : Calendar, IEquatable + { + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVDaylightSpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarDaylight)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarDaylight other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarDaylight source, CalendarDaylight target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarDaylight a, CalendarDaylight b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarDaylight a, CalendarDaylight b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarDaylight(Version version) : + base(version) + { } + } +} diff --git a/VisualCard.Calendar/Parts/CalendarEvent.cs b/VisualCard.Calendar/Parts/CalendarEvent.cs index a727aed..cbe0392 100644 --- a/VisualCard.Calendar/Parts/CalendarEvent.cs +++ b/VisualCard.Calendar/Parts/CalendarEvent.cs @@ -32,9 +32,16 @@ namespace VisualCard.Calendar.Parts [DebuggerDisplay("vCalendar event version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] public class CalendarEvent : Calendar, IEquatable { + internal readonly List alarms = []; private readonly Dictionary> partsArray = []; private readonly Dictionary strings = []; + /// + /// Alarm list + /// + public CalendarAlarm[] Alarms => + [.. alarms]; + /// /// Gets a part array from a specified key /// diff --git a/VisualCard.Calendar/Parts/CalendarFreeBusy.cs b/VisualCard.Calendar/Parts/CalendarFreeBusy.cs new file mode 100644 index 0000000..f7eb03b --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarFreeBusy.cs @@ -0,0 +1,137 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar free/busy version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarFreeBusy : Calendar, IEquatable + { + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVFreeBusySpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarFreeBusy)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarFreeBusy other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarFreeBusy source, CalendarFreeBusy target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarFreeBusy a, CalendarFreeBusy b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarFreeBusy a, CalendarFreeBusy b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarFreeBusy(Version version) : + base(version) + { } + } +} diff --git a/VisualCard.Calendar/Parts/CalendarJournal.cs b/VisualCard.Calendar/Parts/CalendarJournal.cs new file mode 100644 index 0000000..b4a649e --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarJournal.cs @@ -0,0 +1,137 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar journal version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarJournal : Calendar, IEquatable + { + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVJournalSpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarJournal)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarJournal other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarJournal source, CalendarJournal target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarJournal a, CalendarJournal b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarJournal a, CalendarJournal b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarJournal(Version version) : + base(version) + { } + } +} diff --git a/VisualCard.Calendar/Parts/CalendarStandard.cs b/VisualCard.Calendar/Parts/CalendarStandard.cs new file mode 100644 index 0000000..ebea41a --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarStandard.cs @@ -0,0 +1,137 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar standard time version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarStandard : Calendar, IEquatable + { + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVStandardSpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarStandard)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarStandard other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarStandard source, CalendarStandard target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarStandard a, CalendarStandard b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarStandard a, CalendarStandard b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarStandard(Version version) : + base(version) + { } + } +} diff --git a/VisualCard.Calendar/Parts/CalendarTimeZone.cs b/VisualCard.Calendar/Parts/CalendarTimeZone.cs new file mode 100644 index 0000000..da951bc --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarTimeZone.cs @@ -0,0 +1,151 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar timezone version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarTimeZone : Calendar, IEquatable + { + internal readonly List standards = []; + internal readonly List daylights = []; + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Standard time list + /// + public CalendarStandard[] StandardTimeList => + [.. standards]; + + /// + /// Daylight time list + /// + public CalendarDaylight[] DaylightTimeList => + [.. daylights]; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVTimeZoneSpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarTimeZone)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarTimeZone other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarTimeZone source, CalendarTimeZone target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarTimeZone a, CalendarTimeZone b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarTimeZone a, CalendarTimeZone b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarTimeZone(Version version) : + base(version) + { } + } +} diff --git a/VisualCard.Calendar/Parts/CalendarTodo.cs b/VisualCard.Calendar/Parts/CalendarTodo.cs new file mode 100644 index 0000000..f2facb5 --- /dev/null +++ b/VisualCard.Calendar/Parts/CalendarTodo.cs @@ -0,0 +1,144 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using VisualCard.Calendar.Parsers; +using VisualCard.Calendar.Parts.Comparers; +using VisualCard.Calendar.Parts.Enums; + +namespace VisualCard.Calendar.Parts +{ + /// + /// A vCalendar card instance + /// + [DebuggerDisplay("vCalendar todo version {CalendarVersion.ToString()}, parts: (A [{partsArray.Count}] | S [{strings.Count}])")] + public class CalendarTodo : Calendar, IEquatable + { + internal readonly List alarms = []; + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; + + /// + /// Alarm list + /// + public CalendarAlarm[] Alarms => + [.. alarms]; + + /// + /// Gets a part array from a specified key + /// + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray() where TPart : BaseCalendarPartInfo + { + // Get the parts enumeration according to the type + var key = VCalendarParserTools.GetPartsArrayEnumFromType(typeof(TPart), CalendarVersion); + + // Now, return the value + return GetPartsArray(key.Item1, CalendarVersion, partsArray); + } + + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public new TPart[] GetPartsArray(CalendarPartsArrayEnum key) where TPart : BaseCalendarPartInfo => + GetPartsArray(key, CalendarVersion, partsArray); + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public new string GetString(CalendarStringsEnum key) => + GetString(key, CalendarVersion, strings); + + /// + /// Saves this parsed card to the string + /// + public new string SaveToString() => + SaveToString(CalendarVersion, partsArray, strings, VCalendarConstants._objectVTodoSpecifier); + + /// + /// Saves the contact to the returned string + /// + public override string ToString() => + SaveToString(); + + /// + public override bool Equals(object obj) => + Equals((CalendarTodo)obj); + + /// + /// Checks to see if both the cards are equal + /// + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarTodo other) => + Equals(this, other); + + /// + /// Checks to see if both the cards are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the card elements are equal. Otherwise, false. + public bool Equals(CalendarTodo source, CalendarTodo target) + { + // We can't perform this operation on null. + if (source is null || target is null) + return false; + + // Check all the properties + return + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1047895655; + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); + return hashCode; + } + + /// + public static bool operator ==(CalendarTodo a, CalendarTodo b) + => a.Equals(b); + + /// + public static bool operator !=(CalendarTodo a, CalendarTodo b) + => !a.Equals(b); + + internal new void AddPartToArray(CalendarPartsArrayEnum key, BaseCalendarPartInfo value) => + AddPartToArray(key, value, CalendarVersion, partsArray); + + internal new void SetString(CalendarStringsEnum key, string value) => + SetString(key, value, strings); + + internal CalendarTodo(Version version) : + base(version) + { } + } +}