diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 000000000..0bb3f2c14 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,114 @@ +[*.cs] + +# Default severity for analyzer diagnostics with category 'Performance' +dotnet_analyzer_diagnostic.category-Performance.severity = warning +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +[*.cs] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.private_or_internal_field_should_be_pascalcase_.severity = suggestion +dotnet_naming_rule.private_or_internal_field_should_be_pascalcase_.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_pascalcase_.style = pascalcase_ + +dotnet_naming_rule.private_or_internal_static_field_should_be_pascalcase_.severity = suggestion +dotnet_naming_rule.private_or_internal_static_field_should_be_pascalcase_.symbols = private_or_internal_static_field +dotnet_naming_rule.private_or_internal_static_field_should_be_pascalcase_.style = pascalcase_ + +dotnet_naming_rule.private_method_should_be_pascalcase_.severity = suggestion +dotnet_naming_rule.private_method_should_be_pascalcase_.symbols = private_method +dotnet_naming_rule.private_method_should_be_pascalcase_.style = pascalcase_ + +# Symbol specifications + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static + +dotnet_naming_symbols.private_method.applicable_kinds = method +dotnet_naming_symbols.private_method.applicable_accessibilities = private +dotnet_naming_symbols.private_method.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascalcase_.required_prefix = +dotnet_naming_style.pascalcase_.required_suffix = _ +dotnet_naming_style.pascalcase_.word_separator = +dotnet_naming_style.pascalcase_.capitalization = pascal_case + +dotnet_naming_style.pascalcase_.required_prefix = +dotnet_naming_style.pascalcase_.required_suffix = _ +dotnet_naming_style.pascalcase_.word_separator = +dotnet_naming_style.pascalcase_.capitalization = pascal_case + +dotnet_naming_style.pascalcase_.required_prefix = +dotnet_naming_style.pascalcase_.required_suffix = _ +dotnet_naming_style.pascalcase_.word_separator = +dotnet_naming_style.pascalcase_.capitalization = pascal_case + +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = warning + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case diff --git a/src/Calendar.png b/src/Calendar.png new file mode 100644 index 000000000..e3fa4acba Binary files /dev/null and b/src/Calendar.png differ diff --git a/src/CalendarComponents.png b/src/CalendarComponents.png new file mode 100644 index 000000000..95a5c544e Binary files /dev/null and b/src/CalendarComponents.png differ diff --git a/src/DataTypes.png b/src/DataTypes.png new file mode 100644 index 000000000..ddaa7c331 Binary files /dev/null and b/src/DataTypes.png differ diff --git a/src/Evaluation.png b/src/Evaluation.png new file mode 100644 index 000000000..b95c4d9ba Binary files /dev/null and b/src/Evaluation.png differ diff --git a/src/Ical.Net.CoreUnitTests/AlarmTest.cs b/src/Ical.Net.CoreUnitTests/AlarmTest.cs index 67df16fda..82ed78de4 100644 --- a/src/Ical.Net.CoreUnitTests/AlarmTest.cs +++ b/src/Ical.Net.CoreUnitTests/AlarmTest.cs @@ -9,9 +9,9 @@ namespace Ical.Net.CoreUnitTests [TestFixture] public class AlarmTest { - private const string _tzid = "US-Eastern"; + const string _tzid = "US-Eastern"; - public void TestAlarm(string calendarString, List dates, CalDateTime start, CalDateTime end) + public static void TestAlarm(string calendarString, List dates, CalDateTime start, CalDateTime end) { var iCal = Calendar.Load(calendarString); ProgramTest.TestCal(iCal); diff --git a/src/Ical.Net.CoreUnitTests/AttendeeTest.cs b/src/Ical.Net.CoreUnitTests/AttendeeTest.cs index 135bca897..2f61d8ef8 100644 --- a/src/Ical.Net.CoreUnitTests/AttendeeTest.cs +++ b/src/Ical.Net.CoreUnitTests/AttendeeTest.cs @@ -18,7 +18,7 @@ public class AttendeeTest End = new CalDateTime(2010, 3, 26) }; - private static readonly IList _attendees = new List + static readonly IList _attendees = new List { new Attendee("MAILTO:james@example.com") { diff --git a/src/Ical.Net.CoreUnitTests/CalDateTimeTests.cs b/src/Ical.Net.CoreUnitTests/CalDateTimeTests.cs index fa5007487..ac75ef97b 100644 --- a/src/Ical.Net.CoreUnitTests/CalDateTimeTests.cs +++ b/src/Ical.Net.CoreUnitTests/CalDateTimeTests.cs @@ -9,9 +9,10 @@ namespace Ical.Net.CoreUnitTests { public class CalDateTimeTests { - private static readonly DateTime _now = DateTime.Now; - private static readonly DateTime _later = _now.AddHours(1); - private static CalendarEvent GetEventWithRecurrenceRules(string tzId) + static readonly DateTime _now = DateTime.Now; + static readonly DateTime _later = _now.AddHours(1); + + static CalendarEvent GetEventWithRecurrenceRules(string tzId) { var dailyForFiveDays = new RecurrencePattern(FrequencyType.Daily, 1) { @@ -67,8 +68,7 @@ public static IEnumerable ToTimeZoneTestCases() } [TestCaseSource(nameof(AsDateTimeOffsetTestCases))] - public DateTimeOffset AsDateTimeOffsetTests(CalDateTime incoming) - => incoming.AsDateTimeOffset; + public DateTimeOffset AsDateTimeOffsetTests(CalDateTime incoming) => incoming.AsDateTimeOffset; public static IEnumerable AsDateTimeOffsetTestCases() { diff --git a/src/Ical.Net.CoreUnitTests/CalendarEventTest.cs b/src/Ical.Net.CoreUnitTests/CalendarEventTest.cs index 350f129e9..8feffaccb 100644 --- a/src/Ical.Net.CoreUnitTests/CalendarEventTest.cs +++ b/src/Ical.Net.CoreUnitTests/CalendarEventTest.cs @@ -12,9 +12,9 @@ namespace Ical.Net.CoreUnitTests [TestFixture] public class CalendarEventTest { - private static readonly DateTime _now = DateTime.UtcNow; - private static readonly DateTime _later = _now.AddHours(1); - private static readonly string _uid = Guid.NewGuid().ToString(); + static readonly DateTime _now = DateTime.UtcNow; + static readonly DateTime _later = _now.AddHours(1); + static readonly string _uid = Guid.NewGuid().ToString(); /// /// Ensures that events can be properly added to a calendar. @@ -214,7 +214,7 @@ public void EventWithExDateShouldNotBeEqualToSameEventWithoutExDate() Assert.AreNotEqual(noException.GetHashCode(), withException.GetHashCode()); } - private static CalendarEvent GetSimpleEvent() => new CalendarEvent + static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_now), DtEnd = new CalDateTime(_later), @@ -238,9 +238,10 @@ public void RrulesAreSignificantTests() Assert.AreNotEqual(simpleEvent.GetHashCode(), testRdate.GetHashCode()); } - private static List GetSimpleRecurrenceList() + static List GetSimpleRecurrenceList() => new List { new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 } }; - private static List GetExceptionDates() + + static List GetExceptionDates() => new List { new PeriodList { new Period(new CalDateTime(_now.AddDays(1).Date)) } }; [Test] diff --git a/src/Ical.Net.CoreUnitTests/CollectionHelpersTests.cs b/src/Ical.Net.CoreUnitTests/CollectionHelpersTests.cs index 363b5e059..00979e1ba 100644 --- a/src/Ical.Net.CoreUnitTests/CollectionHelpersTests.cs +++ b/src/Ical.Net.CoreUnitTests/CollectionHelpersTests.cs @@ -8,13 +8,14 @@ namespace Ical.Net.CoreUnitTests { internal class CollectionHelpersTests { - private static readonly DateTime _now = DateTime.UtcNow; - private static readonly DateTime _later = _now.AddHours(1); - private static readonly string _uid = Guid.NewGuid().ToString(); + static readonly DateTime _now = DateTime.UtcNow; + static readonly DateTime _later = _now.AddHours(1); + static readonly string _uid = Guid.NewGuid().ToString(); - private static List GetSimpleRecurrenceList() + static List GetSimpleRecurrenceList() => new List { new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 } }; - private static List GetExceptionDates() + + static List GetExceptionDates() => new List { new PeriodList { new Period(new CalDateTime(_now.AddDays(1).Date)) } }; [Test] diff --git a/src/Ical.Net.CoreUnitTests/CopyTest.cs b/src/Ical.Net.CoreUnitTests/CopyTest.cs index 04a544591..f49c62a85 100644 --- a/src/Ical.Net.CoreUnitTests/CopyTest.cs +++ b/src/Ical.Net.CoreUnitTests/CopyTest.cs @@ -45,17 +45,17 @@ public static IEnumerable CopyCalendarTest_TestCases() yield return new TestCaseData(IcsFiles.XProperty2).SetName("XProperty2"); } - private static readonly DateTime _now = DateTime.Now; - private static readonly DateTime _later = _now.AddHours(1); + static readonly DateTime _now = DateTime.Now; + static readonly DateTime _later = _now.AddHours(1); - private static CalendarEvent GetSimpleEvent() => new CalendarEvent + static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_now), DtEnd = new CalDateTime(_later), Duration = TimeSpan.FromHours(1), }; - private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); + static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); [Test] public void EventUid_Tests() diff --git a/src/Ical.Net.CoreUnitTests/DataTypeTest.cs b/src/Ical.Net.CoreUnitTests/DataTypeTest.cs index 00a34de41..7a637787e 100644 --- a/src/Ical.Net.CoreUnitTests/DataTypeTest.cs +++ b/src/Ical.Net.CoreUnitTests/DataTypeTest.cs @@ -7,10 +7,7 @@ namespace Ical.Net.CoreUnitTests public class DataTypeTest { [Test, Category("DataType")] - public void OrganizerConstructorMustAcceptNull() - { - Assert.DoesNotThrow(() => { var o = new Organizer(null); }); - } + public void OrganizerConstructorMustAcceptNull() => Assert.DoesNotThrow(() => { var o = new Organizer(null); }); [Test, Category("DataType")] public void AttachmentConstructorMustAcceptNull() diff --git a/src/Ical.Net.CoreUnitTests/DocumentationExamples.cs b/src/Ical.Net.CoreUnitTests/DocumentationExamples.cs index 9dbfe8ff6..75ed783a4 100644 --- a/src/Ical.Net.CoreUnitTests/DocumentationExamples.cs +++ b/src/Ical.Net.CoreUnitTests/DocumentationExamples.cs @@ -12,17 +12,16 @@ public class DocumentationExamples public void Daily_Test() { // The first instance of an event taking place on July 1, 2016 between 07:00 and 08:00. - // We want it to recur through the end of July. var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2016-07-01T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2016-07-01T08:00")), + DtStart = new CalDateTime(new DateTime(2016, 07, 01, 07, 0, 0)), + DtEnd = new CalDateTime(new DateTime(2016, 07, 01, 08, 0, 0)), }; //Recur daily through the end of the day, July 31, 2016 var recurrenceRule = new RecurrencePattern(FrequencyType.Daily, 1) { - Until = DateTime.Parse("2016-07-31T23:59:59") + Until = new DateTime(2016, 07, 31, 23, 59, 59) }; vEvent.RecurrenceRules = new List {recurrenceRule}; @@ -32,8 +31,8 @@ public void Daily_Test() // Count the occurrences between July 20, and Aug 5 -- there should be 12: // July 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - var searchStart = DateTime.Parse("2016-07-20"); - var searchEnd = DateTime.Parse("2016-08-05"); + var searchStart = new DateTime(2016,07,20); + var searchEnd = new DateTime(2016, 08, 05); var occurrences = calendar.GetOccurrences(searchStart, searchEnd); Assert.AreEqual(12, occurrences.Count); } @@ -44,50 +43,52 @@ public void EveryOtherTuesdayUntilTheEndOfTheYear_Test() // An event taking place between 07:00 and 08:00, beginning July 5 (a Tuesday) var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2016-07-05T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2016-07-05T08:00")), + DtStart = new CalDateTime(new DateTime(2016, 07, 05, 07, 00, 0)), + DtEnd = new CalDateTime(new DateTime(2016, 07, 05, 08, 00, 0)), }; // Recurring every other Tuesday until Dec 31 - var rrule = new RecurrencePattern(FrequencyType.Weekly, 2) + var rRule = new RecurrencePattern(FrequencyType.Weekly, 2) { - Until = DateTime.Parse("2016-12-31T11:59:59") + Until = new DateTime(2016, 12, 31, 11, 59, 59), + ByMonth = new List { 10, 12 } //limit the Months, because > Weekly }; - vEvent.RecurrenceRules = new List { rrule }; + vEvent.RecurrenceRules = new List { rRule }; // Count every other Tuesday between July 1 and Dec 31. // The first Tuesday is July 5. There should be 13 in total - var searchStart = DateTime.Parse("2010-01-01"); - var searchEnd = DateTime.Parse("2016-12-31"); + var searchStart = new DateTime(2010, 01, 01); + var searchEnd = new DateTime(2016, 12, 31); var tuesdays = vEvent.GetOccurrences(searchStart, searchEnd); - Assert.AreEqual(13, tuesdays.Count); + Assert.AreEqual(5, tuesdays.Count); } [Test] public void FourthThursdayOfNovember_Tests() { // (The number of US thanksgivings between 2000 and 2016) - // An event taking place between 07:00 and 19:00, beginning July 5 (a Tuesday) + // An event taking place between 07:00 and 19:00 var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2000-11-23T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2000-11-23T19:00")), + DtStart = new CalDateTime(new DateTime(2000, 1, 23, 07, 0, 0)), + DtEnd = new CalDateTime(new DateTime(2000, 1, 23, 19, 0, 0)), + IncludeReferenceDate = false, //don't know the exact date }; - // Recurring every other Tuesday until Dec 31 - var rrule = new RecurrencePattern(FrequencyType.Yearly, 1) + // Recurring every 4. Thursday in November + var rRule = new RecurrencePattern(FrequencyType.Yearly, 1) { Frequency = FrequencyType.Yearly, Interval = 1, - ByMonth = new List { 11 }, - ByDay = new List { new WeekDay { DayOfWeek = DayOfWeek.Thursday, Offset = 4 } }, + ByMonth = new List { 11 }, // < Yearly => only in November + ByWeekDay = new List { new WeekDay { DayOfWeek = DayOfWeek.Thursday, Offset = 4 } }, Until = DateTime.MaxValue }; - vEvent.RecurrenceRules = new List { rrule }; + vEvent.RecurrenceRules = new List { rRule }; - var searchStart = DateTime.Parse("2000-01-01"); - var searchEnd = DateTime.Parse("2017-01-01"); + var searchStart = new DateTime(2000, 01, 01); + var searchEnd = new DateTime(2017, 01, 01); var usThanksgivings = vEvent.GetOccurrences(searchStart, searchEnd); Assert.AreEqual(17, usThanksgivings.Count); @@ -103,15 +104,15 @@ public void DailyExceptSunday_Test() //An event that happens daily through 2016, except for Sundays var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2016-01-01T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2016-12-31T08:00")), + DtStart = new CalDateTime(new DateTime(2016, 01, 01, 07, 0, 0)), + DtEnd = new CalDateTime(new DateTime(2016, 12, 31, 08, 0, 0)), RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily, 1)}, }; //Define the exceptions: Sunday var exceptionRule = new RecurrencePattern(FrequencyType.Weekly, 1) { - ByDay = new List { new WeekDay(DayOfWeek.Sunday) } + ByWeekDay = new List { new WeekDay(DayOfWeek.Sunday) } }; vEvent.ExceptionRules = new List {exceptionRule}; @@ -119,8 +120,8 @@ public void DailyExceptSunday_Test() calendar.Events.Add(vEvent); // We are essentially counting all the days that aren't Sunday in 2016, so there should be 314 - var searchStart = DateTime.Parse("2015-12-31"); - var searchEnd = DateTime.Parse("2017-01-01"); + var searchStart = new DateTime(2015, 12, 31); + var searchEnd = new DateTime(2017, 01, 01); var occurrences = calendar.GetOccurrences(searchStart, searchEnd); Assert.AreEqual(314, occurrences.Count); } diff --git a/src/Ical.Net.CoreUnitTests/EqualityAndHashingTests.cs b/src/Ical.Net.CoreUnitTests/EqualityAndHashingTests.cs index 6a69ad550..c0d6142f2 100644 --- a/src/Ical.Net.CoreUnitTests/EqualityAndHashingTests.cs +++ b/src/Ical.Net.CoreUnitTests/EqualityAndHashingTests.cs @@ -13,9 +13,9 @@ namespace Ical.Net.CoreUnitTests { public class EqualityAndHashingTests { - private const string _someTz = "America/Los_Angeles"; - private static readonly DateTime _nowTime = DateTime.Parse("2016-07-16T16:47:02.9310521-04:00"); - private static readonly DateTime _later = _nowTime.AddHours(1); + const string _someTz = "America/Los_Angeles"; + static readonly DateTime _nowTime = DateTime.Parse("2016-07-16T16:47:02.9310521-04:00"); + static readonly DateTime _later = _nowTime.AddHours(1); [Test, TestCaseSource(nameof(CalDateTime_TestCases))] public void CalDateTime_Tests(CalDateTime incomingDt, CalDateTime expectedDt) @@ -59,19 +59,19 @@ public void Event_Tests(CalendarEvent incoming, CalendarEvent expected) Assert.IsTrue(incoming.Equals(expected)); } - private static RecurrencePattern GetSimpleRecurrencePattern() => new RecurrencePattern(FrequencyType.Daily, 1) + static RecurrencePattern GetSimpleRecurrencePattern() => new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 }; - private static CalendarEvent GetSimpleEvent() => new CalendarEvent + static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = TimeSpan.FromHours(1), }; - private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); + static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); public static IEnumerable Event_TestCases() @@ -438,7 +438,7 @@ public void CalDateTimeTests() Assert.AreNotEqual(asLocal, asUtc); } - private void TestComparison(Func calOp, Func intOp) + static void TestComparison(Func calOp, Func intOp) { int? intSome = 1; int? intGreater = 2; diff --git a/src/Ical.Net.CoreUnitTests/GetOccurrenceTests.cs b/src/Ical.Net.CoreUnitTests/GetOccurrenceTests.cs index ab1b5e715..343c89b64 100644 --- a/src/Ical.Net.CoreUnitTests/GetOccurrenceTests.cs +++ b/src/Ical.Net.CoreUnitTests/GetOccurrenceTests.cs @@ -32,7 +32,7 @@ public void WrongDurationTest() var occurrences = calendar.GetOccurrences(searchStart, searchEnd).OrderBy(o => o.Period.StartTime).ToList(); var firstOccurrence = occurrences.First(); - var firstStartCopy = firstStart.Copy(); + CalDateTime firstStartCopy = firstStart.Copy(); var firstEndCopy = firstEnd.Copy(); Assert.AreEqual(firstStartCopy, firstOccurrence.Period.StartTime); Assert.AreEqual(firstEndCopy, firstOccurrence.Period.EndTime); @@ -59,7 +59,7 @@ public void SkippedOccurrenceOnWeeklyPattern() var pattern = new RecurrencePattern { Frequency = FrequencyType.Weekly, - ByDay = new List { new WeekDay(DayOfWeek.Friday) } + ByWeekDay = new List { new WeekDay(DayOfWeek.Friday) } }; vEvent.RecurrenceRules.Add(pattern); var calendar = new Calendar(); @@ -68,9 +68,7 @@ public void SkippedOccurrenceOnWeeklyPattern() var intervalStart = eventStart; var intervalEnd = intervalStart.AddDays(7 * evaluationsCount); - var occurrences = RecurrenceUtil.GetOccurrences( - recurrable: vEvent, - periodStart: intervalStart, + var occurrences = vEvent.GetOccurrences(periodStart: intervalStart, periodEnd: intervalEnd, includeReferenceDateInResults: false); var occurrenceSet = new HashSet(occurrences.Select(o => o.Period.StartTime)); diff --git a/src/Ical.Net.CoreUnitTests/Ical.Net.CoreUnitTests.csproj b/src/Ical.Net.CoreUnitTests/Ical.Net.CoreUnitTests.csproj index 61f66127a..7c7830249 100644 --- a/src/Ical.Net.CoreUnitTests/Ical.Net.CoreUnitTests.csproj +++ b/src/Ical.Net.CoreUnitTests/Ical.Net.CoreUnitTests.csproj @@ -3,6 +3,7 @@ netcoreapp3.1;net50 true ..\..\IcalNetStrongnameKey.snk + enable @@ -14,9 +15,14 @@ - + + + + PreserveNewest + + \ No newline at end of file diff --git a/src/Ical.Net.CoreUnitTests/IcsFiles.cs b/src/Ical.Net.CoreUnitTests/IcsFiles.cs index 907c14e96..ad39b8960 100644 --- a/src/Ical.Net.CoreUnitTests/IcsFiles.cs +++ b/src/Ical.Net.CoreUnitTests/IcsFiles.cs @@ -5,158 +5,156 @@ namespace Ical.Net.CoreUnitTests { internal class IcsFiles { - private static readonly Assembly _assembly = typeof(IcsFiles).GetTypeInfo().Assembly; + static readonly Assembly _assembly = typeof(IcsFiles).GetTypeInfo().Assembly; internal static string ReadStream(string manifestResource) { - using (var stream = _assembly.GetManifestResourceStream(manifestResource)) - { - return new StreamReader(stream).ReadToEnd(); - } + using var stream = new FileStream(manifestResource, FileMode.Open); + return new StreamReader(stream).ReadToEnd(); } - internal static string Alarm1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM1.ics"); - internal static string Alarm2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM2.ics"); - internal static string Alarm3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM3.ics"); - internal static string Alarm4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM4.ics"); - internal static string Alarm5 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM5.ics"); - internal static string Alarm6 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM6.ics"); - internal static string Alarm7 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Alarm.ALARM7.ics"); - internal static string Attachment3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Attachment3.ics"); - internal static string Attachment4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Attachment4.ics"); - internal static string Attendee1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Attendee1.ics"); - internal static string Attendee2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Attendee2.ics"); - internal static string Bug1741093 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Bug1741093.ics"); - internal static string Bug2033495 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Bug2033495.ics"); - internal static string Bug2148092 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Bug2148092.ics"); - internal static string Bug2912657 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Bug2912657.ics"); - internal static string Bug2916581 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Bug2916581.ics"); - internal static string Bug2938007 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Bug2938007.ics"); - internal static string Bug2959692 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Bug2959692.ics"); - internal static string Bug2966236 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Bug2966236.ics"); - internal static string Bug3007244 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Bug3007244.ics"); - internal static string ByMonth1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.ByMonth1.ics"); - internal static string ByMonth2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.ByMonth2.ics"); - internal static string ByMonthDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.ByMonthDay1.ics"); - internal static string Calendar1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Calendar1.ics"); - internal static string CalendarParameters2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.CalendarParameters2.ics"); - internal static string CaseInsensitive1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.CaseInsensitive1.ics"); - internal static string CaseInsensitive2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.CaseInsensitive2.ics"); - internal static string CaseInsensitive3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.CaseInsensitive3.ics"); - internal static string CaseInsensitive4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.CaseInsensitive4.ics"); - internal static string Categories1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Categories1.ics"); - internal static string Daily1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Daily1.ics"); - internal static string DailyByDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyByDay1.ics"); - internal static string DailyByHourMinute1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyByHourMinute1.ics"); - internal static string DailyCount1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyCount1.ics"); - internal static string DailyCount2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyCount2.ics"); - internal static string DailyInterval1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyInterval1.ics"); - internal static string DailyInterval2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyInterval2.ics"); - internal static string DailyUntil1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.DailyUntil1.ics"); - internal static string DateTime1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.DateTime1.ics"); - internal static string DateTime2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.DateTime2.ics"); - internal static string Duration1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Duration1.ics"); - internal static string Empty1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Empty1.ics"); - internal static string EmptyLines1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.EmptyLines1.ics"); - internal static string EmptyLines2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.EmptyLines2.ics"); - internal static string EmptyLines3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.EmptyLines3.ics"); - internal static string EmptyLines4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.EmptyLines4.ics"); - internal static string Encoding1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Encoding1.ics"); - internal static string Encoding2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Encoding2.ics"); - internal static string Encoding3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Encoding3.ics"); - internal static string Event1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Event1.ics"); - internal static string Event2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Event2.ics"); - internal static string Event3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Event3.ics"); - internal static string Event4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Event4.ics"); - internal static string EventStatus => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.EventStatus.ics"); - internal static string GeographicLocation1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.GeographicLocation1.ics"); - internal static string Google1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Google1.ics"); - internal static string Hourly1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Hourly1.ics"); - internal static string HourlyInterval1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.HourlyInterval1.ics"); - internal static string HourlyInterval2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.HourlyInterval2.ics"); - internal static string HourlyUntil1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.HourlyUntil1.ics"); - internal static string Journal1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Journal.JOURNAL1.ics"); - internal static string Journal2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Journal.JOURNAL2.ics"); - internal static string Language1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Language1.ics"); - internal static string Language2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Language2.ics"); - internal static string Language3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Language3.ics"); - internal static string Language4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Language4.ics"); - internal static string Minutely1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Minutely1.ics"); - internal static string MinutelyByHour1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MinutelyByHour1.ics"); - internal static string MinutelyCount1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MinutelyCount1.ics"); - internal static string MinutelyCount2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MinutelyCount2.ics"); - internal static string MinutelyCount3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MinutelyCount3.ics"); - internal static string MinutelyCount4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MinutelyCount4.ics"); - internal static string MinutelyInterval1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MinutelyInterval1.ics"); - internal static string Monthly1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Monthly1.ics"); - internal static string MonthlyByDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyByDay1.ics"); - internal static string MonthlyByMonthDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyByMonthDay1.ics"); - internal static string MonthlyByMonthDay2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyByMonthDay2.ics"); - internal static string MonthlyBySetPos1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyBySetPos1.ics"); - internal static string MonthlyBySetPos2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyBySetPos2.ics"); - internal static string MonthlyCountByDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyCountByDay1.ics"); - internal static string MonthlyCountByDay2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyCountByDay2.ics"); - internal static string MonthlyCountByDay3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyCountByDay3.ics"); - internal static string MonthlyCountByMonthDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyCountByMonthDay1.ics"); - internal static string MonthlyCountByMonthDay2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyCountByMonthDay2.ics"); - internal static string MonthlyCountByMonthDay3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyCountByMonthDay3.ics"); - internal static string MonthlyInterval1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyInterval1.ics"); - internal static string MonthlyUntilByDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.MonthlyUntilByDay1.ics"); - internal static string Outlook2007LineFolds => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Outlook2007LineFolds.ics"); - internal static string Parameter1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Parameter1.ics"); - internal static string Parameter2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Parameter2.ics"); - internal static string Parse1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Parse1.ics"); - internal static string Parse17 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.PARSE17.ics"); - internal static string ProdId1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.ProdID1.ics"); - internal static string ProdId2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.ProdID2.ics"); - internal static string Property1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Property1.ics"); - internal static string RecurrenceDates1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.RecurrenceDates1.ics"); - internal static string RequestStatus1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.RequestStatus1.ics"); - internal static string Secondly1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Secondly1.ics"); - internal static string TimeZone1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.TimeZone1.ics"); - internal static string TimeZone2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.TimeZone2.ics"); - internal static string TimeZone3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.TimeZone3.ics"); - internal static string Todo1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo1.ics"); - internal static string Todo2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo2.ics"); - internal static string Todo3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo3.ics"); - internal static string Todo4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo4.ics"); - internal static string Todo5 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo5.ics"); - internal static string Todo6 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo6.ics"); - internal static string Todo7 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo7.ics"); - internal static string Todo8 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo8.ics"); - internal static string Todo9 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Todo.Todo9.ics"); - internal static string Transparency1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Transparency1.ics"); - internal static string Transparency2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Transparency2.ics"); - internal static string Trigger1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.Trigger1.ics"); - internal static string UsHolidays => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.USHolidays.ics"); - internal static string WeeklyCount1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyCount1.ics"); - internal static string WeeklyCountWkst1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyCountWkst1.ics"); - internal static string WeeklyCountWkst2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyCountWkst2.ics"); - internal static string WeeklyCountWkst3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyCountWkst3.ics"); - internal static string WeeklyCountWkst4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyCountWkst4.ics"); - internal static string WeeklyInterval1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyInterval1.ics"); - internal static string WeeklyUntil1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyUntil1.ics"); - internal static string WeeklyUntilWkst1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyUntilWkst1.ics"); - internal static string WeeklyUntilWkst2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyUntilWkst2.ics"); - internal static string WeeklyWeekStartsLastYear => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyWeekStartsLastYear.ics"); - internal static string WeeklyWkst1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.WeeklyWkst1.ics"); - internal static string XProperty1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.XProperty1.ics"); - internal static string XProperty2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Serialization.XProperty2.ics"); - internal static string Yearly1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.Yearly1.ics"); - internal static string YearlyByDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByDay1.ics"); - internal static string YearlyByMonth1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByMonth1.ics"); - internal static string YearlyByMonth2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByMonth2.ics"); - internal static string YearlyByMonth3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByMonth3.ics"); - internal static string YearlyByMonthDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByMonthDay1.ics"); - internal static string YearlyBySetPos1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyBySetPos1.ics"); - internal static string YearlyByWeekNo1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByWeekNo1.ics"); - internal static string YearlyByWeekNo2 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByWeekNo2.ics"); - internal static string YearlyByWeekNo3 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByWeekNo3.ics"); - internal static string YearlyByWeekNo4 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByWeekNo4.ics"); - internal static string YearlyByWeekNo5 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyByWeekNo5.ics"); - internal static string YearlyComplex1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyComplex1.ics"); - internal static string YearlyCountByMonth1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyCountByMonth1.ics"); - internal static string YearlyCountByYearDay1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyCountByYearDay1.ics"); - internal static string YearlyInterval1 => ReadStream("Ical.Net.CoreUnitTests.Calendars.Recurrence.YearlyInterval1.ics"); + internal static string Alarm1 => ReadStream("Calendars/Alarm/ALARM1.ics"); + internal static string Alarm2 => ReadStream("Calendars/Alarm/ALARM2.ics"); + internal static string Alarm3 => ReadStream("Calendars/Alarm/ALARM3.ics"); + internal static string Alarm4 => ReadStream("Calendars/Alarm/ALARM4.ics"); + internal static string Alarm5 => ReadStream("Calendars/Alarm/ALARM5.ics"); + internal static string Alarm6 => ReadStream("Calendars/Alarm/ALARM6.ics"); + internal static string Alarm7 => ReadStream("Calendars/Alarm/ALARM7.ics"); + internal static string Attachment3 => ReadStream("Calendars/Serialization/Attachment3.ics"); + internal static string Attachment4 => ReadStream("Calendars/Serialization/Attachment4.ics"); + internal static string Attendee1 => ReadStream("Calendars/Serialization/Attendee1.ics"); + internal static string Attendee2 => ReadStream("Calendars/Serialization/Attendee2.ics"); + internal static string Bug1741093 => ReadStream("Calendars/Recurrence/Bug1741093.ics"); + internal static string Bug2033495 => ReadStream("Calendars/Serialization/Bug2033495.ics"); + internal static string Bug2148092 => ReadStream("Calendars/Serialization/Bug2148092.ics"); + internal static string Bug2912657 => ReadStream("Calendars/Recurrence/Bug2912657.ics"); + internal static string Bug2916581 => ReadStream("Calendars/Recurrence/Bug2916581.ics"); + internal static string Bug2938007 => ReadStream("Calendars/Serialization/Bug2938007.ics"); + internal static string Bug2959692 => ReadStream("Calendars/Recurrence/Bug2959692.ics"); + internal static string Bug2966236 => ReadStream("Calendars/Recurrence/Bug2966236.ics"); + internal static string Bug3007244 => ReadStream("Calendars/Recurrence/Bug3007244.ics"); + internal static string ByMonth1 => ReadStream("Calendars/Recurrence/ByMonth1.ics"); + internal static string ByMonth2 => ReadStream("Calendars/Recurrence/ByMonth2.ics"); + internal static string ByMonthDay1 => ReadStream("Calendars/Recurrence/ByMonthDay1.ics"); + internal static string Calendar1 => ReadStream("Calendars/Serialization/Calendar1.ics"); + internal static string CalendarParameters2 => ReadStream("Calendars/Serialization/CalendarParameters2.ics"); + internal static string CaseInsensitive1 => ReadStream("Calendars/Serialization/CaseInsensitive1.ics"); + internal static string CaseInsensitive2 => ReadStream("Calendars/Serialization/CaseInsensitive2.ics"); + internal static string CaseInsensitive3 => ReadStream("Calendars/Serialization/CaseInsensitive3.ics"); + internal static string CaseInsensitive4 => ReadStream("Calendars/Serialization/CaseInsensitive4.ics"); + internal static string Categories1 => ReadStream("Calendars/Serialization/Categories1.ics"); + internal static string Daily1 => ReadStream("Calendars/Recurrence/Daily1.ics"); + internal static string DailyByDay1 => ReadStream("Calendars/Recurrence/DailyByDay1.ics"); + internal static string DailyByHourMinute1 => ReadStream("Calendars/Recurrence/DailyByHourMinute1.ics"); + internal static string DailyCount1 => ReadStream("Calendars/Recurrence/DailyCount1.ics"); + internal static string DailyCount2 => ReadStream("Calendars/Recurrence/DailyCount2.ics"); + internal static string DailyInterval1 => ReadStream("Calendars/Recurrence/DailyInterval1.ics"); + internal static string DailyInterval2 => ReadStream("Calendars/Recurrence/DailyInterval2.ics"); + internal static string DailyUntil1 => ReadStream("Calendars/Recurrence/DailyUntil1.ics"); + internal static string DateTime1 => ReadStream("Calendars/Serialization/DateTime1.ics"); + internal static string DateTime2 => ReadStream("Calendars/Serialization/DateTime2.ics"); + internal static string Duration1 => ReadStream("Calendars/Serialization/Duration1.ics"); + internal static string Empty1 => ReadStream("Calendars/Recurrence/Empty1.ics"); + internal static string EmptyLines1 => ReadStream("Calendars/Serialization/EmptyLines1.ics"); + internal static string EmptyLines2 => ReadStream("Calendars/Serialization/EmptyLines2.ics"); + internal static string EmptyLines3 => ReadStream("Calendars/Serialization/EmptyLines3.ics"); + internal static string EmptyLines4 => ReadStream("Calendars/Serialization/EmptyLines4.ics"); + internal static string Encoding1 => ReadStream("Calendars/Serialization/Encoding1.ics"); + internal static string Encoding2 => ReadStream("Calendars/Serialization/Encoding2.ics"); + internal static string Encoding3 => ReadStream("Calendars/Serialization/Encoding3.ics"); + internal static string Event1 => ReadStream("Calendars/Serialization/Event1.ics"); + internal static string Event2 => ReadStream("Calendars/Serialization/Event2.ics"); + internal static string Event3 => ReadStream("Calendars/Serialization/Event3.ics"); + internal static string Event4 => ReadStream("Calendars/Serialization/Event4.ics"); + internal static string EventStatus => ReadStream("Calendars/Serialization/EventStatus.ics"); + internal static string GeographicLocation1 => ReadStream("Calendars/Serialization/GeographicLocation1.ics"); + internal static string Google1 => ReadStream("Calendars/Serialization/Google1.ics"); + internal static string Hourly1 => ReadStream("Calendars/Recurrence/Hourly1.ics"); + internal static string HourlyInterval1 => ReadStream("Calendars/Recurrence/HourlyInterval1.ics"); + internal static string HourlyInterval2 => ReadStream("Calendars/Recurrence/HourlyInterval2.ics"); + internal static string HourlyUntil1 => ReadStream("Calendars/Recurrence/HourlyUntil1.ics"); + internal static string Journal1 => ReadStream("Calendars/Journal/JOURNAL1.ics"); + internal static string Journal2 => ReadStream("Calendars/Journal/JOURNAL2.ics"); + internal static string Language1 => ReadStream("Calendars/Serialization/Language1.ics"); + internal static string Language2 => ReadStream("Calendars/Serialization/Language2.ics"); + internal static string Language3 => ReadStream("Calendars/Serialization/Language3.ics"); + internal static string Language4 => ReadStream("Calendars/Serialization/Language4.ics"); + internal static string Minutely1 => ReadStream("Calendars/Recurrence/Minutely1.ics"); + internal static string MinutelyByHour1 => ReadStream("Calendars/Recurrence/MinutelyByHour1.ics"); + internal static string MinutelyCount1 => ReadStream("Calendars/Recurrence/MinutelyCount1.ics"); + internal static string MinutelyCount2 => ReadStream("Calendars/Recurrence/MinutelyCount2.ics"); + internal static string MinutelyCount3 => ReadStream("Calendars/Recurrence/MinutelyCount3.ics"); + internal static string MinutelyCount4 => ReadStream("Calendars/Recurrence/MinutelyCount4.ics"); + internal static string MinutelyInterval1 => ReadStream("Calendars/Recurrence/MinutelyInterval1.ics"); + internal static string Monthly1 => ReadStream("Calendars/Recurrence/Monthly1.ics"); + internal static string MonthlyByDay1 => ReadStream("Calendars/Recurrence/MonthlyByDay1.ics"); + internal static string MonthlyByMonthDay1 => ReadStream("Calendars/Recurrence/MonthlyByMonthDay1.ics"); + internal static string MonthlyByMonthDay2 => ReadStream("Calendars/Recurrence/MonthlyByMonthDay2.ics"); + internal static string MonthlyBySetPos1 => ReadStream("Calendars/Recurrence/MonthlyBySetPos1.ics"); + internal static string MonthlyBySetPos2 => ReadStream("Calendars/Recurrence/MonthlyBySetPos2.ics"); + internal static string MonthlyCountByDay1 => ReadStream("Calendars/Recurrence/MonthlyCountByDay1.ics"); + internal static string MonthlyCountByDay2 => ReadStream("Calendars/Recurrence/MonthlyCountByDay2.ics"); + internal static string MonthlyCountByDay3 => ReadStream("Calendars/Recurrence/MonthlyCountByDay3.ics"); + internal static string MonthlyCountByMonthDay1 => ReadStream("Calendars/Recurrence/MonthlyCountByMonthDay1.ics"); + internal static string MonthlyCountByMonthDay2 => ReadStream("Calendars/Recurrence/MonthlyCountByMonthDay2.ics"); + internal static string MonthlyCountByMonthDay3 => ReadStream("Calendars/Recurrence/MonthlyCountByMonthDay3.ics"); + internal static string MonthlyInterval1 => ReadStream("Calendars/Recurrence/MonthlyInterval1.ics"); + internal static string MonthlyUntilByDay1 => ReadStream("Calendars/Recurrence/MonthlyUntilByDay1.ics"); + internal static string Outlook2007LineFolds => ReadStream("Calendars/Serialization/Outlook2007LineFolds.ics"); + internal static string Parameter1 => ReadStream("Calendars/Serialization/Parameter1.ics"); + internal static string Parameter2 => ReadStream("Calendars/Serialization/Parameter2.ics"); + internal static string Parse1 => ReadStream("Calendars/Serialization/Parse1.ics"); + internal static string Parse17 => ReadStream("Calendars/Serialization/PARSE17.ics"); + internal static string ProdId1 => ReadStream("Calendars/Serialization/ProdID1.ics"); + internal static string ProdId2 => ReadStream("Calendars/Serialization/ProdID2.ics"); + internal static string Property1 => ReadStream("Calendars/Serialization/Property1.ics"); + internal static string RecurrenceDates1 => ReadStream("Calendars/Serialization/RecurrenceDates1.ics"); + internal static string RequestStatus1 => ReadStream("Calendars/Serialization/RequestStatus1.ics"); + internal static string Secondly1 => ReadStream("Calendars/Recurrence/Secondly1.ics"); + internal static string TimeZone1 => ReadStream("Calendars/Serialization/TimeZone1.ics"); + internal static string TimeZone2 => ReadStream("Calendars/Serialization/TimeZone2.ics"); + internal static string TimeZone3 => ReadStream("Calendars/Serialization/TimeZone3.ics"); + internal static string Todo1 => ReadStream("Calendars/Todo/Todo1.ics"); + internal static string Todo2 => ReadStream("Calendars/Todo/Todo2.ics"); + internal static string Todo3 => ReadStream("Calendars/Todo/Todo3.ics"); + internal static string Todo4 => ReadStream("Calendars/Todo/Todo4.ics"); + internal static string Todo5 => ReadStream("Calendars/Todo/Todo5.ics"); + internal static string Todo6 => ReadStream("Calendars/Todo/Todo6.ics"); + internal static string Todo7 => ReadStream("Calendars/Todo/Todo7.ics"); + internal static string Todo8 => ReadStream("Calendars/Todo/Todo8.ics"); + internal static string Todo9 => ReadStream("Calendars/Todo/Todo9.ics"); + internal static string Transparency1 => ReadStream("Calendars/Serialization/Transparency1.ics"); + internal static string Transparency2 => ReadStream("Calendars/Serialization/Transparency2.ics"); + internal static string Trigger1 => ReadStream("Calendars/Serialization/Trigger1.ics"); + internal static string UsHolidays => ReadStream("Calendars/Serialization/USHolidays.ics"); + internal static string WeeklyCount1 => ReadStream("Calendars/Recurrence/WeeklyCount1.ics"); + internal static string WeeklyCountWkst1 => ReadStream("Calendars/Recurrence/WeeklyCountWkst1.ics"); + internal static string WeeklyCountWkst2 => ReadStream("Calendars/Recurrence/WeeklyCountWkst2.ics"); + internal static string WeeklyCountWkst3 => ReadStream("Calendars/Recurrence/WeeklyCountWkst3.ics"); + internal static string WeeklyCountWkst4 => ReadStream("Calendars/Recurrence/WeeklyCountWkst4.ics"); + internal static string WeeklyInterval1 => ReadStream("Calendars/Recurrence/WeeklyInterval1.ics"); + internal static string WeeklyUntil1 => ReadStream("Calendars/Recurrence/WeeklyUntil1.ics"); + internal static string WeeklyUntilWkst1 => ReadStream("Calendars/Recurrence/WeeklyUntilWkst1.ics"); + internal static string WeeklyUntilWkst2 => ReadStream("Calendars/Recurrence/WeeklyUntilWkst2.ics"); + internal static string WeeklyWeekStartsLastYear => ReadStream("Calendars/Recurrence/WeeklyWeekStartsLastYear.ics"); + internal static string WeeklyWkst1 => ReadStream("Calendars/Recurrence/WeeklyWkst1.ics"); + internal static string XProperty1 => ReadStream("Calendars/Serialization/XProperty1.ics"); + internal static string XProperty2 => ReadStream("Calendars/Serialization/XProperty2.ics"); + internal static string Yearly1 => ReadStream("Calendars/Recurrence/Yearly1.ics"); + internal static string YearlyByDay1 => ReadStream("Calendars/Recurrence/YearlyByDay1.ics"); + internal static string YearlyByMonth1 => ReadStream("Calendars/Recurrence/YearlyByMonth1.ics"); + internal static string YearlyByMonth2 => ReadStream("Calendars/Recurrence/YearlyByMonth2.ics"); + internal static string YearlyByMonth3 => ReadStream("Calendars/Recurrence/YearlyByMonth3.ics"); + internal static string YearlyByMonthDay1 => ReadStream("Calendars/Recurrence/YearlyByMonthDay1.ics"); + internal static string YearlyBySetPos1 => ReadStream("Calendars/Recurrence/YearlyBySetPos1.ics"); + internal static string YearlyByWeekNo1 => ReadStream("Calendars/Recurrence/YearlyByWeekNo1.ics"); + internal static string YearlyByWeekNo2 => ReadStream("Calendars/Recurrence/YearlyByWeekNo2.ics"); + internal static string YearlyByWeekNo3 => ReadStream("Calendars/Recurrence/YearlyByWeekNo3.ics"); + internal static string YearlyByWeekNo4 => ReadStream("Calendars/Recurrence/YearlyByWeekNo4.ics"); + internal static string YearlyByWeekNo5 => ReadStream("Calendars/Recurrence/YearlyByWeekNo5.ics"); + internal static string YearlyComplex1 => ReadStream("Calendars/Recurrence/YearlyComplex1.ics"); + internal static string YearlyCountByMonth1 => ReadStream("Calendars/Recurrence/YearlyCountByMonth1.ics"); + internal static string YearlyCountByYearDay1 => ReadStream("Calendars/Recurrence/YearlyCountByYearDay1.ics"); + internal static string YearlyInterval1 => ReadStream("Calendars/Recurrence/YearlyInterval1.ics"); } } diff --git a/src/Ical.Net.CoreUnitTests/ProgramTest.cs b/src/Ical.Net.CoreUnitTests/ProgramTest.cs index 4a2bc7510..c68837bcc 100644 --- a/src/Ical.Net.CoreUnitTests/ProgramTest.cs +++ b/src/Ical.Net.CoreUnitTests/ProgramTest.cs @@ -18,7 +18,7 @@ public void LoadAndDisplayCalendar() Assert.IsNotNull(iCal, "iCalendar did not load."); } - private const string _tzid = "US-Eastern"; + const string _tzid = "US-Eastern"; public static void TestCal(Calendar cal) { diff --git a/src/Ical.Net.CoreUnitTests/RecurrenceTests.cs b/src/Ical.Net.CoreUnitTests/RecurrenceTests.cs index 1b123f627..c13ab55fa 100644 --- a/src/Ical.Net.CoreUnitTests/RecurrenceTests.cs +++ b/src/Ical.Net.CoreUnitTests/RecurrenceTests.cs @@ -19,9 +19,9 @@ namespace Ical.Net.CoreUnitTests [TestFixture] public class RecurrenceTests { - private const string _tzid = "US-Eastern"; + const string _tzid = "US-Eastern"; - private void EventOccurrenceTest( + static void EventOccurrenceTest( Calendar cal, IDateTime fromDate, IDateTime toDate, @@ -60,16 +60,13 @@ int eventIndex } } - private void EventOccurrenceTest( + void EventOccurrenceTest( Calendar cal, IDateTime fromDate, IDateTime toDate, IDateTime[] dateTimes, string[] timeZones - ) - { - EventOccurrenceTest(cal, fromDate, toDate, dateTimes, timeZones, 0); - } + ) => EventOccurrenceTest(cal, fromDate, toDate, dateTimes, timeZones, 0); /// /// See Page 45 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30 @@ -2654,18 +2651,16 @@ public void BugByMonthWhileFreqIsMonthly() [Test, Category("Recurrence")] public void Bug3119920() { - using (var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126T120000;INTERVAL=1;BYDAY=MO")) - { - var start = DateTime.Parse("2010-11-27 9:00:00"); - var serializer = new RecurrencePatternSerializer(); - var rp = (RecurrencePattern)serializer.Deserialize(sr); - var rpe = new RecurrencePatternEvaluator(rp); - var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, rp.Until, false); + using var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126T120000;INTERVAL=1;BYDAY=MO"); + var start = DateTime.Parse("2010-11-27 9:00:00"); + var serializer = new RecurrencePatternSerializer(); + var rp = (RecurrencePattern)serializer.Deserialize(sr); + var rpe = new RecurrencePatternEvaluator(rp); + var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, rp.Until, false); - var period = recurringPeriods.ElementAt(recurringPeriods.Count - 1); + var period = recurringPeriods.ElementAt(recurringPeriods.Count - 1); - Assert.AreEqual(new CalDateTime(2025, 11, 24, 9, 0, 0), period.StartTime); - } + Assert.AreEqual(new CalDateTime(2025, 11, 24, 9, 0, 0), period.StartTime); } /// @@ -2702,14 +2697,12 @@ public void Bug3178652() [Test, Category("Recurrence")] public void Bug3292737() { - using (var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126")) - { - var serializer = new RecurrencePatternSerializer(); - var rp = (RecurrencePattern)serializer.Deserialize(sr); + using var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126"); + var serializer = new RecurrencePatternSerializer(); + var rp = (RecurrencePattern)serializer.Deserialize(sr); - Assert.IsNotNull(rp); - Assert.AreEqual(new DateTime(2025, 11, 26), rp.Until); - } + Assert.IsNotNull(rp); + Assert.AreEqual(new DateTime(2025, 11, 26), rp.Until); } /// @@ -2795,7 +2788,7 @@ public void RecurrencePattern1() RecurrencePattern pattern = new RecurrencePattern("FREQ=SECONDLY;INTERVAL=10"); pattern.RestrictionType = RecurrenceRestrictionType.NoRestriction; - var us = new CultureInfo("en-US"); + var us = new CultureInfo("en-US", false); var startDate = new CalDateTime(DateTime.Parse("3/30/08 11:59:40 PM", us)); var fromDate = new CalDateTime(DateTime.Parse("3/30/08 11:59:40 PM", us)); @@ -2806,8 +2799,8 @@ public void RecurrencePattern1() var occurrences = evaluator.Evaluate( startDate, - DateUtil.SimpleDateTimeToMatch(fromDate, startDate), - DateUtil.SimpleDateTimeToMatch(toDate, startDate), + fromDate.SimpleDateTimeToMatch(startDate), + toDate.SimpleDateTimeToMatch(startDate), false) .OrderBy(o => o.StartTime) .ToList(); @@ -2836,8 +2829,8 @@ public void RecurrencePattern2() var occurrences = evaluator.Evaluate( startDate, - DateUtil.SimpleDateTimeToMatch(fromDate, startDate), - DateUtil.SimpleDateTimeToMatch(toDate, startDate), + fromDate.SimpleDateTimeToMatch(startDate), + toDate.SimpleDateTimeToMatch(startDate), false); Assert.AreNotEqual(0, occurrences.Count); } @@ -2920,9 +2913,9 @@ public void Test2() RecurrencePattern recur = new RecurrencePattern(); recur.Frequency = FrequencyType.Daily; recur.Count = 3; - recur.ByDay.Add(new WeekDay(DayOfWeek.Monday)); - recur.ByDay.Add(new WeekDay(DayOfWeek.Wednesday)); - recur.ByDay.Add(new WeekDay(DayOfWeek.Friday)); + recur.ByWeekDay.Add(new WeekDay(DayOfWeek.Monday)); + recur.ByWeekDay.Add(new WeekDay(DayOfWeek.Wednesday)); + recur.ByWeekDay.Add(new WeekDay(DayOfWeek.Friday)); evt.RecurrenceRules.Add(recur); var serializer = new RecurrencePatternSerializer(); @@ -2934,8 +2927,8 @@ public void Test2() public void Test4() { RecurrencePattern rpattern = new RecurrencePattern(); - rpattern.ByDay.Add(new WeekDay(DayOfWeek.Saturday)); - rpattern.ByDay.Add(new WeekDay(DayOfWeek.Sunday)); + rpattern.ByWeekDay.Add(new WeekDay(DayOfWeek.Saturday)); + rpattern.ByWeekDay.Add(new WeekDay(DayOfWeek.Sunday)); rpattern.Frequency = FrequencyType.Weekly; @@ -2948,8 +2941,8 @@ public void Test4() // Add the exception dates var periods = evaluator.Evaluate( evtStart, - DateUtil.GetSimpleDateTimeData(evtStart), - DateUtil.SimpleDateTimeToMatch(evtEnd, evtStart), + evtStart.GetSimpleDateTimeData(), + evtEnd.SimpleDateTimeToMatch(evtStart), false) .OrderBy(p => p.StartTime) .ToList(); @@ -3054,7 +3047,7 @@ public void OccurrenceMustBeCompletelyContainedWithinSearchRange() var rrule = new RecurrencePattern(FrequencyType.Weekly, interval: 1) { Until = DateTime.Parse("2016-08-31T07:00:00"), - ByDay = new List { new WeekDay(DayOfWeek.Wednesday)}, + ByWeekDay = new List { new WeekDay(DayOfWeek.Wednesday)}, }; var start = DateTime.Parse("2016-08-01T07:00:00"); @@ -3206,9 +3199,10 @@ public void AddExDateToEventAfterGetOccurrencesShouldRecomputeResult() Assert.IsTrue(occurrences.Count == 3); } - private static readonly DateTime _now = DateTime.Now; - private static readonly DateTime _later = _now.AddHours(1); - private static CalendarEvent GetEventWithRecurrenceRules() + static readonly DateTime _now = DateTime.Now; + static readonly DateTime _later = _now.AddHours(1); + + static CalendarEvent GetEventWithRecurrenceRules() { var dailyForFiveDays = new RecurrencePattern(FrequencyType.Daily, 1) { @@ -3277,9 +3271,9 @@ public void ExDateTimeZone_Tests() Assert.AreEqual(3, Regex.Matches(serialized, expected).Count); } - private static RecurrencePattern GetSimpleRecurrencePattern(int count) => new RecurrencePattern(FrequencyType.Daily, 1) { Count = count, }; + static RecurrencePattern GetSimpleRecurrencePattern(int count) => new RecurrencePattern(FrequencyType.Daily, 1) { Count = count, }; - private static CalendarEvent GetSimpleEvent() + static CalendarEvent GetSimpleEvent() { var e = new CalendarEvent { diff --git a/src/Ical.Net.CoreUnitTests/SerializationTests.cs b/src/Ical.Net.CoreUnitTests/SerializationTests.cs index 7aa6a19aa..d74ff9b38 100644 --- a/src/Ical.Net.CoreUnitTests/SerializationTests.cs +++ b/src/Ical.Net.CoreUnitTests/SerializationTests.cs @@ -17,13 +17,13 @@ namespace Ical.Net.CoreUnitTests [TestFixture] public class SerializationTests { - private static readonly DateTime _nowTime = DateTime.Now; - private static readonly DateTime _later = _nowTime.AddHours(1); - private static CalendarSerializer GetNewSerializer() => new CalendarSerializer(); - private static string SerializeToString(Calendar c) => GetNewSerializer().SerializeToString(c); - private static string SerializeToString(CalendarEvent e) => SerializeToString(new Calendar { Events = { e } }); - private static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime }; - private static Calendar UnserializeCalendar(string s) => Calendar.Load(s); + static readonly DateTime _nowTime = DateTime.Now; + static readonly DateTime _later = _nowTime.AddHours(1); + static CalendarSerializer GetNewSerializer() => new CalendarSerializer(); + static string SerializeToString(Calendar c) => GetNewSerializer().SerializeToString(c); + static string SerializeToString(CalendarEvent e) => SerializeToString(new Calendar { Events = { e } }); + static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime }; + static Calendar UnserializeCalendar(string s) => Calendar.Load(s); public static void CompareCalendars(Calendar cal1, Calendar cal2) { @@ -33,9 +33,7 @@ public static void CompareCalendars(Calendar cal1, Calendar cal2) for (var i = 0; i < cal1.Children.Count; i++) { - var component1 = cal1.Children[i] as ICalendarComponent; - var component2 = cal2.Children[i] as ICalendarComponent; - if (component1 != null && component2 != null) + if (cal1.Children[i] is ICalendarComponent component1 && cal2.Children[i] is ICalendarComponent component2) { CompareComponents(component1, component2); } @@ -49,17 +47,16 @@ public static void CompareComponents(ICalendarComponent cb1, ICalendarComponent var isMatch = false; foreach (var p2 in cb2.Properties.AllOf(p1.Name)) { - try { Assert.AreEqual(p1, p2, "The properties '" + p1.Name + "' are not equal."); - if (p1.Value is IComparable) + if (p1.Value is IComparable comparable) { - if (((IComparable)p1.Value).CompareTo(p2.Value) != 0) + if (comparable.CompareTo(p2.Value) != 0) continue; } - else if (p1.Value is IEnumerable) + else if (p1.Value is IEnumerable value) { - CompareEnumerables((IEnumerable)p1.Value, (IEnumerable)p2.Value, p1.Name); + CompareEnumerables(value, (IEnumerable)p2.Value, p1.Name); } else { @@ -69,7 +66,6 @@ public static void CompareComponents(ICalendarComponent cb1, ICalendarComponent isMatch = true; break; } - catch { } } Assert.IsTrue(isMatch, "Could not find a matching property - " + p1.Name + ":" + (p1.Value?.ToString() ?? string.Empty)); @@ -109,15 +105,18 @@ public static void CompareEnumerables(IEnumerable a1, IEnumerable a2, string val } } + public static string InspectSerializedSection(string serialized, string sectionName, params string[] elements) + => InspectSerializedSection(serialized, sectionName, elements.AsEnumerable()); + public static string InspectSerializedSection(string serialized, string sectionName, IEnumerable elements) { const string notFound = "expected '{0}' not found"; var searchFor = "BEGIN:" + sectionName; - var begin = serialized.IndexOf(searchFor); + var begin = serialized.IndexOf(searchFor, StringComparison.Ordinal); Assert.AreNotEqual(-1, begin, notFound, searchFor); searchFor = "END:" + sectionName; - var end = serialized.IndexOf(searchFor, begin); + var end = serialized.IndexOf(searchFor, begin, StringComparison.Ordinal); Assert.AreNotEqual(-1, end, notFound, searchFor); var searchRegion = serialized.Substring(begin, end - begin + 1); @@ -158,7 +157,9 @@ static Dictionary GetValues(string serialized, string name, stri : match.Groups[1].Value.Substring(1).Split(';').Select(v => v.Split('=')).ToDictionary(v => v[0], v => v.Length > 1 ? v[1] : null); } - [Test, Category("Serialization"), Ignore("TODO: standard time, for NZ standard time (current example)")] + [Test] + [Category("Serialization")] + [Ignore("TODO: standard time, for NZ standard time (current example)")] public void TimeZoneSerialize() { //ToDo: This test is broken as of 2016-07-13 @@ -183,18 +184,13 @@ public void TimeZoneSerialize() var serializer = new CalendarSerializer(); var serializedCalendar = serializer.SerializeToString(cal); - var vTimezone = InspectSerializedSection(serializedCalendar, "VTIMEZONE", new[] { "TZID:" + tz.TzId }); + var vTimezone = InspectSerializedSection(serializedCalendar, "VTIMEZONE", "TZID:" + tz.TzId); var o = tzi.BaseUtcOffset.ToString("hhmm", CultureInfo.InvariantCulture); - InspectSerializedSection(vTimezone, "STANDARD", new[] {"TZNAME:" + tzi.StandardName, "TZOFFSETTO:" + o - //"DTSTART:20150402T030000", - //"RRULE:FREQ=YEARLY;BYDAY=1SU;BYHOUR=3;BYMINUTE=0;BYMONTH=4", - //"TZOFFSETFROM:+1300" - }); - - - InspectSerializedSection(vTimezone, "DAYLIGHT", new[] { "TZNAME:" + tzi.DaylightName, "TZOFFSETFROM:" + o }); + InspectSerializedSection(vTimezone, "STANDARD", "TZNAME:" + tzi.StandardName, "TZOFFSETTO:" + o); + InspectSerializedSection(vTimezone, "DAYLIGHT", "TZNAME:" + tzi.DaylightName, "TZOFFSETFROM:" + o); } + [Test, Category("Serialization")] public void SerializeDeserialize() { @@ -269,18 +265,10 @@ public void EventPropertiesSerialized() Assert.IsTrue(serializedCalendar.Contains(SerializationConstants.LineBreak + p + SerializationConstants.LineBreak), "expected '" + p + "' not found"); } - InspectSerializedSection(serializedCalendar, "VEVENT", - new[] - { - "CLASS:" + evt.Class, "CREATED:" + CalDateString(evt.Created), "DTSTAMP:" + CalDateString(evt.DtStamp), - "LAST-MODIFIED:" + CalDateString(evt.LastModified), "SEQUENCE:" + evt.Sequence, "UID:" + evt.Uid, "PRIORITY:" + evt.Priority, - "LOCATION:" + evt.Location, "SUMMARY:" + evt.Summary, "DTSTART:" + CalDateString(evt.DtStart), "DTEND:" + CalDateString(evt.DtEnd) - //"TRANSPARENCY:" + TransparencyType.Opaque.ToString().ToUpperInvariant(), - //"STATUS:" + EventStatus.Confirmed.ToString().ToUpperInvariant() - }); + InspectSerializedSection(serializedCalendar, "VEVENT", "CLASS:" + evt.Class, "CREATED:" + CalDateString(evt.Created), "DTSTAMP:" + CalDateString(evt.DtStamp), "LAST-MODIFIED:" + CalDateString(evt.LastModified), "SEQUENCE:" + evt.Sequence, "UID:" + evt.Uid, "PRIORITY:" + evt.Priority, "LOCATION:" + evt.Location, "SUMMARY:" + evt.Summary, "DTSTART:" + CalDateString(evt.DtStart), "DTEND:" + CalDateString(evt.DtEnd)); } - private static readonly IList _attendees = new List + static readonly IList _attendees = new List { new Attendee("MAILTO:james@example.com") { @@ -321,7 +309,7 @@ public void AttendeesSerialized() var serializer = new CalendarSerializer(); var serializedCalendar = serializer.SerializeToString(cal); - var vEvt = InspectSerializedSection(serializedCalendar, "VEVENT", new[] { "ORGANIZER:" + org }); + var vEvt = InspectSerializedSection(serializedCalendar, "VEVENT", "ORGANIZER:" + org); foreach (var a in evt.Attendees) { @@ -348,7 +336,7 @@ public void AttendeesSerialized() [Test] public void ZeroTimeSpan_Test() { - var result = new TimeSpanSerializer().SerializeToString(TimeSpan.Zero); + var result = TimeSpanSerializer.SerializeToString(TimeSpan.Zero); Assert.IsTrue("P0D".Equals(result, StringComparison.Ordinal)); } diff --git a/src/Ical.Net.CoreUnitTests/SymmetricSerializationTests.cs b/src/Ical.Net.CoreUnitTests/SymmetricSerializationTests.cs index ec5590d4c..334e85f5f 100644 --- a/src/Ical.Net.CoreUnitTests/SymmetricSerializationTests.cs +++ b/src/Ical.Net.CoreUnitTests/SymmetricSerializationTests.cs @@ -13,14 +13,14 @@ namespace Ical.Net.CoreUnitTests { public class SymmetricSerializationTests { - private const string _ldapUri = "ldap://example.com:6666/o=eDABC Industries,c=3DUS??(cn=3DBMary Accepted)"; - - private static readonly DateTime _nowTime = DateTime.Now; - private static readonly DateTime _later = _nowTime.AddHours(1); - private static CalendarSerializer GetNewSerializer() => new CalendarSerializer(); - private static string SerializeToString(Calendar c) => GetNewSerializer().SerializeToString(c); - private static CalendarEvent GetSimpleEvent() => new CalendarEvent {DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime}; - private static Calendar UnserializeCalendar(string s) => Calendar.Load(s); + const string _ldapUri = "ldap://example.com:6666/o=eDABC Industries,c=3DUS??(cn=3DBMary Accepted)"; + + static readonly DateTime _nowTime = DateTime.Now; + static readonly DateTime _later = _nowTime.AddHours(1); + static CalendarSerializer GetNewSerializer() => new CalendarSerializer(); + static string SerializeToString(Calendar c) => GetNewSerializer().SerializeToString(c); + static CalendarEvent GetSimpleEvent() => new CalendarEvent {DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime}; + static Calendar UnserializeCalendar(string s) => Calendar.Load(s); [Test, TestCaseSource(nameof(Event_TestCases))] public void Event_Tests(Calendar iCalendar) diff --git a/src/Ical.Net.CoreUnitTests/TodoTest.cs b/src/Ical.Net.CoreUnitTests/TodoTest.cs index 2546b7df8..c61024db6 100644 --- a/src/Ical.Net.CoreUnitTests/TodoTest.cs +++ b/src/Ical.Net.CoreUnitTests/TodoTest.cs @@ -9,7 +9,7 @@ namespace Ical.Net.CoreUnitTests [TestFixture] public class TodoTest { - private const string _tzid = "US-Eastern"; + const string _tzid = "US-Eastern"; [Test, TestCaseSource(nameof(ActiveTodo_TestCases)), Category("Todo")] public void ActiveTodo_Tests(string calendarString, IList> incoming) diff --git a/src/Ical.Net.CoreUnitTests/VTimeZoneTest.cs b/src/Ical.Net.CoreUnitTests/VTimeZoneTest.cs index 36f3eddcf..f3f3aeb33 100644 --- a/src/Ical.Net.CoreUnitTests/VTimeZoneTest.cs +++ b/src/Ical.Net.CoreUnitTests/VTimeZoneTest.cs @@ -10,16 +10,10 @@ namespace Ical.Net.CoreUnitTests public class VTimeZoneTest { [Test, Category("VTimeZone")] - public void InvalidTzIdShouldThrowException() - { - Assert.Throws(() => new VTimeZone("shouldFail")); - } + public void InvalidTzIdShouldThrowException() => Assert.Throws(() => new VTimeZone("shouldFail")); [Test, Category("VTimeZone")] - public void VTimeZoneFromDateTimeZoneNullZoneShouldThrowException() - { - Assert.Throws(() => CreateTestCalendar("shouldFail")); - } + public void VTimeZoneFromDateTimeZoneNullZoneShouldThrowException() => Assert.Throws(() => CreateTestCalendar("shouldFail")); [Test, Category("VTimeZone")] public void VTimeZoneAmericaPhoenixShouldSerializeProperly() @@ -213,7 +207,7 @@ public void VTimeZoneAmericaDetroitShouldSerializeProperly() Assert.IsTrue(serialized.Contains("DTSTART:20071104T020000"), "DTSTART:20071104T020000 was not serialized"); } - private static Calendar CreateTestCalendar(string tzId, DateTime? earliestTime = null, bool includeHistoricalData = true) + static Calendar CreateTestCalendar(string tzId, DateTime? earliestTime = null, bool includeHistoricalData = true) { var iCal = new Calendar(); diff --git a/src/Ical.Net.sln b/src/Ical.Net.sln index b4cba3a8d..72dacb0c9 100644 --- a/src/Ical.Net.sln +++ b/src/Ical.Net.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30406.217 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33103.184 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ical.Net", "Ical.Net\Ical.Net.csproj", "{71446356-D4EC-4141-B25C-4FAE6FA86265}" EndProject @@ -9,6 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ical.Net.CoreUnitTests", "I EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfTests", "PerfTests\PerfTests.csproj", "{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{98E2E6B6-7A84-4CB1-9CC3-088DE639185A}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/Ical.Net.sln.DotSettings b/src/Ical.Net.sln.DotSettings new file mode 100644 index 000000000..3fe3a3d11 --- /dev/null +++ b/src/Ical.Net.sln.DotSettings @@ -0,0 +1,8 @@ + + Space + True + True + True + True + True + True \ No newline at end of file diff --git a/src/Ical.Net/Calendar.cd b/src/Ical.Net/Calendar.cd new file mode 100644 index 000000000..0efc50d96 --- /dev/null +++ b/src/Ical.Net/Calendar.cd @@ -0,0 +1,226 @@ + + + + + + + + + + + MAAAgAAAACYAEQAAgAIkAgIAAgAgANIAMIQAAgIAAAA= + Calendar.cs + + + + + + + + + + + AAAAAAAAAAQAASAAAAAAAAAAAAAAAAAIAAAAAAACAAA= + CalendarComponents\CalendarComponent.cs + + + + + + + + + AAAAAFEAAEQAASAAgABEQAQABAAAAIAEAAAAAAABAEQ= + CalendarObject.cs + + + + + + + + + AAAAAAAAAAEAACAIQAAAAAAAAgAAAAAAAAAAAAAAAAA= + CalendarObjectBase.cs + + + + + + + + + + + + + + IACCAAAQAAAiABAAAAAABAAAAAAACBAAAAIAAAAAAAQ= + CalendarComponents\Alarm.cs + + + + + + AAAAAAEIIAAAAAQCgBAABAAAAAAIAIAACAAAAAAAIAA= + CalendarComponents\UniqueComponent.cs + + + + + + + AAAAAAAgABAAAIAAoEIBAAAAAAQAAIAgCiAAAAAIAAA= + CalendarComponents\VTimeZone.cs + + + + + + AAAAQAAAAiQAAQBgoIAhAACAQAQAABACAIQAAAEAAAA= + VTimeZoneInfo.cs + + + + + + + + + + + AAJAAAEEIgUCAQQAkAIAAAQAAACAAIQAAAARAAAAIAA= + CalendarComponents\CalendarEvent.cs + + + + + + + + + + + + + + + + + + + + + BACAAAAIAiQgCQEloIIhAAACAAAAEJQCAIBAAAEAAAA= + CalendarComponents\RecurringComponent.cs + + + + + + + + + + + + + + + + gIAwAAAAAAAESCAEkIAAIEQAAIABAIAAAQAsAAAgAAQ= + DataTypes\RecurrencePattern.cs + + + + + + AEIABEAAAAAAADAUiAABBAQAAAAAAIAECAQAAABQIAA= + DataTypes\PeriodList.cs + + + + + + + AAAAAAAAACAAAAAgIIAgAAAAAAAAAAACAAAAAAAAAAA= + CalendarComponents\IRecurrable.cs + + + + + + BACAAAAAAAAgAAEEAAIAAAACAAAAEAQAAAAAAAEAAAA= + CalendarComponents\IRecurringComponent.cs + + + + \ No newline at end of file diff --git a/src/Ical.Net/Calendar.cs b/src/Ical.Net/Calendar.cs index 6a8a12b52..f037a9bdb 100644 --- a/src/Ical.Net/Calendar.cs +++ b/src/Ical.Net/Calendar.cs @@ -12,6 +12,13 @@ namespace Ical.Net { + /// A Calendar is a Set of , , , and Info + /// + /// Definite are the IETF RFCs # 2445 (1998) 5545 (2009) and 7986 (not supported in this Code yet). + /// iCalendar.org is also a good Resource. + /// + /// Additionally WebDAV HTTP Protocol Extensions are defined in RFCs #4791 and 6638. + /// public class Calendar : CalendarComponent, IGetOccurrencesTyped, IGetFreeBusy, IMergeable { public static Calendar Load(string iCalendarString) @@ -37,13 +44,6 @@ public static IList Load(TextReader tr) public static IList Load(string ical) => Load(new StringReader(ical)); - private IUniqueComponentList _mUniqueComponents; - private IUniqueComponentList _mEvents; - private IUniqueComponentList _mTodos; - private ICalendarObjectList _mJournals; - private IUniqueComponentList _mFreeBusy; - private ICalendarObjectList _mTimeZones; - /// /// To load an existing an iCalendar object, use one of the provided LoadFromXXX methods. /// @@ -59,14 +59,14 @@ public Calendar() Initialize(); } - private void Initialize() + void Initialize() { - _mUniqueComponents = new UniqueComponentListProxy(Children); - _mEvents = new UniqueComponentListProxy(Children); - _mTodos = new UniqueComponentListProxy(Children); - _mJournals = new CalendarObjectListProxy(Children); - _mFreeBusy = new UniqueComponentListProxy(Children); - _mTimeZones = new CalendarObjectListProxy(Children); + UniqueComponents = new UniqueComponentListProxy(Children); + Events = new UniqueComponentListProxy(Children); + Todos = new UniqueComponentListProxy(Children); + Journals = new CalendarObjectListProxy(Children); + FreeBusy = new UniqueComponentListProxy(Children); + TimeZones = new CalendarObjectListProxy(Children); } protected override void OnDeserializing(StreamingContext context) @@ -113,66 +113,66 @@ public override int GetHashCode() } } - public virtual IUniqueComponentList UniqueComponents => _mUniqueComponents; + public IUniqueComponentList UniqueComponents { get; private set; } - public virtual IEnumerable RecurringItems => Children.OfType(); + public IEnumerable RecurringItems => Children.OfType(); /// /// A collection of components in the iCalendar. /// - public virtual IUniqueComponentList Events => _mEvents; + public IUniqueComponentList Events { get; private set; } /// /// A collection of components in the iCalendar. /// - public virtual IUniqueComponentList FreeBusy => _mFreeBusy; + public IUniqueComponentList FreeBusy { get; private set; } /// /// A collection of components in the iCalendar. /// - public virtual ICalendarObjectList Journals => _mJournals; + public ICalendarObjectList Journals { get; private set; } /// /// A collection of VTimeZone components in the iCalendar. /// - public virtual ICalendarObjectList TimeZones => _mTimeZones; + public ICalendarObjectList TimeZones { get; private set; } /// /// A collection of components in the iCalendar. /// - public virtual IUniqueComponentList Todos => _mTodos; + public IUniqueComponentList Todos { get; private set; } - public virtual string Version + public string Version { get => Properties.Get("VERSION"); set => Properties.Set("VERSION", value); } - public virtual string ProductId + public string ProductId { get => Properties.Get("PRODID"); set => Properties.Set("PRODID", value); } - public virtual string Scale + public string Scale { get => Properties.Get("CALSCALE"); set => Properties.Set("CALSCALE", value); } - public virtual string Method + public string Method { get => Properties.Get("METHOD"); set => Properties.Set("METHOD", value); } - public virtual RecurrenceRestrictionType RecurrenceRestriction + public RecurrenceRestrictionType RecurrenceRestriction { get => Properties.Get("X-DDAY-ICAL-RECURRENCE-RESTRICTION"); set => Properties.Set("X-DDAY-ICAL-RECURRENCE-RESTRICTION", value); } - public virtual RecurrenceEvaluationModeType RecurrenceEvaluationMode + public RecurrenceEvaluationModeType RecurrenceEvaluationMode { get => Properties.Get("X-DDAY-ICAL-RECURRENCE-EVALUATION-MODE"); set => Properties.Set("X-DDAY-ICAL-RECURRENCE-EVALUATION-MODE", value); @@ -201,10 +201,7 @@ public VTimeZone AddTimeZone(VTimeZone tz) /// The beginning date/time of the range to test. /// The end date/time of the range to test. [Obsolete("This method is no longer supported. Use GetOccurrences() instead.")] - public void Evaluate(IDateTime fromDate, IDateTime toDate) - { - throw new NotSupportedException("Evaluate() is no longer supported as a public method. Use GetOccurrences() instead."); - } + public void Evaluate(IDateTime fromDate, IDateTime toDate) => throw new NotSupportedException("Evaluate() is no longer supported as a public method. Use GetOccurrences() instead."); /// /// Evaluates component recurrences for the given range of time, for @@ -214,10 +211,7 @@ public void Evaluate(IDateTime fromDate, IDateTime toDate) /// The beginning date/time of the range to test. /// The end date/time of the range to test. [Obsolete("This method is no longer supported. Use GetOccurrences() instead.")] - public void Evaluate(IDateTime fromDate, IDateTime toDate) - { - throw new NotSupportedException("Evaluate() is no longer supported as a public method. Use GetOccurrences() instead."); - } + public void Evaluate(IDateTime fromDate, IDateTime toDate) => throw new NotSupportedException("Evaluate() is no longer supported as a public method. Use GetOccurrences() instead."); /// /// Clears recurrence evaluations for recurring components. @@ -236,10 +230,10 @@ public void ClearEvaluation() /// /// The date for which to return occurrences. Time is ignored on this parameter. /// A list of occurrences that occur on the given date (). - public virtual HashSet GetOccurrences(IDateTime dt) + public HashSet GetOccurrences(IDateTime dt) => GetOccurrences(new CalDateTime(dt.AsSystemLocal.Date), new CalDateTime(dt.AsSystemLocal.Date.AddDays(1).AddSeconds(-1))); - public virtual HashSet GetOccurrences(DateTime dt) + public HashSet GetOccurrences(DateTime dt) => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddSeconds(-1))); /// @@ -249,10 +243,10 @@ public virtual HashSet GetOccurrences(DateTime dt) /// The beginning date/time of the range. /// The end date/time of the range. /// A list of occurrences that fall between the dates provided. - public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) => GetOccurrences(startTime, endTime); - public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) => GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime)); /// @@ -267,10 +261,10 @@ public virtual HashSet GetOccurrences(DateTime startTime, DateTime e /// /// The date for which to return occurrences. /// A list of Periods representing the occurrences of this object. - public virtual HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent + public HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent => GetOccurrences(new CalDateTime(dt.AsSystemLocal.Date), new CalDateTime(dt.AsSystemLocal.Date.AddDays(1).AddTicks(-1))); - public virtual HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent + public HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddTicks(-1))); /// @@ -280,7 +274,7 @@ public virtual HashSet GetOccurrences(DateTime dt) where T : IRec /// /// The starting date range /// The ending date range - public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent { var occurrences = new HashSet(RecurringItems .OfType() @@ -298,7 +292,7 @@ public virtual HashSet GetOccurrences(IDateTime startTime, IDateT return occurrences; } - public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent => GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime)); /// @@ -321,23 +315,19 @@ public virtual HashSet GetOccurrences(DateTime startTime, DateTim public T Create() where T : ICalendarComponent { var obj = Activator.CreateInstance(typeof (T)) as ICalendarObject; - if (obj is T) + if (obj is T component) { this.AddChild(obj); - return (T) obj; + return component; } - return default(T); + return default; } - public void Dispose() - { - Children.Clear(); - } + public void Dispose() => Children.Clear(); - public virtual void MergeWith(IMergeable obj) + public void MergeWith(IMergeable obj) { - var c = obj as Calendar; - if (c == null) + if (!(obj is Calendar c)) { return; } @@ -359,9 +349,9 @@ public virtual void MergeWith(IMergeable obj) foreach (var child in c.Children) { - if (child is IUniqueComponent) + if (child is IUniqueComponent component) { - if (!UniqueComponents.ContainsKey(((IUniqueComponent) child).Uid)) + if (!UniqueComponents.ContainsKey(component.Uid)) { this.AddChild(child); } @@ -373,12 +363,12 @@ public virtual void MergeWith(IMergeable obj) } } - public virtual FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) => CalendarComponents.FreeBusy.Create(this, freeBusyRequest); + public FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) => CalendarComponents.FreeBusy.Create(this, freeBusyRequest); - public virtual FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) + public FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) => CalendarComponents.FreeBusy.Create(this, CalendarComponents.FreeBusy.CreateRequest(fromInclusive, toExclusive, null, null)); - public virtual FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) + public FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) => CalendarComponents.FreeBusy.Create(this, CalendarComponents.FreeBusy.CreateRequest(fromInclusive, toExclusive, organizer, contacts)); /// diff --git a/src/Ical.Net/CalendarCollection.cs b/src/Ical.Net/CalendarCollection.cs index 2e42be37d..d21c4b96b 100644 --- a/src/Ical.Net/CalendarCollection.cs +++ b/src/Ical.Net/CalendarCollection.cs @@ -10,9 +10,7 @@ namespace Ical.Net { - /// - /// A list of iCalendars. - /// + /// A list of iCalendars that can be loaded from a . public class CalendarCollection : List { public static CalendarCollection Load(string iCalendarString) @@ -21,14 +19,14 @@ public static CalendarCollection Load(string iCalendarString) /// /// Loads an from an open stream. /// - /// The stream from which to load the object + /// The stream from which to load the object /// An object - public static CalendarCollection Load(Stream s) - => Load(new StreamReader(s, Encoding.UTF8)); + public static CalendarCollection Load(Stream stream) + => Load(new StreamReader(stream, Encoding.UTF8)); - public static CalendarCollection Load(TextReader tr) + public static CalendarCollection Load(TextReader reader) { - var calendars = SimpleDeserializer.Default.Deserialize(tr).OfType(); + var calendars = SimpleDeserializer.Default.Deserialize(reader).OfType(); var collection = new CalendarCollection(); collection.AddRange(calendars); return collection; @@ -122,26 +120,17 @@ public HashSet GetOccurrences(DateTime startTime, DateTime endTim return occurrences; } - private FreeBusy CombineFreeBusy(FreeBusy main, FreeBusy current) + static FreeBusy CombineFreeBusy(FreeBusy main, FreeBusy current) { main?.MergeWith(current); return current; } - public FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) - { - return this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(freeBusyRequest))); - } + public FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) => this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(freeBusyRequest))); - public FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) - { - return this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(fromInclusive, toExclusive))); - } + public FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) => this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(fromInclusive, toExclusive))); - public FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) - { - return this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(organizer, contacts, fromInclusive, toExclusive))); - } + public FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) => this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(organizer, contacts, fromInclusive, toExclusive))); public override int GetHashCode() => CollectionHelpers.GetHashCode(this); diff --git a/src/Ical.Net/CalendarComponents/Alarm.cs b/src/Ical.Net/CalendarComponents/Alarm.cs index e82d04299..8d860e208 100644 --- a/src/Ical.Net/CalendarComponents/Alarm.cs +++ b/src/Ical.Net/CalendarComponents/Alarm.cs @@ -11,55 +11,55 @@ namespace Ical.Net.CalendarComponents public class Alarm : CalendarComponent { //ToDo: Implement IEquatable - public virtual string Action + public string Action { get => Properties.Get(AlarmAction.Key); set => Properties.Set(AlarmAction.Key, value); } - public virtual Attachment Attachment + public Attachment Attachment { get => Properties.Get("ATTACH"); set => Properties.Set("ATTACH", value); } - public virtual IList Attendees + public IList Attendees { get => Properties.GetMany("ATTENDEE"); set => Properties.Set("ATTENDEE", value); } - public virtual string Description + public string Description { get => Properties.Get("DESCRIPTION"); set => Properties.Set("DESCRIPTION", value); } - public virtual TimeSpan Duration + public TimeSpan Duration { get => Properties.Get("DURATION"); set => Properties.Set("DURATION", value); } - public virtual int Repeat + public int Repeat { get => Properties.Get("REPEAT"); set => Properties.Set("REPEAT", value); } - public virtual string Summary + public string Summary { get => Properties.Get("SUMMARY"); set => Properties.Set("SUMMARY", value); } - public virtual Trigger Trigger + public Trigger? Trigger { get => Properties.Get(TriggerRelation.Key); set => Properties.Set(TriggerRelation.Key, value); } - protected virtual IList Occurrences { get; set; } + protected IList Occurrences { get; set; } public Alarm() { @@ -71,7 +71,7 @@ public Alarm() /// Gets a list of alarm occurrences for the given recurring component, /// that occur between and . /// - public virtual IList GetOccurrences(IRecurringComponent rc, IDateTime fromDate, IDateTime toDate) + public IList GetOccurrences(IRecurringComponent rc, IDateTime? fromDate, IDateTime? toDate) { Occurrences.Clear(); @@ -91,7 +91,7 @@ public virtual IList GetOccurrences(IRecurringComponent rc, IDa fromDate = rc.Start.Copy(); } - var d = default(TimeSpan); + TimeSpan? d = default; foreach (var o in rc.GetOccurrences(fromDate, toDate)) { var dt = o.Period.StartTime; @@ -100,20 +100,20 @@ public virtual IList GetOccurrences(IRecurringComponent rc, IDa if (o.Period.EndTime != null) { dt = o.Period.EndTime; - if (d == default(TimeSpan)) + if (d == default) { d = o.Period.Duration; } } // Use the "last-found" duration as a reference point - else if (d != default(TimeSpan)) + else if (d != default) { - dt = o.Period.StartTime.Add(d); + dt = o.Period.StartTime.Add(d.Value); } else { throw new ArgumentException( - "Alarm trigger is relative to the START of the occurrence; however, the occurence has no discernible end."); + "Alarm trigger is relative to the START of the occurrence; however, the occurrence has no discernible end."); } } @@ -136,18 +136,16 @@ public virtual IList GetOccurrences(IRecurringComponent rc, IDa /// /// Polls the component for alarms that have been triggered - /// since the provided date/time. If - /// is null, all triggered alarms will be returned. + /// between and date/time. + /// If is null, all triggered alarms will be returned. /// - /// The earliest date/time to poll trigered alarms for. /// A list of objects, each containing a triggered alarm. - public virtual IList Poll(IDateTime start, IDateTime end) + public IList Poll(IDateTime? start, IDateTime? end) { var results = new List(); // Evaluate the alarms to determine the recurrences - var rc = Parent as RecurringComponent; - if (rc == null) + if (!(Parent is RecurringComponent rc)) { return results; } diff --git a/src/Ical.Net/CalendarComponents/CalendarComponent.cs b/src/Ical.Net/CalendarComponents/CalendarComponent.cs index f9d1de653..720d899e2 100644 --- a/src/Ical.Net/CalendarComponents/CalendarComponent.cs +++ b/src/Ical.Net/CalendarComponents/CalendarComponent.cs @@ -13,9 +13,9 @@ public class CalendarComponent : CalendarObject, ICalendarComponent /// /// Returns a list of properties that are associated with the iCalendar object. /// - public virtual CalendarPropertyList Properties { get; protected set; } + public CalendarPropertyList Properties { get; protected set; } - public CalendarComponent() : base() + public CalendarComponent() { Initialize(); } @@ -25,10 +25,7 @@ public CalendarComponent(string name) : base(name) Initialize(); } - private void Initialize() - { - Properties = new CalendarPropertyList(this); - } + void Initialize() => Properties = new CalendarPropertyList(this); protected override void OnDeserializing(StreamingContext context) { @@ -41,8 +38,7 @@ public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var c = obj as ICalendarComponent; - if (c == null) + if (!(obj is ICalendarComponent c)) { return; } @@ -57,7 +53,7 @@ public override void CopyFrom(ICopyable obj) /// /// Adds a property to this component. /// - public virtual void AddProperty(string name, string value) + public void AddProperty(string name, string value) { var p = new CalendarProperty(name, value); AddProperty(p); @@ -66,7 +62,7 @@ public virtual void AddProperty(string name, string value) /// /// Adds a property to this component. /// - public virtual void AddProperty(ICalendarProperty p) + public void AddProperty(ICalendarProperty p) { p.Parent = this; Properties.Set(p.Name, p.Value); diff --git a/src/Ical.Net/CalendarComponents/CalendarComponents.cd b/src/Ical.Net/CalendarComponents/CalendarComponents.cd new file mode 100644 index 000000000..13417af7f --- /dev/null +++ b/src/Ical.Net/CalendarComponents/CalendarComponents.cd @@ -0,0 +1,192 @@ + + + + + + IACCAAAQAAAiABAAAAAABAAAAAAACBAAAAIAAAAAAAQ= + CalendarComponents\Alarm.cs + + + + + + AAAAAAAAAAQAASAAAAAAAAAAAAAAAAAIAAAAAAACAAA= + CalendarComponents\CalendarComponent.cs + + + + + + + + + AAJAAAEEIgUCAQQAkAIAAAQAAACAAIQAAAARAAAAIAA= + CalendarComponents\CalendarEvent.cs + + + + + + + AAIAAAAAAiEAACAkAAAAAAAAAAAgAEAAAAAAAAAAAAA= + CalendarComponents\FreeBusy.cs + + + + + + + AAAAAAAEAAAAAAAAgAAAAAAAAAAAAIAAAAAAAAAAAAA= + CalendarComponents\Journal.cs + + + + + + + + + + + + + + + + BACAAAAIAiQgCQEloIIhAAACAAAAEJQCAIBAAAEAAAA= + CalendarComponents\RecurringComponent.cs + + + + + + + + + + + + + AIAAAAAEIgACAQIAEAIAAAAAAAAAABAAAAAQIAAAMAA= + CalendarComponents\Todo.cs + + + + + + + AAAAAAEIIAAAAAQCgBAABAAAAAAIAIAACAAAAAAAIAA= + CalendarComponents\UniqueComponent.cs + + + + + + + AAAAAAAgABAAAIAAoEIBAAAAAAQAAIAgCiAAAAAIAAA= + CalendarComponents\VTimeZone.cs + + + + + + AAAAAFEAAEQAASAAgABEQAQABAAAAIAEAAAAAAABAEQ= + CalendarObject.cs + + + + + + + AAAAAAAAAAEAACAIQAAAAAAAAgAAAAAAAAAAAAAAAAA= + CalendarObjectBase.cs + + + + + + + AEIABEAAAAAAADAUiAABBAQAAAAAAIAECAQAAABQIAA= + DataTypes\PeriodList.cs + + + + + + + gIAwAAAAAIAASCAEkIAAIEQAAIABAIAAAQAsAAAgAAQ= + DataTypes\RecurrencePattern.cs + + + + + + + + AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAA= + CalendarComponents\IAlarmContainer.cs + + + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CalendarComponents\ICalendarComponent.cs + + + + + + AAAAAAAAACAAAAAgIIAgAAAAAAAAAAACAAAAAAAAAAA= + CalendarComponents\IRecurrable.cs + + + + + + BACAAAAAAAAgAAEEAAIAAAACAAAAEAQAAAAAAAEAAAA= + CalendarComponents\IRecurringComponent.cs + + + + + + AAAAAAAAIAAAAAACABAABAAAAAAIAAAACAAAAAAAIAA= + CalendarComponents\IUniqueComponent.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA= + ICalendarPropertyListContainer.cs + + + + \ No newline at end of file diff --git a/src/Ical.Net/CalendarComponents/CalendarEvent.cs b/src/Ical.Net/CalendarComponents/CalendarEvent.cs index fbdb0d35c..603333c3f 100644 --- a/src/Ical.Net/CalendarComponents/CalendarEvent.cs +++ b/src/Ical.Net/CalendarComponents/CalendarEvent.cs @@ -58,7 +58,7 @@ public override IDateTime DtStart /// will be extrapolated. /// /// - public virtual IDateTime DtEnd + public IDateTime DtEnd { get => Properties.Get("DTEND"); set @@ -92,7 +92,7 @@ public virtual IDateTime DtEnd // // Therefore, Duration is not serialized, as DtEnd // should always be extrapolated from the duration. - public virtual TimeSpan Duration + public TimeSpan Duration { get => Properties.Get("DURATION"); set @@ -108,7 +108,7 @@ public virtual TimeSpan Duration /// /// An alias to the DtEnd field (i.e. end date/time). /// - public virtual IDateTime End + public IDateTime End { get => DtEnd; set => DtEnd = value; @@ -117,7 +117,7 @@ public virtual IDateTime End /// /// Returns true if the event is an all-day event. /// - public virtual bool IsAllDay + public bool IsAllDay { get => !Start.HasTime; set @@ -135,7 +135,7 @@ public virtual bool IsAllDay if (value && Start != null && End != null && Equals(Start.Date, End.Date)) { - Duration = default(TimeSpan); + Duration = default; End = Start.AddDays(1); } } @@ -164,7 +164,7 @@ public string Location /// Conference room #2 /// Projector /// - public virtual IList Resources + public IList Resources { get => Properties.GetMany("RESOURCES"); set => Properties.Set("RESOURCES", value ?? new List()); @@ -192,7 +192,7 @@ public string Transparency set => Properties.Set(TransparencyType.Key, value); } - private EventEvaluator _mEvaluator; + EventEvaluator _mEvaluator; /// /// Constructs an Event object, with an iCalObject @@ -203,7 +203,7 @@ public CalendarEvent() Initialize(); } - private void Initialize() + void Initialize() { Name = EventStatus.Name; @@ -220,32 +220,24 @@ private void Initialize() /// /// The date to test. /// True if the event occurs on the provided, False otherwise. - public virtual bool OccursOn(IDateTime dateTime) - { - return _mEvaluator.Periods.Any(p => p.StartTime.Date == dateTime.Date || // It's the start date OR - (p.StartTime.Date <= dateTime.Date && // It's after the start date AND - (p.EndTime.HasTime && p.EndTime.Date >= dateTime.Date || // an end time was specified, and it's after the test date - (!p.EndTime.HasTime && p.EndTime.Date > dateTime.Date)))); - } + public bool OccursOn(IDateTime dateTime) => _mEvaluator.Periods.Any(p => p.StartTime.Date == dateTime.Date || // It's the start date OR + (p.StartTime.Date <= dateTime.Date && // It's after the start date AND + (p.EndTime.HasTime && p.EndTime.Date >= dateTime.Date || // an end time was specified, and it's after the test date + (!p.EndTime.HasTime && p.EndTime.Date > dateTime.Date)))); /// /// Use this method to determine if an event begins at a given date and time. /// /// The date and time to test. /// True if the event begins at the given date and time - public virtual bool OccursAt(IDateTime dateTime) - { - return _mEvaluator.Periods.Any(p => p.StartTime.Equals(dateTime)); - } + public bool OccursAt(IDateTime dateTime) => _mEvaluator.Periods.Any(p => p.StartTime.Equals(dateTime)); /// /// Determines whether or not the is actively displayed /// as an upcoming or occurred event. /// /// True if the event has not been cancelled, False otherwise. - public virtual bool IsActive => !string.Equals(Status, EventStatus.Cancelled, EventStatus.Comparison); - - protected override bool EvaluationIncludesReferenceDate => true; + public bool IsActive => !string.Equals(Status, EventStatus.Cancelled, EventStatus.Comparison); protected override void OnDeserializing(StreamingContext context) { @@ -261,7 +253,7 @@ protected override void OnDeserialized(StreamingContext context) ExtrapolateTimes(-1); } - private void ExtrapolateTimes(int source) + void ExtrapolateTimes(int source) { /* * Source values, a fix introduced to prevent stack overflow exceptions from occuring. @@ -270,15 +262,15 @@ private void ExtrapolateTimes(int source) * 1 = Duration * 2 = DtStart */ - if (DtEnd == null && DtStart != null && Duration != default(TimeSpan) && source != 0) + if (DtEnd == null && DtStart != null && Duration != default && source != 0) { DtEnd = DtStart.Add(Duration); } - else if (Duration == default(TimeSpan) && DtStart != null && DtEnd != null && source != 1) + else if (Duration == default && DtStart != null && DtEnd != null && source != 1) { Duration = DtEnd.Subtract(DtStart); } - else if (DtStart == null && Duration != default(TimeSpan) && DtEnd != null && source != 2) + else if (DtStart == null && Duration != default && DtEnd != null && source != 2) { DtStart = DtEnd.Subtract(Duration); } @@ -299,7 +291,7 @@ protected bool Equals(CalendarEvent other) && string.Equals(Status, other.Status, StringComparison.Ordinal) && IsActive == other.IsActive && string.Equals(Transparency, other.Transparency, TransparencyType.Comparison) - && EvaluationIncludesReferenceDate == other.EvaluationIncludesReferenceDate + && IncludeReferenceDate == other.IncludeReferenceDate && Attachments.SequenceEqual(other.Attachments) && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules); diff --git a/src/Ical.Net/CalendarComponents/FreeBusy.cs b/src/Ical.Net/CalendarComponents/FreeBusy.cs index 6568793d5..85eb4435c 100644 --- a/src/Ical.Net/CalendarComponents/FreeBusy.cs +++ b/src/Ical.Net/CalendarComponents/FreeBusy.cs @@ -10,11 +10,10 @@ public class FreeBusy : UniqueComponent, IMergeable { public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest) { - if (!(obj is IGetOccurrencesTyped)) + if (!(obj is IGetOccurrencesTyped getOccurrences)) { return null; } - var getOccurrences = (IGetOccurrencesTyped) obj; var occurrences = getOccurrences.GetOccurrences(freeBusyRequest.Start, freeBusyRequest.End); var contacts = new List(); var isFilteredByAttendees = false; @@ -35,9 +34,7 @@ public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest) foreach (var o in occurrences) { - var uc = o.Source as IUniqueComponent; - - if (uc == null) + if (!(o.Source is IUniqueComponent uc)) { continue; } @@ -122,37 +119,37 @@ public FreeBusy() Name = Components.Freebusy; } - public virtual IList Entries + public IList Entries { get => Properties.GetMany("FREEBUSY"); set => Properties.Set("FREEBUSY", value); } - public virtual IDateTime DtStart + public IDateTime DtStart { get => Properties.Get("DTSTART"); set => Properties.Set("DTSTART", value); } - public virtual IDateTime DtEnd + public IDateTime DtEnd { get => Properties.Get("DTEND"); set => Properties.Set("DTEND", value); } - public virtual IDateTime Start + public IDateTime Start { get => Properties.Get("DTSTART"); set => Properties.Set("DTSTART", value); } - public virtual IDateTime End + public IDateTime End { get => Properties.Get("DTEND"); set => Properties.Set("DTEND", value); } - public virtual FreeBusyStatus GetFreeBusyStatus(Period period) + public FreeBusyStatus GetFreeBusyStatus(Period period) { var status = FreeBusyStatus.Free; if (period == null) @@ -167,7 +164,7 @@ public virtual FreeBusyStatus GetFreeBusyStatus(Period period) return status; } - public virtual FreeBusyStatus GetFreeBusyStatus(IDateTime dt) + public FreeBusyStatus GetFreeBusyStatus(IDateTime dt) { var status = FreeBusyStatus.Free; if (dt == null) @@ -182,7 +179,7 @@ public virtual FreeBusyStatus GetFreeBusyStatus(IDateTime dt) return status; } - public virtual void MergeWith(IMergeable obj) + public void MergeWith(IMergeable obj) { if (!(obj is FreeBusy fb)) { diff --git a/src/Ical.Net/CalendarComponents/IUniqueComponent.cs b/src/Ical.Net/CalendarComponents/IUniqueComponent.cs index ac0004ded..6263bd04f 100644 --- a/src/Ical.Net/CalendarComponents/IUniqueComponent.cs +++ b/src/Ical.Net/CalendarComponents/IUniqueComponent.cs @@ -4,8 +4,10 @@ namespace Ical.Net.CalendarComponents { + /// Calendar-Component with a public interface IUniqueComponent : ICalendarComponent { + /// Unique identifier for this string Uid { get; set; } IList Attendees { get; set; } diff --git a/src/Ical.Net/CalendarComponents/Journal.cs b/src/Ical.Net/CalendarComponents/Journal.cs index 735d4f817..b28138eed 100644 --- a/src/Ical.Net/CalendarComponents/Journal.cs +++ b/src/Ical.Net/CalendarComponents/Journal.cs @@ -22,13 +22,6 @@ public Journal() Name = JournalStatus.Name; } - protected override bool EvaluationIncludesReferenceDate => true; - - protected override void OnDeserializing(StreamingContext context) - { - base.OnDeserializing(context); - } - protected bool Equals(Journal other) => Start.Equals(other.Start) && Equals(other as RecurringComponent); public override bool Equals(object obj) diff --git a/src/Ical.Net/CalendarComponents/RecurringComponent.cs b/src/Ical.Net/CalendarComponents/RecurringComponent.cs index 218329264..20ea64e53 100644 --- a/src/Ical.Net/CalendarComponents/RecurringComponent.cs +++ b/src/Ical.Net/CalendarComponents/RecurringComponent.cs @@ -9,12 +9,11 @@ namespace Ical.Net.CalendarComponents { - /// - /// An iCalendar component that recurs. - /// + /// iCalendar component that contains multiple s in and . /// + /// All s have individual End Dates, but share the common Date. /// This component automatically handles - /// RRULEs, RDATE, EXRULEs, and EXDATEs, as well as the DTSTART + /// RRULEs, RDATE, EXRULEs, and EXDATEs, as well as the DTSTART /// for the recurring item (all recurring items must have a DTSTART). /// public class RecurringComponent : UniqueComponent, IRecurringComponent @@ -23,117 +22,140 @@ public class RecurringComponent : UniqueComponent, IRecurringComponent public static IEnumerable SortByDate(IEnumerable list) => list.OrderBy(d => d); - protected virtual bool EvaluationIncludesReferenceDate => false; + public bool IncludeReferenceDate { get; set; } = true; - public virtual IList Attachments + public IList Attachments { get => Properties.GetMany("ATTACH"); set => Properties.Set("ATTACH", value); } - public virtual IList Categories + /// AKA Tags; a List of Category Names + public IList Categories { get => Properties.GetMany("CATEGORIES"); set => Properties.Set("CATEGORIES", value); } - public virtual string Class + /// String for the distinguished 'class' in the + public string Class { get => Properties.Get("CLASS"); set => Properties.Set("CLASS", value); } - public virtual IList Contacts + public IList Contacts { get => Properties.GetMany("CONTACT"); set => Properties.Set("CONTACT", value); } - public virtual IDateTime Created + public IDateTime Created { get => Properties.Get("CREATED"); set => Properties.Set("CREATED", value); } - public virtual string Description + /// Longer than the + public string Description { get => Properties.Get("DESCRIPTION"); set => Properties.Set("DESCRIPTION", value); } - /// - /// The start date/time of the component. - /// + /// DTSTART; The start date/time of the component. + /// common to all and public virtual IDateTime DtStart { get => Properties.Get("DTSTART"); set => Properties.Set("DTSTART", value); } - public virtual IList ExceptionDates + /// Additional explicit exempted Dates + /// + /// Opposite to + /// + public IList ExceptionDates { get => Properties.GetMany("EXDATE"); set => Properties.Set("EXDATE", value); } - public virtual IList ExceptionRules + /// periodic Exception-Dates + /// + /// Opposite to . + /// According to RFC-5545 each Component should have only a single , + /// otherwise the Schedule is undefined! + /// + public IList ExceptionRules { get => Properties.GetMany("EXRULE"); set => Properties.Set("EXRULE", value); } - public virtual IDateTime LastModified + public IDateTime LastModified { get => Properties.Get("LAST-MODIFIED"); set => Properties.Set("LAST-MODIFIED", value); } - public virtual int Priority + public int Priority { get => Properties.Get("PRIORITY"); set => Properties.Set("PRIORITY", value); } - public virtual IList RecurrenceDates + /// Additional explicit Recurrence Dates + /// + /// Opposite to + /// + public IList RecurrenceDates { get => Properties.GetMany("RDATE"); set => Properties.Set("RDATE", value); } - public virtual IList RecurrenceRules + /// periodic Rules-Dates + /// + /// Opposite to . + /// According to RFC-5545 each Component should have only a single , + /// otherwise the Schedule is undefined! + /// + public IList RecurrenceRules { get => Properties.GetMany("RRULE"); set => Properties.Set("RRULE", value); } - public virtual IDateTime RecurrenceId + public IDateTime RecurrenceId { get => Properties.Get("RECURRENCE-ID"); set => Properties.Set("RECURRENCE-ID", value); } - public virtual IList RelatedComponents + public IList RelatedComponents { get => Properties.GetMany("RELATED-TO"); set => Properties.Set("RELATED-TO", value); } - public virtual int Sequence + public int Sequence { get => Properties.Get("SEQUENCE"); set => Properties.Set("SEQUENCE", value); } - /// - /// An alias to the DTStart field (i.e. start date/time). - /// - public virtual IDateTime Start + /// DTSTART; An alias to the field (i.e. start date/time). + public IDateTime Start { get => DtStart; set => DtStart = value; } - public virtual string Summary + /// Summary of the long + public string Summary { get => Properties.Get("SUMMARY"); set => Properties.Set("SUMMARY", value); @@ -142,7 +164,7 @@ public virtual string Summary /// /// A list of s for this recurring component. /// - public virtual ICalendarObjectList Alarms => new CalendarObjectListProxy(Children); + public ICalendarObjectList Alarms => new CalendarObjectListProxy(Children); public RecurringComponent() { @@ -156,9 +178,9 @@ public RecurringComponent(string name) : base(name) EnsureProperties(); } - private void Initialize() => SetService(new RecurringEvaluator(this)); + void Initialize() => SetService(new RecurringEvaluator(this)); - private void EnsureProperties() + void EnsureProperties() { if (!Properties.ContainsKey("SEQUENCE")) { @@ -173,24 +195,23 @@ protected override void OnDeserializing(StreamingContext context) Initialize(); } - public virtual void ClearEvaluation() => RecurrenceUtil.ClearEvaluation(this); + public void ClearEvaluation() => RecurrenceUtil.ClearEvaluation(this); - public virtual HashSet GetOccurrences(IDateTime dt) => RecurrenceUtil.GetOccurrences(this, dt, EvaluationIncludesReferenceDate); + public HashSet GetOccurrences(IDateTime dt) => this.GetOccurrences(dt, IncludeReferenceDate); - public virtual HashSet GetOccurrences(DateTime dt) - => RecurrenceUtil.GetOccurrences(this, new CalDateTime(dt), EvaluationIncludesReferenceDate); + public HashSet GetOccurrences(DateTime dt) + => this.GetOccurrences(new CalDateTime(dt), IncludeReferenceDate); - public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) - => RecurrenceUtil.GetOccurrences(this, startTime, endTime, EvaluationIncludesReferenceDate); + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + => this.GetOccurrences(startTime, endTime, IncludeReferenceDate); - public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) - => RecurrenceUtil.GetOccurrences(this, new CalDateTime(startTime), new CalDateTime(endTime), EvaluationIncludesReferenceDate); + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) + => this.GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime), IncludeReferenceDate); - public virtual IList PollAlarms() => PollAlarms(null, null); + public IList PollAlarms() => PollAlarms(null, null); - public virtual IList PollAlarms(IDateTime startTime, IDateTime endTime) - => Alarms?.SelectMany(a => a.Poll(startTime, endTime)).ToList() - ?? new List(); + public IList PollAlarms(IDateTime? startTime, IDateTime? endTime) + => Alarms.SelectMany(a => a.Poll(startTime, endTime)).ToList(); protected bool Equals(RecurringComponent other) { diff --git a/src/Ical.Net/CalendarComponents/Todo.cs b/src/Ical.Net/CalendarComponents/Todo.cs index 1d0973948..7c3575f05 100644 --- a/src/Ical.Net/CalendarComponents/Todo.cs +++ b/src/Ical.Net/CalendarComponents/Todo.cs @@ -14,12 +14,12 @@ namespace Ical.Net.CalendarComponents [DebuggerDisplay("{Summary} - {Status}")] public class Todo : RecurringComponent, IAlarmContainer { - private readonly TodoEvaluator _mEvaluator; + readonly TodoEvaluator _mEvaluator; /// /// The date/time the todo was completed. /// - public virtual IDateTime Completed + public IDateTime Completed { get => Properties.Get("COMPLETED"); set => Properties.Set("COMPLETED", value); @@ -41,7 +41,7 @@ public override IDateTime DtStart /// /// The due date of the todo item. /// - public virtual IDateTime Due + public IDateTime Due { get => Properties.Get("DUE"); set @@ -64,7 +64,7 @@ public virtual IDateTime Due // // Therefore, Duration is not serialized, as Due // should always be extrapolated from the duration. - public virtual TimeSpan Duration + public TimeSpan Duration { get => Properties.Get("DURATION"); set @@ -74,25 +74,25 @@ public virtual TimeSpan Duration } } - public virtual GeographicLocation GeographicLocation + public GeographicLocation GeographicLocation { get => Properties.Get("GEO"); set => Properties.Set("GEO", value); } - public virtual string Location + public string Location { get => Properties.Get("LOCATION"); set => Properties.Set("LOCATION", value); } - public virtual int PercentComplete + public int PercentComplete { get => Properties.Get("PERCENT-COMPLETE"); set => Properties.Set("PERCENT-COMPLETE", value); } - public virtual IList Resources + public IList Resources { get => Properties.GetMany("RESOURCES"); set => Properties.Set("RESOURCES", value ?? new List()); @@ -101,7 +101,7 @@ public virtual IList Resources /// /// The status of the todo item. /// - public virtual string Status + public string Status { get => Properties.Get(TodoStatus.Key); set @@ -145,7 +145,7 @@ public Todo() /// /// /// True if the todo item has been completed - public virtual bool IsCompleted(IDateTime currDt) + public bool IsCompleted(IDateTime currDt) { if (Status == TodoStatus.Completed) { @@ -157,7 +157,7 @@ public virtual bool IsCompleted(IDateTime currDt) // Evaluate to the previous occurrence. _mEvaluator.EvaluateToPreviousOccurrence(Completed, currDt); - return _mEvaluator.Periods.Cast().All(p => !p.StartTime.GreaterThan(Completed) || !currDt.GreaterThanOrEqual(p.StartTime)); + return _mEvaluator.Periods.All(p => !p.StartTime.GreaterThan(Completed) || !currDt.GreaterThanOrEqual(p.StartTime)); } return false; } @@ -168,7 +168,7 @@ public virtual bool IsCompleted(IDateTime currDt) /// /// The date and time to test. /// True if the item is Active as of , False otherwise. - public virtual bool IsActive(IDateTime currDt) + public bool IsActive(IDateTime currDt) => (DtStart == null || currDt.GreaterThanOrEqual(DtStart)) && (!IsCompleted(currDt) && !IsCancelled); @@ -176,17 +176,9 @@ public virtual bool IsActive(IDateTime currDt) /// Returns True if the todo item was cancelled. /// /// True if the todo was cancelled, False otherwise. - public virtual bool IsCancelled => string.Equals(Status, TodoStatus.Cancelled, TodoStatus.Comparison); + public bool IsCancelled => string.Equals(Status, TodoStatus.Cancelled, TodoStatus.Comparison); - protected override bool EvaluationIncludesReferenceDate => true; - - protected override void OnDeserializing(StreamingContext context) - { - //ToDo: a necessary evil, for now - base.OnDeserializing(context); - } - - private void ExtrapolateTimes(int source) + void ExtrapolateTimes(int source) { /* * Source values, a fix introduced to prevent StackOverflow exceptions from occuring. @@ -194,15 +186,15 @@ private void ExtrapolateTimes(int source) * 1 = Duration * 2 = DtStart */ - if (Due == null && DtStart != null && Duration != default(TimeSpan) && source != 0) + if (Due == null && DtStart != null && Duration != default && source != 0) { Due = DtStart.Add(Duration); } - else if (Duration == default(TimeSpan) && DtStart != null && Due != null && source != 1) + else if (Duration == default && DtStart != null && Due != null && source != 1) { Duration = Due.Subtract(DtStart); } - else if (DtStart == null && Duration != default(TimeSpan) && Due != null && source != 2) + else if (DtStart == null && Duration != default && Due != null && source != 2) { DtStart = Due.Subtract(Duration); } diff --git a/src/Ical.Net/CalendarComponents/UniqueComponent.cs b/src/Ical.Net/CalendarComponents/UniqueComponent.cs index 305681aec..56793319d 100644 --- a/src/Ical.Net/CalendarComponents/UniqueComponent.cs +++ b/src/Ical.Net/CalendarComponents/UniqueComponent.cs @@ -28,7 +28,7 @@ public UniqueComponent(string name) : base(name) EnsureProperties(); } - private void EnsureProperties() + void EnsureProperties() { if (string.IsNullOrEmpty(Uid)) { @@ -46,37 +46,37 @@ private void EnsureProperties() } } - public virtual IList Attendees + public IList Attendees { get => Properties.GetMany("ATTENDEE"); set => Properties.Set("ATTENDEE", value); } - public virtual IList Comments + public IList Comments { get => Properties.GetMany("COMMENT"); set => Properties.Set("COMMENT", value); } - public virtual IDateTime DtStamp + public IDateTime DtStamp { get => Properties.Get("DTSTAMP"); set => Properties.Set("DTSTAMP", value); } - public virtual Organizer Organizer + public Organizer Organizer { get => Properties.Get("ORGANIZER"); set => Properties.Set("ORGANIZER", value); } - public virtual IList RequestStatuses + public IList RequestStatuses { get => Properties.GetMany("REQUEST-STATUS"); set => Properties.Set("REQUEST-STATUS", value); } - public virtual Uri Url + public Uri Url { get => Properties.Get("URL"); set => Properties.Set("URL", value); @@ -94,21 +94,20 @@ public int CompareTo(UniqueComponent other) public override bool Equals(object obj) { - if (obj is RecurringComponent && obj != this) + if (obj is RecurringComponent o && o != this) { - var r = (RecurringComponent) obj; if (Uid != null) { - return Uid.Equals(r.Uid); + return Uid.Equals(o.Uid); } - return Uid == r.Uid; + return Uid == o.Uid; } return base.Equals(obj); } public override int GetHashCode() => Uid?.GetHashCode() ?? base.GetHashCode(); - public virtual string Uid + public string Uid { get => Properties.Get("UID"); set => Properties.Set("UID", value); diff --git a/src/Ical.Net/CalendarComponents/VTimeZone.cs b/src/Ical.Net/CalendarComponents/VTimeZone.cs index 08aff0d06..1d9543531 100644 --- a/src/Ical.Net/CalendarComponents/VTimeZone.cs +++ b/src/Ical.Net/CalendarComponents/VTimeZone.cs @@ -117,7 +117,7 @@ public static VTimeZone FromDateTimeZone(string tzId, DateTime earlistDateTimeTo return vTimeZone; } - private static VTimeZoneInfo CreateTimeZoneInfo(List matchedIntervals, List intervals, bool isRRule = true, + static VTimeZoneInfo CreateTimeZoneInfo(List matchedIntervals, List intervals, bool isRRule = true, bool isOnlyInterval = false) { if (matchedIntervals == null || !matchedIntervals.Any()) @@ -180,7 +180,7 @@ private static VTimeZoneInfo CreateTimeZoneInfo(List matchedInterv return timeZoneInfo; } - private static List GetMatchingIntervals(List intervals, ZoneInterval intervalToMatch, bool consecutiveOnly = false) + static List GetMatchingIntervals(List intervals, ZoneInterval intervalToMatch, bool consecutiveOnly = false) { var matchedIntervals = intervals .Where(x => x.Start != Instant.MinValue) @@ -221,7 +221,7 @@ private static List GetMatchingIntervals(List interv return consecutiveIntervals; } - private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List intervals, TimeSpan delta) + static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List intervals, TimeSpan delta) { foreach (var interval in intervals) { @@ -239,13 +239,13 @@ private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List< } } - private static void PopulateTimeZoneInfoRecurrenceRules(VTimeZoneInfo tzi, ZoneInterval interval) + static void PopulateTimeZoneInfoRecurrenceRules(VTimeZoneInfo tzi, ZoneInterval interval) { var recurrence = new IntervalRecurrencePattern(interval); tzi.RecurrenceRules.Add(recurrence); } - private class IntervalRecurrencePattern : RecurrencePattern + class IntervalRecurrencePattern : RecurrencePattern { public IntervalRecurrencePattern(ZoneInterval interval) { @@ -254,9 +254,9 @@ public IntervalRecurrencePattern(ZoneInterval interval) var date = interval.IsoLocalStart.ToDateTimeUnspecified(); var weekday = date.DayOfWeek; - var num = DateUtil.WeekOfMonth(date); + var num = date.WeekOfMonth(); - ByDay.Add(num != 5 ? new WeekDay(weekday, num) : new WeekDay(weekday, -1)); + ByWeekDay.Add(num != 5 ? new WeekDay(weekday, num) : new WeekDay(weekday, -1)); } } @@ -277,9 +277,9 @@ public VTimeZone(string tzId) : this() Location = _nodaZone.Id; } - private DateTimeZone _nodaZone; - private string _tzId; - public virtual string TzId + DateTimeZone _nodaZone; + string _tzId; + public string TzId { get { @@ -320,8 +320,8 @@ public virtual string TzId } } - private Uri _url; - public virtual Uri Url + Uri _url; + public Uri Url { get => _url ?? (_url = Properties.Get("TZURL")); set @@ -331,7 +331,7 @@ public virtual Uri Url } } - private string _location; + string _location; public string Location { get => _location ?? (_location = Properties.Get("X-LIC-LOCATION")); @@ -353,7 +353,7 @@ public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; + if (obj.GetType() != GetType()) return false; return Equals((VTimeZone)obj); } diff --git a/src/Ical.Net/CalendarObject.cs b/src/Ical.Net/CalendarObject.cs index c5b4f3823..06cfb6732 100644 --- a/src/Ical.Net/CalendarObject.cs +++ b/src/Ical.Net/CalendarObject.cs @@ -4,13 +4,10 @@ namespace Ical.Net { - /// - /// The base class for all iCalendar objects and components. - /// + /// d base class for all iCalendar objects and components. public class CalendarObject : CalendarObjectBase, ICalendarObject { - private ICalendarObjectList _children; - private ServiceProvider _serviceProvider; + ServiceProvider _serviceProvider; internal CalendarObject() { @@ -28,14 +25,16 @@ public CalendarObject(int line, int col) : this() Column = col; } - private void Initialize() + /// Instead of Constructor to support , which skips the Constructor + void Initialize() { - //ToDo: I'm fairly certain this is ONLY used for null checking. If so, maybe it can just be a bool? CalendarObjectList is an empty object, and + //ToDo: I'm fairly certain this is ONLY used for null checking. + // If so, maybe it can just be a bool? CalendarObjectList is an empty object, and //ToDo: its constructor parameter is ignored - _children = new CalendarObjectList(this); + Children = new CalendarObjectList(this); _serviceProvider = new ServiceProvider(); - _children.ItemAdded += Children_ItemAdded; + Children.ItemAdded += Children_ItemAdded; } [OnDeserializing] @@ -48,11 +47,11 @@ private void Initialize() protected virtual void OnDeserialized(StreamingContext context) {} - private void Children_ItemAdded(object sender, ObjectEventArgs e) => e.First.Parent = this; + void Children_ItemAdded(object sender, ObjectEventArgs e) => e.First.Parent = this; protected bool Equals(CalendarObject other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; @@ -63,8 +62,7 @@ public override bool Equals(object obj) public override void CopyFrom(ICopyable c) { - var obj = c as ICalendarObject; - if (obj == null) + if (!(c is ICalendarObject obj)) { return; } @@ -83,25 +81,17 @@ public override void CopyFrom(ICopyable c) } } - /// - /// Returns the parent iCalObject that owns this one. - /// - public virtual ICalendarObject Parent { get; set; } - - /// - /// A collection of iCalObjects that are children of the current object. - /// - public virtual ICalendarObjectList Children => _children; - - /// - /// Gets or sets the name of the iCalObject. For iCalendar components, this is the RFC 5545 name of the component. - /// - public virtual string Name { get; set; } - - /// - /// Returns the that this DDayiCalObject belongs to. - /// - public virtual Calendar Calendar + /// Returns the parent iCalObject that owns this one. + public ICalendarObject? Parent { get; set; } + + /// A collection of iCalObjects that are children of the current object. + public ICalendarObjectList Children { get; private set; } + + /// Gets or sets the name of the iCalObject. For iCalendar components, this is the RFC 5545 name of the component. + public string Name { get; set; } + + /// Returns the that this DDayiCalObject belongs to. + public Calendar? Calendar { get { @@ -116,27 +106,27 @@ public virtual Calendar Calendar protected set { } } - public virtual int Line { get; set; } + public int Line { get; set; } - public virtual int Column { get; set; } + public int Column { get; set; } - public virtual object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); + public object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); - public virtual object GetService(string name) => _serviceProvider.GetService(name); + public object GetService(string name) => _serviceProvider.GetService(name); - public virtual T GetService() => _serviceProvider.GetService(); + public T GetService() => _serviceProvider.GetService(); - public virtual T GetService(string name) => _serviceProvider.GetService(name); + public T GetService(string name) => _serviceProvider.GetService(name); - public virtual void SetService(string name, object obj) => _serviceProvider.SetService(name, obj); + public void SetService(string name, object obj) => _serviceProvider.SetService(name, obj); - public virtual void SetService(object obj) => _serviceProvider.SetService(obj); + public void SetService(object obj) => _serviceProvider.SetService(obj); - public virtual void RemoveService(Type type) => _serviceProvider.RemoveService(type); + public void RemoveService(Type type) => _serviceProvider.RemoveService(type); - public virtual void RemoveService(string name) => _serviceProvider.RemoveService(name); + public void RemoveService(string name) => _serviceProvider.RemoveService(name); - public virtual string Group + public string Group { get => Name; set => Name = value; diff --git a/src/Ical.Net/CalendarObjectBase.cs b/src/Ical.Net/CalendarObjectBase.cs index 70a3febe3..f11a16387 100644 --- a/src/Ical.Net/CalendarObjectBase.cs +++ b/src/Ical.Net/CalendarObjectBase.cs @@ -2,46 +2,39 @@ namespace Ical.Net { + /// Base Class for all Calendar Objects + /// + /// public class CalendarObjectBase : ICopyable, ILoadable { - private bool _mIsLoaded; - public CalendarObjectBase() { - _mIsLoaded = true; + IsLoaded = true; } - /// - /// Copies values from the target object to the - /// current object. - /// public virtual void CopyFrom(ICopyable c) {} - /// - /// Creates a copy of the object. - /// - /// The copy of the object. - public virtual T Copy() + public T Copy() { var type = GetType(); var obj = Activator.CreateInstance(type) as ICopyable; // Duplicate our values - if (obj is T) + if (obj is T obj1) { obj.CopyFrom(this); - return (T) obj; + return obj1; } - return default(T); + return default; } - public virtual bool IsLoaded => _mIsLoaded; + public bool IsLoaded { get; private set; } - public event EventHandler Loaded; + public event EventHandler? Loaded; - public virtual void OnLoaded() + public void OnLoaded() { - _mIsLoaded = true; + IsLoaded = true; Loaded?.Invoke(this, EventArgs.Empty); } } diff --git a/src/Ical.Net/CalendarObjectExtensions.cs b/src/Ical.Net/CalendarObjectExtensions.cs index e6b266a4d..c4475de70 100644 --- a/src/Ical.Net/CalendarObjectExtensions.cs +++ b/src/Ical.Net/CalendarObjectExtensions.cs @@ -2,14 +2,8 @@ { public static class CalendarObjectExtensions { - public static void AddChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject - { - obj.Children.Add(child); - } + public static void AddChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject => obj.Children.Add(child); - public static void RemoveChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject - { - obj.Children.Remove(child); - } + public static void RemoveChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject => obj.Children.Remove(child); } } \ No newline at end of file diff --git a/src/Ical.Net/CalendarParameter.cs b/src/Ical.Net/CalendarParameter.cs index 3e98de3f2..126b21846 100644 --- a/src/Ical.Net/CalendarParameter.cs +++ b/src/Ical.Net/CalendarParameter.cs @@ -10,7 +10,7 @@ namespace Ical.Net [DebuggerDisplay("{Name}={string.Join(\",\", Values)}")] public class CalendarParameter : CalendarObject, IValueObject { - private HashSet _values; + HashSet _values; public CalendarParameter() { @@ -37,10 +37,7 @@ public CalendarParameter(string name, IEnumerable values) : base(name) } } - private void Initialize() - { - _values = new HashSet(StringComparer.OrdinalIgnoreCase); - } + void Initialize() => _values = new HashSet(StringComparer.OrdinalIgnoreCase); protected override void OnDeserializing(StreamingContext context) { @@ -62,28 +59,28 @@ public override void CopyFrom(ICopyable c) _values = new HashSet(p.Values.Where(IsValidValue), StringComparer.OrdinalIgnoreCase); } - public virtual IEnumerable Values => _values; + public IEnumerable Values => _values; - public virtual bool ContainsValue(string value) => _values.Contains(value); + public bool ContainsValue(string value) => _values.Contains(value); - public virtual int ValueCount => _values?.Count ?? 0; + public int ValueCount => _values?.Count ?? 0; - public virtual void SetValue(string value) + public void SetValue(string value) { _values.Clear(); _values.Add(value); } - public virtual void SetValue(IEnumerable values) + public void SetValue(IEnumerable values) { // Remove all previous values _values.Clear(); _values.UnionWith(values.Where(IsValidValue)); } - private bool IsValidValue(string value) => !string.IsNullOrWhiteSpace(value); + bool IsValidValue(string value) => !string.IsNullOrWhiteSpace(value); - public virtual void AddValue(string value) + public void AddValue(string value) { if (!IsValidValue(value)) { @@ -92,12 +89,9 @@ public virtual void AddValue(string value) _values.Add(value); } - public virtual void RemoveValue(string value) - { - _values.Remove(value); - } + public void RemoveValue(string value) => _values.Remove(value); - public virtual string Value + public string Value { get => Values?.FirstOrDefault(); set => SetValue(value); diff --git a/src/Ical.Net/CalendarProperty.cs b/src/Ical.Net/CalendarProperty.cs index f26d22612..2c834d6e6 100644 --- a/src/Ical.Net/CalendarProperty.cs +++ b/src/Ical.Net/CalendarProperty.cs @@ -4,14 +4,12 @@ namespace Ical.Net { - /// - /// A class that represents a property of the - /// itself or one of its components. It can also represent non-standard + /// d property of the itself or one of its components. + /// + /// It can also represent non-standard /// (X-) properties of an iCalendar component, as seen with many /// applications, such as with Apple's iCal. /// X-WR-CALNAME:US Holidays - /// - /// /// Currently, the "known" properties for an iCalendar are as /// follows: /// @@ -26,12 +24,12 @@ namespace Ical.Net [DebuggerDisplay("{Name}:{Value}")] public class CalendarProperty : CalendarObject, ICalendarProperty { - private List _values = new List(); + List _values = new List(); /// /// Returns a list of parameters that are associated with the iCalendar object. /// - public virtual IParameterCollection Parameters { get; protected set; } = new ParameterList(); + public IParameterCollection Parameters { get; protected set; } = new ParameterList(); public CalendarProperty() {} @@ -47,7 +45,7 @@ public CalendarProperty(int line, int col) : base(line, col) {} /// /// Adds a parameter to the iCalendar object. /// - public virtual void AddParameter(string name, string value) + public void AddParameter(string name, string value) { var p = new CalendarParameter(name, value); Parameters.Add(p); @@ -56,17 +54,13 @@ public virtual void AddParameter(string name, string value) /// /// Adds a parameter to the iCalendar object. /// - public virtual void AddParameter(CalendarParameter p) - { - Parameters.Add(p); - } + public void AddParameter(CalendarParameter p) => Parameters.Add(p); public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var p = obj as ICalendarProperty; - if (p == null) + if (!(obj is ICalendarProperty p)) { return; } @@ -74,7 +68,7 @@ public override void CopyFrom(ICopyable obj) SetValue(p.Values); } - public virtual IEnumerable Values => _values; + public IEnumerable Values => _values; public object Value { @@ -99,11 +93,11 @@ public object Value } } - public virtual bool ContainsValue(object value) => _values.Contains(value); + public bool ContainsValue(object value) => _values.Contains(value); - public virtual int ValueCount => _values?.Count ?? 0; + public int ValueCount => _values?.Count ?? 0; - public virtual void SetValue(object value) + public void SetValue(object value) { if (_values.Count == 0) { @@ -120,7 +114,7 @@ public virtual void SetValue(object value) } } - public virtual void SetValue(IEnumerable values) + public void SetValue(IEnumerable values) { // Remove all previous values _values.Clear(); @@ -128,7 +122,7 @@ public virtual void SetValue(IEnumerable values) _values.AddRange(toAdd); } - public virtual void AddValue(object value) + public void AddValue(object value) { if (value == null) { @@ -138,7 +132,7 @@ public virtual void AddValue(object value) _values.Add(value); } - public virtual void RemoveValue(object value) + public void RemoveValue(object value) { if (value == null) { diff --git a/src/Ical.Net/CalendarPropertyList.cs b/src/Ical.Net/CalendarPropertyList.cs index 8df928c7b..28eba1b56 100644 --- a/src/Ical.Net/CalendarPropertyList.cs +++ b/src/Ical.Net/CalendarPropertyList.cs @@ -5,7 +5,7 @@ namespace Ical.Net { public class CalendarPropertyList : GroupedValueList { - private readonly ICalendarObject _mParent; + readonly ICalendarObject _mParent; public CalendarPropertyList() {} @@ -15,10 +15,7 @@ public CalendarPropertyList(ICalendarObject parent) ItemAdded += CalendarPropertyList_ItemAdded; } - private void CalendarPropertyList_ItemAdded(object sender, ObjectEventArgs e) - { - e.First.Parent = _mParent; - } + void CalendarPropertyList_ItemAdded(object sender, ObjectEventArgs e) => e.First.Parent = _mParent; public ICalendarProperty this[string name] => ContainsKey(name) ? AllOf(name).FirstOrDefault() diff --git a/src/Ical.Net/Collections/GroupedList.cs b/src/Ical.Net/Collections/GroupedList.cs index 66e02938e..1c40bc075 100644 --- a/src/Ical.Net/Collections/GroupedList.cs +++ b/src/Ical.Net/Collections/GroupedList.cs @@ -12,10 +12,10 @@ public class GroupedList : IGroupedList where TItem : class, IGroupedObject { - private readonly List> _lists = new List>(); - private readonly Dictionary> _dictionary = new Dictionary>(); + readonly List> _lists = new List>(); + readonly Dictionary> _dictionary = new Dictionary>(); - private IMultiLinkedList EnsureList(TGroup group) + IMultiLinkedList EnsureList(TGroup group) { if (group == null) { @@ -34,7 +34,7 @@ private IMultiLinkedList EnsureList(TGroup group) return list; } - private IMultiLinkedList ListForIndex(int index, out int relativeIndex) + IMultiLinkedList ListForIndex(int index, out int relativeIndex) { foreach (var list in _lists.Where(list => list.StartIndex <= index && list.ExclusiveEnd > index)) { @@ -47,12 +47,9 @@ private IMultiLinkedList ListForIndex(int index, out int relativeIndex) public event EventHandler> ItemAdded; - protected void OnItemAdded(TItem obj, int index) - { - ItemAdded?.Invoke(this, new ObjectEventArgs(obj, index)); - } + protected void OnItemAdded(TItem obj, int index) => ItemAdded?.Invoke(this, new ObjectEventArgs(obj, index)); - public virtual void Add(TItem item) + public void Add(TItem item) { if (item == null) { @@ -67,7 +64,7 @@ public virtual void Add(TItem item) OnItemAdded(item, list.StartIndex + index); } - public virtual int IndexOf(TItem item) + public int IndexOf(TItem item) { var group = item.Group; if (!_dictionary.ContainsKey(group)) @@ -87,7 +84,7 @@ public virtual int IndexOf(TItem item) return -1; } - public virtual void Clear(TGroup group) + public void Clear(TGroup group) { if (!_dictionary.ContainsKey(group)) { @@ -98,27 +95,27 @@ public virtual void Clear(TGroup group) _dictionary[group].Clear(); } - public virtual void Clear() + public void Clear() { _dictionary.Clear(); _lists.Clear(); } - public virtual bool ContainsKey(TGroup group) => _dictionary.ContainsKey(@group); + public bool ContainsKey(TGroup group) => _dictionary.ContainsKey(group); - public virtual int Count => _lists.Sum(list => list.Count); + public int Count => _lists.Sum(list => list.Count); - public virtual int CountOf(TGroup group) => _dictionary.ContainsKey(group) + public int CountOf(TGroup group) => _dictionary.ContainsKey(group) ? _dictionary[group].Count : 0; - public virtual IEnumerable Values() => _dictionary.Values.SelectMany(i => i); + public IEnumerable Values() => _dictionary.Values.SelectMany(i => i); - public virtual IEnumerable AllOf(TGroup group) => _dictionary.ContainsKey(@group) - ? (IEnumerable) _dictionary[@group] + public IEnumerable AllOf(TGroup group) => _dictionary.ContainsKey(group) + ? (IEnumerable) _dictionary[group] : new TItem[0]; - public virtual bool Remove(TItem obj) + public bool Remove(TItem obj) { var group = obj.Group; if (!_dictionary.ContainsKey(group)) @@ -138,7 +135,7 @@ public virtual bool Remove(TItem obj) return true; } - public virtual bool Remove(TGroup group) + public bool Remove(TGroup group) { if (!_dictionary.ContainsKey(group)) { @@ -153,20 +150,17 @@ public virtual bool Remove(TGroup group) return true; } - public virtual bool Contains(TItem item) + public bool Contains(TItem item) { var group = item.Group; return _dictionary.ContainsKey(group) && _dictionary[group].Contains(item); } - public virtual void CopyTo(TItem[] array, int arrayIndex) - { - _dictionary.SelectMany(kvp => kvp.Value).ToArray().CopyTo(array, arrayIndex); - } + public void CopyTo(TItem[] array, int arrayIndex) => _dictionary.SelectMany(kvp => kvp.Value).ToArray().CopyTo(array, arrayIndex); - public virtual bool IsReadOnly => false; + public bool IsReadOnly => false; - public virtual void Insert(int index, TItem item) + public void Insert(int index, TItem item) { int relativeIndex; var list = ListForIndex(index, out relativeIndex); @@ -179,7 +173,7 @@ public virtual void Insert(int index, TItem item) OnItemAdded(item, index); } - public virtual void RemoveAt(int index) + public void RemoveAt(int index) { int relativeIndex; var list = ListForIndex(index, out relativeIndex); @@ -191,7 +185,7 @@ public virtual void RemoveAt(int index) list.RemoveAt(relativeIndex); } - public virtual TItem this[int index] + public TItem this[int index] { get { diff --git a/src/Ical.Net/Collections/GroupedListEnumerator.cs b/src/Ical.Net/Collections/GroupedListEnumerator.cs index 6bcbd4ff1..35e1b4d2a 100644 --- a/src/Ical.Net/Collections/GroupedListEnumerator.cs +++ b/src/Ical.Net/Collections/GroupedListEnumerator.cs @@ -6,23 +6,20 @@ namespace Ical.Net.Collections public class GroupedListEnumerator : IEnumerator { - private readonly IList> _lists; - private IEnumerator> _listsEnumerator; - private IEnumerator _listEnumerator; + readonly IList> _lists; + IEnumerator> _listsEnumerator; + IEnumerator _listEnumerator; public GroupedListEnumerator(IList> lists) => _lists = lists; - public virtual TType Current + public TType Current => _listEnumerator == null - ? default(TType) + ? default : _listEnumerator.Current; - public virtual void Dispose() - { - Reset(); - } + public void Dispose() => Reset(); - private void DisposeListEnumerator() + void DisposeListEnumerator() { if (_listEnumerator == null) { @@ -34,10 +31,10 @@ private void DisposeListEnumerator() object IEnumerator.Current => _listEnumerator == null - ? default(TType) + ? default : _listEnumerator.Current; - private bool MoveNextList() + bool MoveNextList() { if (_listsEnumerator == null) { @@ -64,7 +61,7 @@ private bool MoveNextList() return true; } - public virtual bool MoveNext() + public bool MoveNext() { while (true) { @@ -91,7 +88,7 @@ public virtual bool MoveNext() } } - public virtual void Reset() + public void Reset() { if (_listsEnumerator == null) { diff --git a/src/Ical.Net/Collections/GroupedValueList.cs b/src/Ical.Net/Collections/GroupedValueList.cs index 3640186dc..d014f5314 100644 --- a/src/Ical.Net/Collections/GroupedValueList.cs +++ b/src/Ical.Net/Collections/GroupedValueList.cs @@ -11,12 +11,9 @@ public class GroupedValueList : where TInterface : class, IGroupedObject, IValueObject where TItem : new() { - public virtual void Set(TGroup group, TValueType value) - { - Set(group, new[] { value }); - } + public void Set(TGroup group, TValueType value) => Set(group, new[] { value }); - public virtual void Set(TGroup group, IEnumerable values) + public void Set(TGroup group, IEnumerable values) { if (ContainsKey(group)) { @@ -31,7 +28,7 @@ public virtual void Set(TGroup group, IEnumerable values) obj.SetValue(values); } - public virtual TType Get(TGroup group) + public TType Get(TGroup group) { var firstItem = AllOf(group).FirstOrDefault(); if (firstItem?.Values != null) @@ -41,9 +38,10 @@ public virtual TType Get(TGroup group) .OfType() .FirstOrDefault(); } - return default(TType); + return default; } - public virtual IList GetMany(TGroup group) => new GroupedValueListProxy(this, group); + public IList GetMany(TGroup group) + => new GroupedValueListProxy(this, group); } } diff --git a/src/Ical.Net/Collections/IGroupedCollection.cs b/src/Ical.Net/Collections/IGroupedCollection.cs index a317e7c0a..cf44f90ce 100644 --- a/src/Ical.Net/Collections/IGroupedCollection.cs +++ b/src/Ical.Net/Collections/IGroupedCollection.cs @@ -3,42 +3,27 @@ namespace Ical.Net.Collections { - public interface IGroupedCollection : + public interface IGroupedCollection : ICollection where TItem : class, IGroupedObject { - /// - /// Fired after an item is added to the collection. - /// + /// Fired after an item is added to the collection. event EventHandler> ItemAdded; - /// - /// Removes all items with the matching group from the collection. - /// + /// Removes all items with from the collection. /// True if the object was removed, false otherwise. bool Remove(TGroup group); - /// - /// Clears all items matching the specified group. - /// + /// Clears all items matching the specified . void Clear(TGroup group); - /// - /// Returns true if the list contains at least one - /// object with a matching group, false otherwise. - /// + /// Returns true if the list contains at least one object with a matching . bool ContainsKey(TGroup group); - /// - /// Returns the number of objects in the list - /// with a matching group. - /// + /// Returns the number of objects in the list with a matching . int CountOf(TGroup group); - /// - /// Returns a list of objects that - /// match the specified group. - /// + /// Returns a list of objects that match the specified . IEnumerable AllOf(TGroup group); } } diff --git a/src/Ical.Net/Collections/MultiLinkedList.cs b/src/Ical.Net/Collections/MultiLinkedList.cs index 9cee33596..0ed9fa00b 100644 --- a/src/Ical.Net/Collections/MultiLinkedList.cs +++ b/src/Ical.Net/Collections/MultiLinkedList.cs @@ -6,21 +6,15 @@ public class MultiLinkedList : List, IMultiLinkedList { - private IMultiLinkedList _previous; - private IMultiLinkedList _next; + IMultiLinkedList _previous; + IMultiLinkedList _next; - public virtual void SetPrevious(IMultiLinkedList previous) - { - _previous = previous; - } + public void SetPrevious(IMultiLinkedList previous) => _previous = previous; - public virtual void SetNext(IMultiLinkedList next) - { - _next = next; - } + public void SetNext(IMultiLinkedList next) => _next = next; - public virtual int StartIndex => _previous?.ExclusiveEnd ?? 0; + public int StartIndex => _previous?.ExclusiveEnd ?? 0; - public virtual int ExclusiveEnd => Count > 0 ? StartIndex + Count : StartIndex; + public int ExclusiveEnd => Count > 0 ? StartIndex + Count : StartIndex; } } diff --git a/src/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs b/src/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs index eb3ccc1fb..902e54b2c 100644 --- a/src/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs +++ b/src/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs @@ -13,7 +13,7 @@ public class GroupedCollectionProxy : where TOriginal : class, IGroupedObject where TNew : class, TOriginal { - private readonly Func _predicate; + readonly Func _predicate; public GroupedCollectionProxy(IGroupedCollection realObject, Func predicate = null) { @@ -21,41 +21,29 @@ public GroupedCollectionProxy(IGroupedCollection realObject, SetProxiedObject(realObject); } - public virtual event EventHandler> ItemAdded; - public virtual event EventHandler> ItemRemoved; + public event EventHandler> ItemAdded; + public event EventHandler> ItemRemoved; - protected void OnItemAdded(TNew item, int index) - { - ItemAdded?.Invoke(this, new ObjectEventArgs(item, index)); - } + protected void OnItemAdded(TNew item, int index) => ItemAdded?.Invoke(this, new ObjectEventArgs(item, index)); - protected void OnItemRemoved(TNew item, int index) - { - ItemRemoved?.Invoke(this, new ObjectEventArgs(item, index)); - } + protected void OnItemRemoved(TNew item, int index) => ItemRemoved?.Invoke(this, new ObjectEventArgs(item, index)); - public virtual bool Remove(TGroup group) => RealObject.Remove(group); + public bool Remove(TGroup group) => RealObject.Remove(group); - public virtual void Clear(TGroup group) - { - RealObject.Clear(group); - } + public void Clear(TGroup group) => RealObject.Clear(group); - public virtual bool ContainsKey(TGroup group) => RealObject.ContainsKey(group); + public bool ContainsKey(TGroup group) => RealObject.ContainsKey(group); - public virtual int CountOf(TGroup group) => RealObject.OfType().Count(); + public int CountOf(TGroup group) => RealObject.OfType().Count(); - public virtual IEnumerable AllOf(TGroup group) => RealObject + public IEnumerable AllOf(TGroup group) => RealObject .AllOf(group) .OfType() .Where(_predicate); - public virtual void Add(TNew item) - { - RealObject.Add(item); - } + public void Add(TNew item) => RealObject.Add(item); - public virtual void Clear() + public void Clear() { // Only clear items of this type // that match the predicate. @@ -70,9 +58,9 @@ public virtual void Clear() } } - public virtual bool Contains(TNew item) => RealObject.Contains(item); + public bool Contains(TNew item) => RealObject.Contains(item); - public virtual void CopyTo(TNew[] array, int arrayIndex) + public void CopyTo(TNew[] array, int arrayIndex) { var i = 0; foreach (var item in this) @@ -81,15 +69,15 @@ public virtual void CopyTo(TNew[] array, int arrayIndex) } } - public virtual int Count => RealObject + public int Count => RealObject .OfType() .Count(); - public virtual bool IsReadOnly => false; + public bool IsReadOnly => false; - public virtual bool Remove(TNew item) => RealObject.Remove(item); + public bool Remove(TNew item) => RealObject.Remove(item); - public virtual IEnumerator GetEnumerator() => RealObject + public IEnumerator GetEnumerator() => RealObject .OfType() .GetEnumerator(); @@ -99,9 +87,6 @@ IEnumerator IEnumerable.GetEnumerator() => RealObject public IGroupedCollection RealObject { get; private set; } - public virtual void SetProxiedObject(IGroupedCollection realObject) - { - RealObject = realObject; - } + public void SetProxiedObject(IGroupedCollection realObject) => RealObject = realObject; } } diff --git a/src/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs b/src/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs index 0b908e6b4..aecb5b20e 100644 --- a/src/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs +++ b/src/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs @@ -13,9 +13,9 @@ public class GroupedValueListProxy, IValueObject where TItem : new() { - private readonly GroupedValueList _realObject; - private readonly TGroup _group; - private TInterface _container; + readonly GroupedValueList _realObject; + readonly TGroup _group; + TInterface _container; public GroupedValueListProxy(GroupedValueList realObject, TGroup group) { @@ -23,7 +23,7 @@ public GroupedValueListProxy(GroupedValueList, int, int, bool> action) + void IterateValues(Func, int, int, bool> action) { var i = 0; foreach (var obj in _realObject) @@ -66,15 +66,12 @@ private void IterateValues(Func, int, int, bool> ac } } - private IEnumerator GetEnumeratorInternal() - { - return Items + IEnumerator GetEnumeratorInternal() => Items .Where(o => o.ValueCount > 0) .SelectMany(o => o.Values.OfType()) .GetEnumerator(); - } - public virtual void Add(TNewValue item) + public void Add(TNewValue item) { // Add the value to the object if (item is TOriginalValue) @@ -84,7 +81,7 @@ public virtual void Add(TNewValue item) } } - public virtual void Clear() + public void Clear() { var items = Items.Where(o => o.Values != null); @@ -95,22 +92,19 @@ public virtual void Clear() } } - public virtual bool Contains(TNewValue item) => Items.Any(o => o.ContainsValue((TOriginalValue)(object)item)); + public bool Contains(TNewValue item) => Items.Any(o => o.ContainsValue((TOriginalValue)(object)item)); - public virtual void CopyTo(TNewValue[] array, int arrayIndex) - { - Items + public void CopyTo(TNewValue[] array, int arrayIndex) => Items .Where(o => o.Values != null) .SelectMany(o => o.Values) .ToArray() .CopyTo(array, arrayIndex); - } - - public virtual int Count => Items.Sum(o => o.ValueCount); - public virtual bool IsReadOnly => false; + public int Count => Items.Sum(o => o.ValueCount); - public virtual bool Remove(TNewValue item) + public bool IsReadOnly => false; + + public bool Remove(TNewValue item) { if (!(item is TOriginalValue)) { @@ -129,11 +123,11 @@ public virtual bool Remove(TNewValue item) return true; } - public virtual IEnumerator GetEnumerator() => GetEnumeratorInternal(); + public IEnumerator GetEnumerator() => GetEnumeratorInternal(); IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorInternal(); - public virtual int IndexOf(TNewValue item) + public int IndexOf(TNewValue item) { var index = -1; @@ -157,48 +151,42 @@ public virtual int IndexOf(TNewValue item) return index; } - public virtual void Insert(int index, TNewValue item) - { - IterateValues((o, i, count) => - { - var value = (TOriginalValue)(object)item; - - // Determine if this index is found within this object - if (index < i || index >= count) - { - return true; - } - - // Convert the items to a list - var items = o.Values.ToList(); - // Insert the item at the relative index within the list - items.Insert(index - i, value); - // Set the new list - o.SetValue(items); - return false; - }); - } - - public virtual void RemoveAt(int index) - { - IterateValues((o, i, count) => - { - // Determine if this index is found within this object - if (index >= i && index < count) - { - // Convert the items to a list - var items = o.Values.ToList(); - // Remove the item at the relative index within the list - items.RemoveAt(index - i); - // Set the new list - o.SetValue(items); - return false; - } - return true; - }); - } - - public virtual TNewValue this[int index] + public void Insert(int index, TNewValue item) => IterateValues((o, i, count) => + { + var value = (TOriginalValue)(object)item; + + // Determine if this index is found within this object + if (index < i || index >= count) + { + return true; + } + + // Convert the items to a list + var items = o.Values.ToList(); + // Insert the item at the relative index within the list + items.Insert(index - i, value); + // Set the new list + o.SetValue(items); + return false; + }); + + public void RemoveAt(int index) => IterateValues((o, i, count) => + { + // Determine if this index is found within this object + if (index >= i && index < count) + { + // Convert the items to a list + var items = o.Values.ToList(); + // Remove the item at the relative index within the list + items.RemoveAt(index - i); + // Set the new list + o.SetValue(items); + return false; + } + return true; + }); + + public TNewValue this[int index] { get { @@ -209,7 +197,7 @@ public virtual TNewValue this[int index] .Skip(index) .FirstOrDefault(); } - return default(TNewValue); + return default; } set { @@ -225,7 +213,7 @@ public virtual TNewValue this[int index] } } - public virtual IEnumerable Items => _group == null + public IEnumerable Items => _group == null ? _realObject : _realObject.AllOf(_group); } diff --git a/src/Ical.Net/Constants.cs b/src/Ical.Net/Constants.cs index 5a61a55e2..4bfb6c9b8 100644 --- a/src/Ical.Net/Constants.cs +++ b/src/Ical.Net/Constants.cs @@ -170,14 +170,13 @@ public enum FreeBusyStatus public enum FrequencyType { - None, - Secondly, - Minutely, - Hourly, - Daily, - Weekly, - Monthly, - Yearly + Secondly = 1, + Minutely = 60, + Hourly = 60 * Minutely, + Daily = 24 * Hourly, + Weekly = 7 * Daily, + Monthly = 30 * Daily, + Yearly = 365 * Daily } /// diff --git a/src/Ical.Net/DataTypes/Attachment.cs b/src/Ical.Net/DataTypes/Attachment.cs index 28a7e1d1f..6c9197046 100644 --- a/src/Ical.Net/DataTypes/Attachment.cs +++ b/src/Ical.Net/DataTypes/Attachment.cs @@ -13,11 +13,11 @@ namespace Ical.Net.DataTypes /// public class Attachment : EncodableDataType { - public virtual Uri Uri { get; set; } - public virtual byte[] Data { get; } + public Uri? Uri { get; set; } + public byte[] Data { get; } - private Encoding _valueEncoding = System.Text.Encoding.UTF8; - public virtual Encoding ValueEncoding + Encoding _valueEncoding = System.Text.Encoding.UTF8; + public Encoding ValueEncoding { get => _valueEncoding; set @@ -30,7 +30,7 @@ public virtual Encoding ValueEncoding } } - public virtual string FormatType + public string FormatType { get => Parameters.Get("FMTTYPE"); set => Parameters.Set("FMTTYPE", value); diff --git a/src/Ical.Net/DataTypes/Attendee.cs b/src/Ical.Net/DataTypes/Attendee.cs index eff96fbe6..a92462485 100644 --- a/src/Ical.Net/DataTypes/Attendee.cs +++ b/src/Ical.Net/DataTypes/Attendee.cs @@ -7,9 +7,9 @@ namespace Ical.Net.DataTypes { public class Attendee : EncodableDataType { - private Uri _sentBy; + Uri _sentBy; /// SENT-BY, to indicate who is acting on behalf of the ATTENDEE - public virtual Uri SentBy + public Uri SentBy { get { @@ -33,9 +33,9 @@ public virtual Uri SentBy } } - private string _commonName; + string _commonName; /// CN: to show the common or displayable name associated with the calendar address - public virtual string CommonName + public string CommonName { get { @@ -56,9 +56,9 @@ public virtual string CommonName } } - private Uri _directoryEntry; + Uri _directoryEntry; /// DIR, to indicate the URI that points to the directory information corresponding to the attendee - public virtual Uri DirectoryEntry + public Uri DirectoryEntry { get { @@ -82,9 +82,9 @@ public virtual Uri DirectoryEntry } } - private string _type; + string _type; /// CUTYPE: the type of calendar user - public virtual string Type + public string Type { get { @@ -106,9 +106,9 @@ public virtual string Type } } - private List _members; + List _members; /// MEMBER: the groups the user belongs to - public virtual IList Members + public IList Members { get => _members ?? (_members = new List(Parameters.GetMany("MEMBER"))); set @@ -118,9 +118,9 @@ public virtual IList Members } } - private string _role; + string _role; /// ROLE: the intended role the attendee will have - public virtual string Role + public string Role { get { @@ -141,8 +141,8 @@ public virtual string Role } } - private string _participationStatus; - public virtual string ParticipationStatus + string _participationStatus; + public string ParticipationStatus { get { @@ -163,9 +163,9 @@ public virtual string ParticipationStatus } } - private bool? _rsvp; + bool? _rsvp; /// RSVP, to indicate whether a reply is requested - public virtual bool Rsvp + public bool Rsvp { get { @@ -191,9 +191,9 @@ public virtual bool Rsvp } } - private List _delegatedTo; + List _delegatedTo; /// DELEGATED-TO, to indicate the calendar users that the original request was delegated to - public virtual IList DelegatedTo + public IList DelegatedTo { get => _delegatedTo ?? (_delegatedTo = new List(Parameters.GetMany("DELEGATED-TO"))); set @@ -207,9 +207,9 @@ public virtual IList DelegatedTo } } - private List _delegatedFrom; + List _delegatedFrom; /// DELEGATED-FROM, to indicate whom the request was delegated from - public virtual IList DelegatedFrom + public IList DelegatedFrom { get => _delegatedFrom ?? (_delegatedFrom = new List(Parameters.GetMany("DELEGATED-FROM"))); set @@ -224,7 +224,7 @@ public virtual IList DelegatedFrom } /// Uri associated with the attendee, typically an email address - public virtual Uri Value { get; set; } + public Uri Value { get; set; } public Attendee() {} diff --git a/src/Ical.Net/DataTypes/CalDateTime.cs b/src/Ical.Net/DataTypes/CalDateTime.cs index 017aa021c..2eab15d62 100644 --- a/src/Ical.Net/DataTypes/CalDateTime.cs +++ b/src/Ical.Net/DataTypes/CalDateTime.cs @@ -6,22 +6,23 @@ namespace Ical.Net.DataTypes { - /// - /// The iCalendar equivalent of the .NET class. + /// The iCalendar class-equivalent of the .NET struct. /// - /// In addition to the features of the class, the - /// class handles time zone differences, and integrates seamlessly into the iCalendar framework. + /// In addition to the features of the class, + /// the class handles time zone differences, + /// and integrates seamlessly into the iCalendar framework. + /// + /// Sole Implementation. /// - /// public sealed class CalDateTime : EncodableDataType, IDateTime { + /// Timezone-ID Parameter + const string StrTzId = "TZID"; + public static CalDateTime Now => new CalDateTime(DateTime.Now); public static CalDateTime Today => new CalDateTime(DateTime.Today); - private bool _hasDate; - private bool _hasTime; - public CalDateTime() { } public CalDateTime(IDateTime value) @@ -36,7 +37,7 @@ public CalDateTime(DateTime value) : this(value, null) { } /// `Utc`. If a non-UTC time zone is specified, the underlying `DateTimeKind` property will be `Local`. If no time zone is specified, the `DateTimeKind` /// property will be left untouched. /// - public CalDateTime(DateTime value, string tzId) + public CalDateTime(DateTime value, string? tzId) { Initialize(value, tzId, null); } @@ -68,14 +69,11 @@ public CalDateTime(string value) CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); } - private void Initialize(int year, int month, int day, int hour, int minute, int second, string tzId, Calendar cal) - { - Initialize(CoerceDateTime(year, month, day, hour, minute, second, DateTimeKind.Local), tzId, cal); - } + void Initialize(int year, int month, int day, int hour, int minute, int second, string? tzId, ICalendarObject? cal) => Initialize(CoerceDateTime(year, month, day, hour, minute, second, DateTimeKind.Local), tzId, cal); - private void Initialize(DateTime value, string tzId, Calendar cal) + void Initialize(DateTime value, string? tzId, ICalendarObject? cal) { - if (!string.IsNullOrWhiteSpace(tzId) && !tzId.Equals("UTC", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(tzId) && !tzId!.Equals("UTC", StringComparison.OrdinalIgnoreCase)) { // Definitely local value = DateTime.SpecifyKind(value, DateTimeKind.Local); @@ -94,10 +92,8 @@ private void Initialize(DateTime value, string tzId, Calendar cal) AssociatedObject = cal; } - private DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + static DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) { - var dt = DateTime.MinValue; - // NOTE: determine if a date/time value exceeds the representable date/time values in .NET. // If so, let's automatically adjust the date/time to compensate. // FIXME: should we have a parsing setting that will throw an exception @@ -107,19 +103,21 @@ private DateTime CoerceDateTime(int year, int month, int day, int hour, int minu { if (year > 9999) { - dt = DateTime.MaxValue; + return DateTime.MaxValue; } - else if (year > 0) + if (year > 0) { - dt = new DateTime(year, month, day, hour, minute, second, kind); + return new DateTime(year, month, day, hour, minute, second, kind); } + return DateTime.MinValue; + } + catch + { + return DateTime.MinValue; } - catch { } - - return dt; } - public override ICalendarObject AssociatedObject + public override ICalendarObject? AssociatedObject { get => base.AssociatedObject; set @@ -135,15 +133,14 @@ public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var dt = obj as IDateTime; - if (dt == null) + if (!(obj is IDateTime dt)) { return; } _value = dt.Value; - _hasDate = dt.HasDate; - _hasTime = dt.HasTime; + HasDate = dt.HasDate; + HasTime = dt.HasTime; AssociateWith(dt); } @@ -235,40 +232,41 @@ public DateTime AsSystemLocal } } - private DateTime _asUtc = DateTime.MinValue; - /// - /// Returns a representation of the DateTime in Coordinated Universal Time (UTC) - /// + /// Cache for the UTC Value + DateTime _asUtc = DateTime.MinValue; + + /// Returns a representation of the DateTime in Coordinated Universal Time (UTC) public DateTime AsUtc { get { - if (_asUtc == DateTime.MinValue) + if (_asUtc != DateTime.MinValue) { - // In order of weighting: - // 1) Specified TzId - // 2) Value having a DateTimeKind.Utc - // 3) Use the OS's time zone - - if (!string.IsNullOrWhiteSpace(TzId)) - { - var asLocal = DateUtil.ToZonedDateTimeLeniently(Value, TzId); - _asUtc = asLocal.ToDateTimeUtc(); - } - else if(IsUtc || Value.Kind == DateTimeKind.Utc) - { - _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Utc); - } - else - { - _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime(); - } + return _asUtc; + } + // In order of weighting: + // 1) Specified TzId + // 2) Value having a DateTimeKind.Utc + // 3) Use the OS's time zone + + if (!string.IsNullOrWhiteSpace(TzId)) + { + var asLocal = Value.ToZonedDateTimeLeniently(TzId); + _asUtc = asLocal.ToDateTimeUtc(); + } + else if(IsUtc || Value.Kind == DateTimeKind.Utc) + { + _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Utc); + } + else + { + _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime(); } return _asUtc; } } - private DateTime _value; + DateTime _value; public DateTime Value { get => _value; @@ -286,32 +284,26 @@ public DateTime Value public bool IsUtc => _value.Kind == DateTimeKind.Utc; - public bool HasDate - { - get => _hasDate; - set => _hasDate = value; - } + public bool HasDate { get; set; } - public bool HasTime - { - get => _hasTime; - set => _hasTime = value; - } + public bool HasTime { get; set; } - private string _tzId = string.Empty; + string _tzId = string.Empty; - /// - /// Setting the TzId to a local time zone will set Value.Kind to Local. Setting TzId to UTC will set Value.Kind to Utc. If the incoming value is null - /// or whitespace, Value.Kind will be set to Unspecified. Setting the TzId will NOT incur a UTC offset conversion under any circumstances. To convert - /// to another time zone, use the ToTimeZone() method. - /// + /// To convert to another time zone, use . + /// + /// Setting the TzId to a local time zone will set Value.Kind to Local. + /// Setting TzId to UTC will set Value.Kind to Utc. + /// If the incoming value is null or whitespace, Value.Kind will be set to Unspecified. + /// Setting the TzId will NOT incur a UTC offset conversion under any circumstances. + /// public string TzId { get { if (string.IsNullOrWhiteSpace(_tzId)) { - _tzId = Parameters.Get("TZID"); + _tzId = Parameters.Get(StrTzId); } return _tzId; } @@ -327,7 +319,7 @@ public string TzId var isEmpty = string.IsNullOrWhiteSpace(value); if (isEmpty) { - Parameters.Remove("TZID"); + Parameters.Remove(StrTzId); _tzId = null; Value = DateTime.SpecifyKind(Value, DateTimeKind.Local); return; @@ -338,7 +330,7 @@ public string TzId : DateTimeKind.Local; Value = DateTime.SpecifyKind(Value, kind); - Parameters.Set("TZID", value); + Parameters.Set(StrTzId, value); _tzId = value; } } @@ -369,9 +361,7 @@ public string TzId public TimeSpan TimeOfDay => Value.TimeOfDay; - /// - /// Returns a representation of the IDateTime in the specified time zone - /// + /// Returns a representation of the IDateTime in the specified time zone public IDateTime ToTimeZone(string tzId) { if (string.IsNullOrWhiteSpace(tzId)) @@ -384,7 +374,7 @@ public IDateTime ToTimeZone(string tzId) ? TimeZoneInfo.Local.Id : TzId; - var zonedOriginal = DateUtil.ToZonedDateTimeLeniently(Value, originalTzId); + var zonedOriginal = Value.ToZonedDateTimeLeniently(originalTzId); var converted = zonedOriginal.WithZone(DateUtil.GetZone(tzId)); return converted.Zone == DateTimeZone.Utc @@ -399,7 +389,7 @@ public IDateTime ToTimeZone(string tzId) public DateTimeOffset AsDateTimeOffset => string.IsNullOrWhiteSpace(TzId) ? new DateTimeOffset(AsSystemLocal) - : DateUtil.ToZonedDateTimeLeniently(Value, TzId).ToDateTimeOffset(); + : Value.ToZonedDateTimeLeniently(TzId).ToDateTimeOffset(); public IDateTime Add(TimeSpan ts) => this + ts; diff --git a/src/Ical.Net/DataTypes/CalendarDataType.cs b/src/Ical.Net/DataTypes/CalendarDataType.cs index 3a1aaf594..1f83c406b 100644 --- a/src/Ical.Net/DataTypes/CalendarDataType.cs +++ b/src/Ical.Net/DataTypes/CalendarDataType.cs @@ -9,18 +9,18 @@ namespace Ical.Net.DataTypes /// public abstract class CalendarDataType : ICalendarDataType { - private IParameterCollection _parameters; - private ParameterCollectionProxy _proxy; - private ServiceProvider _serviceProvider; + IParameterCollection _parameters; + ParameterCollectionProxy _proxy; + ServiceProvider _serviceProvider; - protected ICalendarObject _AssociatedObject; + protected ICalendarObject? _AssociatedObject; protected CalendarDataType() { Initialize(); } - private void Initialize() + void Initialize() { _parameters = new ParameterList(); _proxy = new ParameterCollectionProxy(_parameters); @@ -28,73 +28,42 @@ private void Initialize() } [OnDeserializing] - internal void DeserializingInternal(StreamingContext context) - { - OnDeserializing(context); - } + internal void DeserializingInternal(StreamingContext context) => OnDeserializing(context); [OnDeserialized] - internal void DeserializedInternal(StreamingContext context) - { - OnDeserialized(context); - } + internal void DeserializedInternal(StreamingContext context) => OnDeserialized(context); - protected virtual void OnDeserializing(StreamingContext context) - { - Initialize(); - } + protected virtual void OnDeserializing(StreamingContext context) => Initialize(); protected virtual void OnDeserialized(StreamingContext context) {} - public virtual Type GetValueType() - { - // See RFC 5545 Section 3.2.20. - if (_proxy != null && _proxy.ContainsKey("VALUE")) - { - switch (_proxy.Get("VALUE")) + /// See RFC 5545 Section 3.2.20. + /// for "TIME" only + public Type GetValueType() + => _proxy == null || !_proxy.ContainsKey("VALUE") + ? null + : _proxy.Get("VALUE") switch { - case "BINARY": - return typeof (byte[]); - case "BOOLEAN": - return typeof (bool); - case "CAL-ADDRESS": - return typeof (Uri); - case "DATE": - return typeof (IDateTime); - case "DATE-TIME": - return typeof (IDateTime); - case "DURATION": - return typeof (TimeSpan); - case "FLOAT": - return typeof (double); - case "INTEGER": - return typeof (int); - case "PERIOD": - return typeof (Period); - case "RECUR": - return typeof (RecurrencePattern); - case "TEXT": - return typeof (string); - case "TIME": - // FIXME: implement ISO.8601.2004 - throw new NotImplementedException(); - case "URI": - return typeof (Uri); - case "UTC-OFFSET": - return typeof (UtcOffset); - default: - return null; - } - } - return null; - } - - public virtual void SetValueType(string type) - { - _proxy?.Set("VALUE", type ?? type.ToUpper()); - } - - public virtual ICalendarObject AssociatedObject + "BINARY" => typeof(byte[]), + "BOOLEAN" => typeof(bool), + "CAL-ADDRESS" => typeof(Uri), + "DATE" => typeof(IDateTime), + "DATE-TIME" => typeof(IDateTime), + "DURATION" => typeof(TimeSpan), + "FLOAT" => typeof(double), + "INTEGER" => typeof(int), + "PERIOD" => typeof(Period), + "RECUR" => typeof(RecurrencePattern), + "TEXT" => typeof(string), + "TIME" => throw new NotImplementedException(), // FIXME: implement ISO.8601.2004 + "URI" => typeof(Uri), + "UTC-OFFSET" => typeof(UtcOffset), + _ => null + }; + + public void SetValueType(string type) => _proxy?.Set("VALUE", type ?? type.ToUpper()); + + public virtual ICalendarObject? AssociatedObject { get => _AssociatedObject; set @@ -108,9 +77,9 @@ public virtual ICalendarObject AssociatedObject if (_AssociatedObject != null) { _proxy.SetParent(_AssociatedObject); - if (_AssociatedObject is ICalendarParameterCollectionContainer) + if (_AssociatedObject is ICalendarParameterCollectionContainer container) { - _proxy.SetProxiedObject(((ICalendarParameterCollectionContainer) _AssociatedObject).Parameters); + _proxy.SetProxiedObject(container.Parameters); } } else @@ -121,9 +90,9 @@ public virtual ICalendarObject AssociatedObject } } - public virtual Calendar Calendar => _AssociatedObject?.Calendar; + public Calendar Calendar => _AssociatedObject?.Calendar; - public virtual string Language + public string Language { get => Parameters.Get("LANGUAGE"); set => Parameters.Set("LANGUAGE", value); @@ -135,38 +104,34 @@ public virtual string Language /// public virtual void CopyFrom(ICopyable obj) { - if (!(obj is ICalendarDataType)) + if (!(obj is ICalendarDataType dt)) { return; } - var dt = (ICalendarDataType) obj; _AssociatedObject = dt.AssociatedObject; _proxy.SetParent(_AssociatedObject); _proxy.SetProxiedObject(dt.Parameters); } - /// - /// Creates a copy of the object. - /// - /// The copy of the object. - public virtual T Copy() + /// The copy of this object. + public T Copy() { var type = GetType(); var obj = Activator.CreateInstance(type) as ICopyable; // Duplicate our values - if (obj is T) + if (obj is T obj1) { obj.CopyFrom(this); - return (T) obj; + return obj1; } - return default(T); + return default; } - public virtual IParameterCollection Parameters => _proxy; + public IParameterCollection Parameters => _proxy; - public virtual object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); + public object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); public object GetService(string name) => _serviceProvider.GetService(name); diff --git a/src/Ical.Net/DataTypes/DataTypes.cd b/src/Ical.Net/DataTypes/DataTypes.cd new file mode 100644 index 000000000..69736e333 --- /dev/null +++ b/src/Ical.Net/DataTypes/DataTypes.cd @@ -0,0 +1,206 @@ + + + + + + AAAAAAAAAAAAAAQAgAAAAAAAAAAAAIQAAABAAAAAAAI= + DataTypes\AlarmOccurrence.cs + + + + + + + + + + + + + + + AACAAAAAAAAAACAEhAAAAAAAAAAAAJAACAAAAgAAAAA= + DataTypes\Attachment.cs + + + + + + + + + + + + + + AAAAEAgSAgAIACTAkAAAAAAIAAIAAIAAAQAlCAAIEAA= + DataTypes\Attendee.cs + + + + + + + + + + + + + + ADICACAgJpiAASTGwAmDAkKAAGQICqIFIAAkIQhQByA= + DataTypes\CalDateTime.cs + + + + + + + AAAAABEACAQEATACAAABAAAEBgAAAIAEAIAAAAABAFQ= + DataTypes\CalendarDataType.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAA= + DataTypes\EncodableDataType.cs + + + + + + + AAAAAAAEAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + DataTypes\FreeBusyEntry.cs + + + + + + AAAAAAAAAEAAACAEgAAIAAAAAAAAAIAAAAAAAAAAAAA= + DataTypes\GeographicLocation.cs + + + + + + AAAAAAAAAgAAAAQEgAAAAAAAAAAAAIAAAAAAAAAAAAI= + DataTypes\Occurrence.cs + + + + + + + AAAAAAAQAAAAACAAgAAAAAAAAAAAAIAAAAAkAAAAEAA= + DataTypes\Organizer.cs + + + + + + AAEAAAAAIAACACQEgBAABAAAIAAgAIAAAAAAAAAQIAA= + DataTypes\Period.cs + + + + + + + + + + + + + + + AEIABEAAAAAAADAUiAABBAQAAAAAAIAECAQAAABQIAA= + DataTypes\PeriodList.cs + + + + + + + gIAwAAAAAIAASCAEkIAAIEQAAIABAIAAAQAsAAAgAAQ= + DataTypes\RecurrencePattern.cs + + + + + + AAAAAAAAAAAgACAEgAAAAAAAAAAAAIAAIAAAAAAAAAA= + DataTypes\RequestStatus.cs + + + + + + AAAAAgAAAAAAACAEgBAAAAAAAAAQAIAAAAAAAACAAAA= + DataTypes\StatusCode.cs + + + + + + AAAAAAAQAAACAKAAgAAAAAAAAAAAAIQAACAAAAAEAAA= + DataTypes\Trigger.cs + + + + + + + + + + + + + + AIAAAAAAAAAQAAAEwACAAAAAAAAAAICEAAAFAAAAgAA= + DataTypes\UTCOffset.cs + + + + + + AAAAAAAAAAAAACQAgAAAAAAAAAAAAIAAAAAEAAAAgAA= + DataTypes\WeekDay.cs + + + + + + + AAAAAAAACAAAAAACAAABAAAAAAAAAAAEAAAAAAAAABA= + DataTypes\ICalendarDataType.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAA= + DataTypes\ICalendarParameterCollectionContainer.cs + + + + + + ACICACAgJoiAAABAAAADAAKAAEAICCIFIAAkIQBQACA= + DataTypes\IDateTime.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAA= + DataTypes\IEncodableDataType.cs + + + + \ No newline at end of file diff --git a/src/Ical.Net/DataTypes/EncodableDataType.cs b/src/Ical.Net/DataTypes/EncodableDataType.cs index 1658fe2d1..60121c722 100644 --- a/src/Ical.Net/DataTypes/EncodableDataType.cs +++ b/src/Ical.Net/DataTypes/EncodableDataType.cs @@ -5,7 +5,7 @@ /// public class EncodableDataType : CalendarDataType, IEncodableDataType { - public virtual string Encoding + public string Encoding { get => Parameters.Get("ENCODING"); set => Parameters.Set("ENCODING", value); diff --git a/src/Ical.Net/DataTypes/FreeBusyEntry.cs b/src/Ical.Net/DataTypes/FreeBusyEntry.cs index 9aefeb805..5e9839882 100644 --- a/src/Ical.Net/DataTypes/FreeBusyEntry.cs +++ b/src/Ical.Net/DataTypes/FreeBusyEntry.cs @@ -2,7 +2,7 @@ { public class FreeBusyEntry : Period { - public virtual FreeBusyStatus Status { get; set; } + public FreeBusyStatus Status { get; set; } public FreeBusyEntry() { @@ -21,8 +21,7 @@ public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var fb = obj as FreeBusyEntry; - if (fb != null) + if (obj is FreeBusyEntry fb) { Status = fb.Status; } diff --git a/src/Ical.Net/DataTypes/ICalendarDataType.cs b/src/Ical.Net/DataTypes/ICalendarDataType.cs index 304c82544..5b1664d8c 100644 --- a/src/Ical.Net/DataTypes/ICalendarDataType.cs +++ b/src/Ical.Net/DataTypes/ICalendarDataType.cs @@ -6,7 +6,8 @@ public interface ICalendarDataType : ICalendarParameterCollectionContainer, ICop { Type GetValueType(); void SetValueType(string type); - ICalendarObject AssociatedObject { get; set; } + + ICalendarObject? AssociatedObject { get; set; } Calendar Calendar { get; } string Language { get; set; } diff --git a/src/Ical.Net/DataTypes/IDateTime.cs b/src/Ical.Net/DataTypes/IDateTime.cs index bead53f51..9c8460624 100644 --- a/src/Ical.Net/DataTypes/IDateTime.cs +++ b/src/Ical.Net/DataTypes/IDateTime.cs @@ -2,112 +2,78 @@ namespace Ical.Net.DataTypes { + /// with public interface IDateTime : IEncodableDataType, IComparable, IFormattable, ICalendarDataType { - /// - /// Converts the date/time to this computer's local date/time. - /// + /// Converts the date/time to this computer's local date/time. DateTime AsSystemLocal { get; } - /// - /// Converts the date/time to UTC (Coordinated Universal Time) - /// + /// Converts the date/time to UTC (Coordinated Universal Time) DateTime AsUtc { get; } - /// - /// Returns a DateTimeOffset representation of the Value. If a TzId is specified, it will use that time zone's UTC offset, otherwise it will use the - /// system-local time zone. - /// + /// Returns a DateTimeOffset representation of the Value. + /// + /// If a TzId is specified, it will use that time zone's UTC offset, + /// otherwise it will use the system-local time zone. + /// DateTimeOffset AsDateTimeOffset { get; } - /// - /// Gets/sets whether the Value of this date/time represents - /// a universal time. - /// + /// Gets/sets whether the Value of this date/time represents a universal time. bool IsUtc { get; } - /// - /// Gets the time zone name this time is in, if it references a time zone. - /// + /// Gets the time zone name this time is in, if it references a time zone. + /// Same as string TimeZoneName { get; } - /// - /// Gets/sets the underlying DateTime value stored. This should always - /// use DateTimeKind.Utc, regardless of its actual representation. - /// Use IsUtc along with the TZID to control how this - /// date/time is handled. - /// + /// Gets/sets the underlying DateTime value stored. + /// + /// This should always use DateTimeKind.Utc, regardless of its actual representation. + /// Use IsUtc along with the TZID to control how this date/time is handled. + /// DateTime Value { get; set; } - /// - /// Gets/sets whether or not this date/time value contains a 'date' part. - /// + /// Gets/sets whether or not this date/time value contains a 'date' part. bool HasDate { get; set; } - /// - /// Gets/sets whether or not this date/time value contains a 'time' part. - /// + /// Gets/sets whether or not this date/time value contains a 'time' part. bool HasTime { get; set; } - /// - /// Gets/sets the time zone ID for this date/time value. - /// + /// Gets/sets the time zone ID for this date/time value. + /// Same as string TzId { get; set; } - /// - /// Gets the year for this date/time value. - /// + /// Gets the year for this date/time value. int Year { get; } - /// - /// Gets the month for this date/time value. - /// + /// Gets the month for this date/time value. int Month { get; } - /// - /// Gets the day for this date/time value. - /// + /// Gets the day for this date/time value. int Day { get; } - /// - /// Gets the hour for this date/time value. - /// + /// Gets the hour for this date/time value. int Hour { get; } - /// - /// Gets the minute for this date/time value. - /// + /// Gets the minute for this date/time value. int Minute { get; } - /// - /// Gets the second for this date/time value. - /// + /// Gets the second for this date/time value. int Second { get; } - /// - /// Gets the millisecond for this date/time value. - /// + /// Gets the millisecond for this date/time value. int Millisecond { get; } - /// - /// Gets the ticks for this date/time value. - /// + /// Gets the ticks for this date/time value. long Ticks { get; } - /// - /// Gets the DayOfWeek for this date/time value. - /// + /// Gets the DayOfWeek for this date/time value. DayOfWeek DayOfWeek { get; } - /// - /// Gets the date portion of the date/time value. - /// + /// Gets the date portion of the date/time value. DateTime Date { get; } - /// - /// Converts the date/time value to a local time - /// within the specified time zone. - /// + /// Converts the date/time value to a local time + /// within the specified time zone. IDateTime ToTimeZone(string tzId); IDateTime Add(TimeSpan ts); @@ -128,6 +94,7 @@ public interface IDateTime : IEncodableDataType, IComparable, IFormat bool LessThanOrEqual(IDateTime dt); bool GreaterThanOrEqual(IDateTime dt); - void AssociateWith(IDateTime dt); + /// Synchronizes the between this and + void AssociateWith(IDateTime that); } } \ No newline at end of file diff --git a/src/Ical.Net/DataTypes/Occurrence.cs b/src/Ical.Net/DataTypes/Occurrence.cs index 3332099a4..904e0c730 100644 --- a/src/Ical.Net/DataTypes/Occurrence.cs +++ b/src/Ical.Net/DataTypes/Occurrence.cs @@ -28,7 +28,7 @@ public override bool Equals(object obj) { return false; } - return obj is Occurrence && Equals((Occurrence) obj); + return obj is Occurrence occurrence && Equals(occurrence); } public override int GetHashCode() diff --git a/src/Ical.Net/DataTypes/Organizer.cs b/src/Ical.Net/DataTypes/Organizer.cs index 7b0277af0..1f31f5901 100644 --- a/src/Ical.Net/DataTypes/Organizer.cs +++ b/src/Ical.Net/DataTypes/Organizer.cs @@ -11,7 +11,7 @@ namespace Ical.Net.DataTypes [DebuggerDisplay("{Value}")] public class Organizer : EncodableDataType { - public virtual Uri SentBy + public Uri SentBy { get => new Uri(Parameters.Get("SENT-BY")); set @@ -27,13 +27,13 @@ public virtual Uri SentBy } } - public virtual string CommonName + public string CommonName { get => Parameters.Get("CN"); set => Parameters.Set("CN", value); } - public virtual Uri DirectoryEntry + public Uri DirectoryEntry { get => new Uri(Parameters.Get("DIR")); set @@ -49,7 +49,7 @@ public virtual Uri DirectoryEntry } } - public virtual Uri Value { get; set; } + public Uri Value { get; set; } public Organizer() {} @@ -89,8 +89,7 @@ public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var o = obj as Organizer; - if (o != null) + if (obj is Organizer o) { Value = o.Value; } diff --git a/src/Ical.Net/DataTypes/Period.cs b/src/Ical.Net/DataTypes/Period.cs index 7edad2c73..ea1ee2c9e 100644 --- a/src/Ical.Net/DataTypes/Period.cs +++ b/src/Ical.Net/DataTypes/Period.cs @@ -3,7 +3,7 @@ namespace Ical.Net.DataTypes { - /// Represents an iCalendar period of time. + /// with optional and . public class Period : EncodableDataType, IComparable { public Period() { } @@ -11,7 +11,7 @@ public Period() { } public Period(IDateTime occurs) : this(occurs, default(TimeSpan)) {} - public Period(IDateTime start, IDateTime end) + public Period(IDateTime start, IDateTime? end) { if (end != null && end.LessThanOrEqual(start)) { @@ -35,7 +35,7 @@ public Period(IDateTime start, TimeSpan duration) } StartTime = start; - if (duration == default(TimeSpan)) + if (duration == default) { return; } @@ -48,8 +48,7 @@ public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var p = obj as Period; - if (p == null) + if (!(obj is Period p)) { return; } @@ -60,9 +59,9 @@ public override void CopyFrom(ICopyable obj) protected bool Equals(Period other) => Equals(StartTime, other.StartTime) && Equals(EndTime, other.EndTime) && Duration.Equals(other.Duration); - public override bool Equals(object obj) + public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) return false; + if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; return obj.GetType() == GetType() && Equals((Period) obj); } @@ -84,26 +83,26 @@ public override string ToString() return periodSerializer.SerializeToString(this); } - private void ExtrapolateTimes() + void ExtrapolateTimes() { - if (EndTime == null && StartTime != null && Duration != default(TimeSpan)) + if (EndTime == null && StartTime != null && Duration != null) { - EndTime = StartTime.Add(Duration); + EndTime = StartTime.Add(Duration.Value); } - else if (Duration == default(TimeSpan) && StartTime != null && EndTime != null) + else if (Duration == null && StartTime != null && EndTime != null) { Duration = EndTime.Subtract(StartTime); } - else if (StartTime == null && Duration != default(TimeSpan) && EndTime != null) + else if (StartTime == null && Duration != null && EndTime != null) { - StartTime = EndTime.Subtract(Duration); + StartTime = EndTime.Subtract(Duration.Value); } } - private IDateTime _startTime; - public virtual IDateTime StartTime + IDateTime? _startTime; + public IDateTime? StartTime { - get => _startTime.HasTime + get => _startTime?.HasTime ?? false ? _startTime : new CalDateTime(new DateTime(_startTime.Value.Year, _startTime.Value.Month, _startTime.Value.Day, 0, 0, 0), _startTime.TzId); set @@ -117,8 +116,8 @@ public virtual IDateTime StartTime } } - private IDateTime _endTime; - public virtual IDateTime EndTime + IDateTime? _endTime; + public IDateTime? EndTime { get => _endTime; set @@ -132,8 +131,8 @@ public virtual IDateTime EndTime } } - private TimeSpan _duration; - public virtual TimeSpan Duration + TimeSpan? _duration; + public TimeSpan? Duration { get { @@ -156,7 +155,7 @@ public virtual TimeSpan Duration } } - public virtual bool Contains(IDateTime dt) + public bool Contains(IDateTime dt) { // Start time is inclusive if (dt == null || StartTime == null || !StartTime.LessThanOrEqual(dt)) @@ -168,7 +167,7 @@ public virtual bool Contains(IDateTime dt) return EndTime == null || EndTime.GreaterThan(dt); } - public virtual bool CollidesWith(Period period) => period != null + public bool CollidesWith(Period period) => period != null && ((period.StartTime != null && Contains(period.StartTime)) || (period.EndTime != null && Contains(period.EndTime))); public int CompareTo(Period other) diff --git a/src/Ical.Net/DataTypes/PeriodList.cs b/src/Ical.Net/DataTypes/PeriodList.cs index e5a09e79a..48e078b1b 100644 --- a/src/Ical.Net/DataTypes/PeriodList.cs +++ b/src/Ical.Net/DataTypes/PeriodList.cs @@ -24,6 +24,7 @@ public PeriodList() SetService(new PeriodListEvaluator(this)); } + /// Deserializes the vCard Representation public PeriodList(string value) : this() { var serializer = new PeriodListSerializer(); diff --git a/src/Ical.Net/DataTypes/RecurrencePattern.cs b/src/Ical.Net/DataTypes/RecurrencePattern.cs index 6b05a9441..5fe53d54d 100644 --- a/src/Ical.Net/DataTypes/RecurrencePattern.cs +++ b/src/Ical.Net/DataTypes/RecurrencePattern.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.IO; using System.Linq; using Ical.Net.Evaluation; @@ -8,19 +9,114 @@ namespace Ical.Net.DataTypes { - /// - /// An iCalendar representation of the RRULE property. - /// https://tools.ietf.org/html/rfc5545#section-3.3.10 - /// + + public static class XRecurrence + { + [Pure] public static DateTime IncrementDate(this RecurrencePattern pattern, DateTime dt) => pattern.IncrementDateBy(dt, pattern.Interval); + [Pure] public static DateTime DecrementDate(this RecurrencePattern pattern, DateTime dt) => pattern.IncrementDateBy(dt, -pattern.Interval); + [Pure] public static DateTime IncrementDateBy(this RecurrencePattern pattern, DateTime date, int interval) + { + if (interval == 0) + { + throw new ArgumentOutOfRangeException(nameof(interval), interval, "Cannot evaluate with an interval of zero. Please use an interval other than zero."); + } + + return pattern.Frequency switch + { + FrequencyType.Secondly => date.AddSeconds(interval), + FrequencyType.Minutely => date.AddMinutes(interval), + FrequencyType.Hourly => date.AddHours(interval), + FrequencyType.Daily => date.AddDays(interval), + FrequencyType.Weekly => date.AddWeeks(interval, pattern.FirstDayOfWeek), + FrequencyType.Monthly => date.AddDays(-date.Day + 1).AddMonths(interval), + FrequencyType.Yearly => date.AddDays(-date.DayOfYear + 1).AddYears(interval), + _ => throw new Exception( + "FrequencyType.NONE cannot be evaluated. Please specify a FrequencyType before evaluating the recurrence.") + }; + } + + } + + /// An iCalendar representation of the RRULE property. https://tools.ietf.org/html/rfc5545#section-3.3.10 + /// + /// Multiple Patterns are combined into a + /// This is closely related to the Unix Cron Pattern, but with additional Time. + /// + /// BYxxx rule parts define explicit Occurrences for the xxx Frequency. + /// This means they have a different Effect, depending on + /// * xxx being smaller than the Frequency where only the single Seed Time would be used or + /// * xxx NOT smaller than the Frequency, where ALL Times would be used. + /// + /// If multiple BYxxx rule parts are specified, + /// then after evaluating the specified FREQ and INTERVAL rule parts, + /// the BYxxx rule parts are applied to the current set of evaluated occurrences + /// in the following order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, + /// BYHOUR, BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated. + /// + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |Frequency:|SECONDLY|MINUTELY|HOURLY |DAILY |WEEKLY|MONTHLY|YEARLY| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYMONTH |Limit |Limit |Limit |Limit |Limit |Limit |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYWEEKNO |N/A |N/A |N/A |N/A |N/A |N/A |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYYEARDAY |Limit |Limit |Limit |N/A |N/A |N/A |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYMONTHDAY|Limit |Limit |Limit |Limit |N/A |Expand |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYDAY |Limit |Limit |Limit |Limit |Expand|Note 1 |Note 2| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYHOUR |Limit |Limit |Limit |Expand |Expand|Expand |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYMINUTE |Limit |Limit |Expand |Expand |Expand|Expand |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYSECOND |Limit |Expand |Expand |Expand |Expand|Expand |Expand| + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// |BYSETPOS |Limit |Limit |Limit |Limit |Limit |Limit |Limit | + /// +----------+--------+--------+-------+-------+------+-------+------+ + /// + /// "N/A" means that the corresponding BYxxx rule part MUST NOT be used with the corresponding FREQ value. + /// + /// Note 1: Limit if BYMONTHDAY is present; otherwise, special expand for MONTHLY. + /// + /// Note 2: Limit if BYYEARDAY or BYMONTHDAY is present; otherwise, + /// special expand for WEEKLY if BYWEEKNO present; otherwise, + /// special expand for MONTHLY if BYMONTH present; otherwise, + /// special expand for YEARLY. + /// + /// + /// + /// For example, "FREQ=DAILY;BYMONTH=1" limits the number of recurrence instances + /// from all days (if BYMONTH rule part was not present) to all days in January only. + /// BYxxx rule parts for a period of time + /// * less than the frequency generally increase or expand the number of occurrences of the recurrence. + /// For example, "FREQ=YEARLY;BYMONTH=1,2" increases the number of + /// days within the yearly recurrence set from 1 (if BYMONTH rule part + /// is not present) to 2. + /// + /// + /// public class RecurrencePattern : EncodableDataType { - private int _interval = int.MinValue; - private RecurrenceRestrictionType? _restrictionType; - private RecurrenceEvaluationModeType? _evaluationMode; - + int _interval = int.MinValue; + RecurrenceRestrictionType? _restrictionType; + RecurrenceEvaluationModeType? _evaluationMode; + + /// Period of this Pattern; + /// + /// defines when this Event Happens, together with these Lists of Filters: + /// * + /// * + /// * + /// * + /// * + /// * + /// * + /// * + /// public FrequencyType Frequency { get; set; } - private DateTime _until = DateTime.MinValue; + DateTime _until = DateTime.MinValue; public DateTime Until { get => _until; @@ -35,14 +131,18 @@ public DateTime Until } } + /// Specifies how often this recurrence should repeat. + /// + /// specifies unlimited Count. + /// public int Count { get; set; } = int.MinValue; - /// - /// Specifies how often the recurrence should repeat. + /// Specifies the Period Length of the recurrence. + /// /// - 1 = every /// - 2 = every second /// - 3 = every third - /// + /// public int Interval { get => _interval == int.MinValue @@ -51,37 +151,48 @@ public int Interval set => _interval = value; } + #region optional enumerated Event Filters + + /// BYSECOND; optional List of Seconds Filter from 0 to 59 when this Event recurs + /// * (every Second) when no Rules were specified. public List BySecond { get; set; } = new List(); - /// The ordinal minutes of the hour associated with this recurrence pattern. Valid values are 0-59. + /// BYMINUTE; optional List of Minutes Filter from 0 to 59 when this Event recurs + /// * (every Minute) when no Rules were specified. public List ByMinute { get; set; } = new List(); + /// BYHOUR; optional List of Hours Filter from 0 to 23 when this Event recurs + /// * (every Hour) when no Rules were specified. public List ByHour { get; set; } = new List(); - public List ByDay { get; set; } = new List(); + /// BYDAY; optional List of Filter from 0 to 7 when this Event recurs + /// * (every Day) when no Rules were specified. + public List ByWeekDay { get; set; } = new List(); - /// The ordinal days of the month associated with this recurrence pattern. Valid values are 1-31. + /// BYMONTHDAY; optional List of Month-Day Filter from 1 to 31 when this Event recurs + /// * (every Day) when no Rules were specified. + /// Negative Days are counted from the End of the current Month. public List ByMonthDay { get; set; } = new List(); - /// - /// The ordinal days of the year associated with this recurrence pattern. Something recurring on the first day of the year would be a list containing - /// 1, and would also be New Year's Day. - /// + /// BYYEARDAY; optional List of Year-Day Filter from 1 to 366 when this Event recurs + /// * (every Year) when no Rules were specified. public List ByYearDay { get; set; } = new List(); - /// - /// The ordinal week of the year. Valid values are -53 to +53. Negative values count backwards from the end of the specified year. + /// BYWEEKNO; optional List of week of the year Filter from -53 to +53 when this Event recurs + /// + /// Negative values count backwards from the end of the specified year. /// A week is defined by ISO.8601.2004 - /// + /// public List ByWeekNo { get; set; } = new List(); - /// - /// List of months in the year associated with this rule. Valid values are 1 through 12. - /// + /// BYMONTH; optional List of months in the year from 1 through 12. public List ByMonth { get; set; } = new List(); + /// BYSETPOS; optional List of Positions in the Set of recurring Events. public List BySetPosition { get; set; } = new List(); + #endregion optional enumerated Event Filters + public DayOfWeek FirstDayOfWeek { get; set; } = DayOfWeek.Monday; public RecurrenceRestrictionType RestrictionType @@ -151,7 +262,7 @@ protected bool Equals(RecurrencePattern other) => (Interval == other.Interval) && CollectionEquals(BySecond, other.BySecond) && CollectionEquals(ByMinute, other.ByMinute) && CollectionEquals(ByHour, other.ByHour) - && CollectionEquals(ByDay, other.ByDay) + && CollectionEquals(ByWeekDay, other.ByWeekDay) && CollectionEquals(ByMonthDay, other.ByMonthDay) && CollectionEquals(ByYearDay, other.ByYearDay) && CollectionEquals(ByWeekNo, other.ByWeekNo) @@ -179,7 +290,7 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(BySecond); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByMinute); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByHour); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByDay); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByWeekDay); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByMonthDay); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByYearDay); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByWeekNo); @@ -192,31 +303,29 @@ public override int GetHashCode() public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - if (!(obj is RecurrencePattern)) + if (!(obj is RecurrencePattern pattern)) { return; } - var r = (RecurrencePattern) obj; - - Frequency = r.Frequency; - Until = r.Until; - Count = r.Count; - Interval = r.Interval; - BySecond = new List(r.BySecond); - ByMinute = new List(r.ByMinute); - ByHour = new List(r.ByHour); - ByDay = new List(r.ByDay); - ByMonthDay = new List(r.ByMonthDay); - ByYearDay = new List(r.ByYearDay); - ByWeekNo = new List(r.ByWeekNo); - ByMonth = new List(r.ByMonth); - BySetPosition = new List(r.BySetPosition); - FirstDayOfWeek = r.FirstDayOfWeek; - RestrictionType = r.RestrictionType; - EvaluationMode = r.EvaluationMode; + Frequency = pattern.Frequency; + Until = pattern.Until; + Count = pattern.Count; + Interval = pattern.Interval; + BySecond = new List(pattern.BySecond); + ByMinute = new List(pattern.ByMinute); + ByHour = new List(pattern.ByHour); + ByWeekDay = new List(pattern.ByWeekDay); + ByMonthDay = new List(pattern.ByMonthDay); + ByYearDay = new List(pattern.ByYearDay); + ByWeekNo = new List(pattern.ByWeekNo); + ByMonth = new List(pattern.ByMonth); + BySetPosition = new List(pattern.BySetPosition); + FirstDayOfWeek = pattern.FirstDayOfWeek; + RestrictionType = pattern.RestrictionType; + EvaluationMode = pattern.EvaluationMode; } - private static bool CollectionEquals(IEnumerable c1, IEnumerable c2) => c1.SequenceEqual(c2); + static bool CollectionEquals(IEnumerable c1, IEnumerable c2) => c1.SequenceEqual(c2); } } \ No newline at end of file diff --git a/src/Ical.Net/DataTypes/RequestStatus.cs b/src/Ical.Net/DataTypes/RequestStatus.cs index 00422dcca..c07689dc0 100644 --- a/src/Ical.Net/DataTypes/RequestStatus.cs +++ b/src/Ical.Net/DataTypes/RequestStatus.cs @@ -8,27 +8,11 @@ namespace Ical.Net.DataTypes /// public class RequestStatus : EncodableDataType { - private string _mDescription; - private string _mExtraData; - private StatusCode _mStatusCode; + public string Description { get; set; } - public virtual string Description - { - get => _mDescription; - set => _mDescription = value; - } + public string ExtraData { get; set; } - public virtual string ExtraData - { - get => _mExtraData; - set => _mExtraData = value; - } - - public virtual StatusCode StatusCode - { - get => _mStatusCode; - set => _mStatusCode = value; - } + public StatusCode StatusCode { get; set; } public RequestStatus() {} @@ -41,12 +25,11 @@ public RequestStatus(string value) : this() public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - if (!(obj is RequestStatus)) + if (!(obj is RequestStatus rs)) { return; } - var rs = (RequestStatus) obj; if (rs.StatusCode != null) { StatusCode = rs.StatusCode; @@ -61,8 +44,8 @@ public override string ToString() return serializer.SerializeToString(this); } - protected bool Equals(RequestStatus other) => string.Equals(_mDescription, other._mDescription) && string.Equals(_mExtraData, other._mExtraData) && - Equals(_mStatusCode, other._mStatusCode); + protected bool Equals(RequestStatus other) => string.Equals(Description, other.Description) && string.Equals(ExtraData, other.ExtraData) && + Equals(StatusCode, other.StatusCode); public override bool Equals(object obj) { @@ -85,9 +68,9 @@ public override int GetHashCode() { unchecked { - var hashCode = _mDescription?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (_mExtraData?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (_mStatusCode?.GetHashCode() ?? 0); + var hashCode = Description?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (ExtraData?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (StatusCode?.GetHashCode() ?? 0); return hashCode; } } diff --git a/src/Ical.Net/DataTypes/StatusCode.cs b/src/Ical.Net/DataTypes/StatusCode.cs index 0e02a2a05..5c473a984 100644 --- a/src/Ical.Net/DataTypes/StatusCode.cs +++ b/src/Ical.Net/DataTypes/StatusCode.cs @@ -48,9 +48,8 @@ public StatusCode(string value) : this() public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - if (obj is StatusCode) + if (obj is StatusCode sc) { - var sc = (StatusCode) obj; Parts = new int[sc.Parts.Length]; sc.Parts.CopyTo(Parts, 0); } diff --git a/src/Ical.Net/DataTypes/Trigger.cs b/src/Ical.Net/DataTypes/Trigger.cs index 53bde1f19..e4e5d747d 100644 --- a/src/Ical.Net/DataTypes/Trigger.cs +++ b/src/Ical.Net/DataTypes/Trigger.cs @@ -10,11 +10,10 @@ namespace Ical.Net.DataTypes /// public class Trigger : EncodableDataType { - private IDateTime _mDateTime; - private TimeSpan? _mDuration; - private string _mRelated = TriggerRelation.Start; + IDateTime _mDateTime; + TimeSpan? _mDuration; - public virtual IDateTime DateTime + public IDateTime DateTime { get => _mDateTime; set @@ -36,7 +35,7 @@ public virtual IDateTime DateTime } } - public virtual TimeSpan? Duration + public TimeSpan? Duration { get => _mDuration; set @@ -52,13 +51,9 @@ public virtual TimeSpan? Duration } } - public virtual string Related - { - get => _mRelated; - set => _mRelated = value; - } + public string Related { get; set; } = TriggerRelation.Start; - public virtual bool IsRelative => _mDuration != null; + public bool IsRelative => _mDuration != null; public Trigger() {} @@ -76,18 +71,17 @@ public Trigger(string value) : this() public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - if (!(obj is Trigger)) + if (!(obj is Trigger trigger)) { return; } - var t = (Trigger) obj; - DateTime = t.DateTime; - Duration = t.Duration; - Related = t.Related; + DateTime = trigger.DateTime; + Duration = trigger.Duration; + Related = trigger.Related; } - protected bool Equals(Trigger other) => Equals(_mDateTime, other._mDateTime) && _mDuration.Equals(other._mDuration) && _mRelated == other._mRelated; + protected bool Equals(Trigger other) => Equals(_mDateTime, other._mDateTime) && _mDuration.Equals(other._mDuration) && Related == other.Related; public override bool Equals(object obj) { @@ -112,7 +106,7 @@ public override int GetHashCode() { var hashCode = _mDateTime?.GetHashCode() ?? 0; hashCode = (hashCode * 397) ^ _mDuration.GetHashCode(); - hashCode = (hashCode * 397) ^ _mRelated?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ Related?.GetHashCode() ?? 0; return hashCode; } } diff --git a/src/Ical.Net/DataTypes/UTCOffset.cs b/src/Ical.Net/DataTypes/UTCOffset.cs index 5c731c46d..4deeefefc 100644 --- a/src/Ical.Net/DataTypes/UTCOffset.cs +++ b/src/Ical.Net/DataTypes/UTCOffset.cs @@ -34,9 +34,9 @@ public UtcOffset(TimeSpan ts) public static explicit operator TimeSpan(UtcOffset o) => o.Offset; - public virtual DateTime ToUtc(DateTime dt) => DateTime.SpecifyKind(dt.Add(-Offset), DateTimeKind.Utc); + public DateTime ToUtc(DateTime dt) => DateTime.SpecifyKind(dt.Add(-Offset), DateTimeKind.Utc); - public virtual DateTime ToLocal(DateTime dt) => DateTime.SpecifyKind(dt.Add(Offset), DateTimeKind.Local); + public DateTime ToLocal(DateTime dt) => DateTime.SpecifyKind(dt.Add(Offset), DateTimeKind.Local); protected bool Equals(UtcOffset other) => Offset == other.Offset; diff --git a/src/Ical.Net/DataTypes/WeekDay.cs b/src/Ical.Net/DataTypes/WeekDay.cs index b2af5707a..8cfe292c5 100644 --- a/src/Ical.Net/DataTypes/WeekDay.cs +++ b/src/Ical.Net/DataTypes/WeekDay.cs @@ -4,14 +4,13 @@ namespace Ical.Net.DataTypes { - /// - /// Represents an RFC 5545 "BYDAY" value. - /// - public class WeekDay : EncodableDataType + /// represents an RFC 5545 "BYDAY" value. + public class WeekDay : EncodableDataType, IEquatable, IComparable { - public virtual int Offset { get; set; } = int.MinValue; + /// 1...4. Week in the Month: ; indicate no Offset + public int Offset { get; set; } = int.MinValue; - public virtual DayOfWeek DayOfWeek { get; set; } + public DayOfWeek DayOfWeek { get; set; } public WeekDay() { @@ -23,9 +22,9 @@ public WeekDay(DayOfWeek day) : this() DayOfWeek = day; } - public WeekDay(DayOfWeek day, int num) : this(day) + public WeekDay(DayOfWeek day, int offset) : this(day) { - Offset = num; + Offset = offset; } public WeekDay(DayOfWeek day, FrequencyOccurrence type) : this(day, (int) type) {} @@ -38,23 +37,23 @@ public WeekDay(string value) public override bool Equals(object obj) { - if (!(obj is WeekDay)) + if (!(obj is WeekDay weekDay)) { return false; } - var ds = (WeekDay) obj; - return ds.Offset == Offset && ds.DayOfWeek == DayOfWeek; + return Equals(weekDay); } + public bool Equals(WeekDay weekDay) => weekDay != null && weekDay.Offset == Offset && weekDay.DayOfWeek == DayOfWeek; + public override int GetHashCode() => Offset.GetHashCode() ^ DayOfWeek.GetHashCode(); public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - if (obj is WeekDay) + if (obj is WeekDay bd) { - var bd = (WeekDay) obj; Offset = bd.Offset; DayOfWeek = bd.DayOfWeek; } @@ -62,26 +61,31 @@ public override void CopyFrom(ICopyable obj) public int CompareTo(object obj) { - WeekDay bd = null; - if (obj is string) + WeekDay wd = null; + if (obj is string) { - bd = new WeekDay(obj.ToString()); + wd = new WeekDay(obj.ToString()); } - else if (obj is WeekDay) + else if (obj is WeekDay day) { - bd = (WeekDay) obj; + wd = day; } - if (bd == null) - { - throw new ArgumentException(); - } - var compare = DayOfWeek.CompareTo(bd.DayOfWeek); - if (compare == 0) - { - compare = Offset.CompareTo(bd.Offset); - } - return compare; + return CompareTo(wd); + } + + public int CompareTo(WeekDay wd) + { + if (wd == null) + { + throw new ArgumentException(); + } + var compare = DayOfWeek.CompareTo(wd.DayOfWeek); + if (compare == 0) + { + compare = Offset.CompareTo(wd.Offset); + } + return compare; } } } \ No newline at end of file diff --git a/src/Ical.Net/Evaluation/Evaluation.cd b/src/Ical.Net/Evaluation/Evaluation.cd new file mode 100644 index 000000000..51cdb745f --- /dev/null +++ b/src/Ical.Net/Evaluation/Evaluation.cd @@ -0,0 +1,75 @@ + + + + + + AAAABAAAAAAAAYACBAAAAgAAEAACQACkCAAEAABAAAA= + Evaluation\Evaluator.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAgAAAAAAgAAAAAAAAAAAAAA= + Evaluation\EventEvaluator.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAQ= + Evaluation\PeriodListEvaluator.cs + + + + + + gIAAAgAkAAAAAAAAAAIAIgARCAQAAAAAAFAgQAgQCAA= + Evaluation\RecurrencePatternEvaluator.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAgAAAABAAAIAAAAAAAAA= + Evaluation\RecurrenceUtil.cs + + + + + + AAAAAAgAAAAAAAAAAAAAAgAAAAAABAAAjAAAAAAAAQA= + Evaluation\RecurringEvaluator.cs + + + + + + AAAAAAIQAAIAAAAAAAAAAgAAAAAAAAAgAAAAAABAAAA= + Evaluation\TimeZoneEvaluator.cs + + + + + + AAAAAAgAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAA= + Evaluation\TimeZoneInfoEvaluator.cs + + + + + + AAAAAAAAAQAAAAAAAAAAAgAABAAAAAAAAAEAAAAAAAA= + Evaluation\TodoEvaluator.cs + + + + + + AAAABAAAAAAAAAACAAAAAgAAEAAAQAAEAAAAAABAAAA= + Evaluation\IEvaluator.cs + + + + \ No newline at end of file diff --git a/src/Ical.Net/Evaluation/Evaluator.cs b/src/Ical.Net/Evaluation/Evaluator.cs index 79c343849..b166883d7 100644 --- a/src/Ical.Net/Evaluation/Evaluator.cs +++ b/src/Ical.Net/Evaluation/Evaluator.cs @@ -2,17 +2,13 @@ using System.Collections.Generic; using System.Globalization; using Ical.Net.DataTypes; -using Ical.Net.Utility; namespace Ical.Net.Evaluation { public abstract class Evaluator : IEvaluator { - private DateTime _mEvaluationStartBounds = DateTime.MaxValue; - private DateTime _mEvaluationEndBounds = DateTime.MinValue; - - private ICalendarObject _mAssociatedObject; - private readonly ICalendarDataType _mAssociatedDataType; + ICalendarObject? _AssociatedObject; + readonly ICalendarDataType _AssociatedDataType; protected HashSet MPeriods; @@ -23,95 +19,49 @@ protected Evaluator() protected Evaluator(ICalendarObject associatedObject) { - _mAssociatedObject = associatedObject; + _AssociatedObject = associatedObject; Initialize(); } protected Evaluator(ICalendarDataType dataType) { - _mAssociatedDataType = dataType; + _AssociatedDataType = dataType; Initialize(); } - private void Initialize() + void Initialize() { Calendar = CultureInfo.CurrentCulture.Calendar; MPeriods = new HashSet(); } - protected IDateTime ConvertToIDateTime(DateTime dt, IDateTime referenceDate) + protected static IDateTime ConvertToIDateTime(DateTime dt, IDateTime referenceDate) { IDateTime newDt = new CalDateTime(dt, referenceDate.TzId); newDt.AssociateWith(referenceDate); return newDt; } - protected void IncrementDate(ref DateTime dt, RecurrencePattern pattern, int interval) - { - // FIXME: use a more specific exception. - if (interval == 0) - { - throw new Exception("Cannot evaluate with an interval of zero. Please use an interval other than zero."); - } - - var old = dt; - switch (pattern.Frequency) - { - case FrequencyType.Secondly: - dt = old.AddSeconds(interval); - break; - case FrequencyType.Minutely: - dt = old.AddMinutes(interval); - break; - case FrequencyType.Hourly: - dt = old.AddHours(interval); - break; - case FrequencyType.Daily: - dt = old.AddDays(interval); - break; - case FrequencyType.Weekly: - dt = DateUtil.AddWeeks(old, interval, pattern.FirstDayOfWeek); - break; - case FrequencyType.Monthly: - dt = old.AddDays(-old.Day + 1).AddMonths(interval); - break; - case FrequencyType.Yearly: - dt = old.AddDays(-old.DayOfYear + 1).AddYears(interval); - break; - // FIXME: use a more specific exception. - default: - throw new Exception("FrequencyType.NONE cannot be evaluated. Please specify a FrequencyType before evaluating the recurrence."); - } - } - public System.Globalization.Calendar Calendar { get; private set; } - public virtual DateTime EvaluationStartBounds - { - get => _mEvaluationStartBounds; - set => _mEvaluationStartBounds = value; - } + public DateTime EvaluationStartBounds { get; set; } = DateTime.MaxValue; - public virtual DateTime EvaluationEndBounds - { - get => _mEvaluationEndBounds; - set => _mEvaluationEndBounds = value; - } + public DateTime EvaluationEndBounds { get; set; } = DateTime.MinValue; - public virtual ICalendarObject AssociatedObject + public ICalendarObject AssociatedObject { - get => _mAssociatedObject ?? _mAssociatedDataType?.AssociatedObject; - protected set => _mAssociatedObject = value; + get => _AssociatedObject ?? _AssociatedDataType?.AssociatedObject; + protected set => _AssociatedObject = value; } - public virtual HashSet Periods => MPeriods; + public HashSet Periods => MPeriods; public virtual void Clear() { - _mEvaluationStartBounds = DateTime.MaxValue; - _mEvaluationEndBounds = DateTime.MinValue; + EvaluationStartBounds = DateTime.MaxValue; + EvaluationEndBounds = DateTime.MinValue; MPeriods.Clear(); } diff --git a/src/Ical.Net/Evaluation/EventEvaluator.cs b/src/Ical.Net/Evaluation/EventEvaluator.cs index b2551d694..8698ae1e8 100644 --- a/src/Ical.Net/Evaluation/EventEvaluator.cs +++ b/src/Ical.Net/Evaluation/EventEvaluator.cs @@ -16,8 +16,8 @@ protected CalendarEvent CalendarEvent public EventEvaluator(CalendarEvent evt) : base(evt) {} - /// - /// Evaluates this event to determine the dates and times for which the event occurs. + /// Evaluates this to determine the dates and times for which the event occurs. + /// /// This method only evaluates events which occur between /// and ; therefore, if you require a list of events which /// occur outside of this range, you must specify a and @@ -27,12 +27,7 @@ public EventEvaluator(CalendarEvent evt) : base(evt) {} /// during processing time, especially when this method in called for a large number /// of events, in sequence, or for a very large time span. /// - /// - /// - /// The beginning date of the range to evaluate. - /// The end date of the range to evaluate. - /// - /// + /// public override HashSet Evaluate(IDateTime referenceTime, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) { // Evaluate recurrences normally @@ -41,18 +36,14 @@ public override HashSet Evaluate(IDateTime referenceTime, DateTime perio foreach (var period in Periods) { period.Duration = CalendarEvent.Duration; - period.EndTime = period.Duration == null - ? period.StartTime - : period.StartTime.Add(CalendarEvent.Duration); + period.EndTime = period.StartTime.Add(CalendarEvent.Duration); } // Ensure each period has a duration foreach (var period in Periods.Where(p => p.EndTime == null)) { period.Duration = CalendarEvent.Duration; - period.EndTime = period.Duration == null - ? period.StartTime - : period.StartTime.Add(CalendarEvent.Duration); + period.EndTime = period.StartTime.Add(CalendarEvent.Duration); } return Periods; diff --git a/src/Ical.Net/Evaluation/PeriodListEvaluator.cs b/src/Ical.Net/Evaluation/PeriodListEvaluator.cs index b959fd23f..3219858d4 100644 --- a/src/Ical.Net/Evaluation/PeriodListEvaluator.cs +++ b/src/Ical.Net/Evaluation/PeriodListEvaluator.cs @@ -6,7 +6,7 @@ namespace Ical.Net.Evaluation { public class PeriodListEvaluator : Evaluator { - private readonly PeriodList _mPeriodList; + readonly PeriodList _mPeriodList; public PeriodListEvaluator(PeriodList rdt) { diff --git a/src/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs b/src/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs index 6ac2a6012..4c41f3842 100644 --- a/src/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs +++ b/src/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs @@ -49,7 +49,7 @@ namespace Ical.Net.Evaluation public class RecurrencePatternEvaluator : Evaluator { // FIXME: in ical4j this is configurable. - private const int _maxIncrementCount = 1000; + const int _maxIncrementCount = 1000; protected RecurrencePattern Pattern { get; set; } @@ -58,7 +58,7 @@ public RecurrencePatternEvaluator(RecurrencePattern pattern) Pattern = pattern; } - private RecurrencePattern ProcessRecurrencePattern(IDateTime referenceDate) + RecurrencePattern ProcessRecurrencePattern(IDateTime referenceDate) { var r = new RecurrencePattern(); r.CopyFrom(Pattern); @@ -66,169 +66,168 @@ private RecurrencePattern ProcessRecurrencePattern(IDateTime referenceDate) // Convert the UNTIL value to one that matches the same time information as the reference date if (r.Until != DateTime.MinValue) { - r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, referenceDate.TzId)).Value; + r.Until = referenceDate.MatchTimeZone(new CalDateTime(r.Until, referenceDate.TzId)).Value; } - if (r.Frequency > FrequencyType.Secondly && r.BySecond.Count == 0 && referenceDate.HasTime - /* NOTE: Fixes a bug where all-day events have BySecond/ByMinute/ByHour added incorrectly */) + if (referenceDate.HasTime) { - r.BySecond.Add(referenceDate.Second); - } - if (r.Frequency > FrequencyType.Minutely && r.ByMinute.Count == 0 && referenceDate.HasTime - /* NOTE: Fixes a bug where all-day events have BySecond/ByMinute/ByHour added incorrectly */) - { - r.ByMinute.Add(referenceDate.Minute); - } - if (r.Frequency > FrequencyType.Hourly && r.ByHour.Count == 0 && referenceDate.HasTime - /* NOTE: Fixes a bug where all-day events have BySecond/ByMinute/ByHour added incorrectly */) - { - r.ByHour.Add(referenceDate.Hour); + if (r.Frequency > FrequencyType.Secondly && + r.BySecond.Count == 0) { //once, at the referenceDate.Second + r.BySecond.Add(referenceDate.Second); + } + if (r.Frequency > FrequencyType.Minutely && + r.ByMinute.Count == 0) { //once, at the referenceDate.Minute + r.ByMinute.Add(referenceDate.Minute); + } + if (r.Frequency > FrequencyType.Hourly && + r.ByHour.Count == 0) { //once, at the referenceDate.Hour + r.ByHour.Add(referenceDate.Hour); + } } // If BYDAY, BYYEARDAY, or BYWEEKNO is specified, then // we don't default BYDAY, BYMONTH or BYMONTHDAY - if (r.ByDay.Count == 0) - { - // If the frequency is weekly, use the original date's day of week. - // NOTE: fixes WeeklyCount1() and WeeklyUntil1() handling - // If BYWEEKNO is specified and BYMONTHDAY/BYYEARDAY is not specified, - // then let's add BYDAY to BYWEEKNO. - // NOTE: fixes YearlyByWeekNoX() handling - if (r.Frequency == FrequencyType.Weekly || (r.ByWeekNo.Count > 0 && r.ByMonthDay.Count == 0 && r.ByYearDay.Count == 0)) - { - r.ByDay.Add(new WeekDay(referenceDate.DayOfWeek)); - } + if (r.ByWeekDay.Count != 0) { + return r; + } - // If BYMONTHDAY is not specified, - // default to the current day of month. - // NOTE: fixes YearlyByMonth1() handling, added BYYEARDAY exclusion - // to fix YearlyCountByYearDay1() handling - if (r.Frequency > FrequencyType.Weekly && r.ByWeekNo.Count == 0 && r.ByYearDay.Count == 0 && r.ByMonthDay.Count == 0) - { - r.ByMonthDay.Add(referenceDate.Day); - } + // If the frequency is weekly, use the original date's day of week. + // NOTE: fixes WeeklyCount1() and WeeklyUntil1() handling + // If BYWEEKNO is specified and BYMONTHDAY/BYYEARDAY is not specified, + // then let's add BYDAY to BYWEEKNO. + // NOTE: fixes YearlyByWeekNoX() handling + if (r.Frequency == FrequencyType.Weekly || (r.ByWeekNo.Count > 0 && r.ByMonthDay.Count == 0 && + r.ByYearDay.Count == 0)) { //once, at the referenceDate.Hour + r.ByWeekDay.Add(new WeekDay(referenceDate.DayOfWeek)); + } - // If BYMONTH is not specified, default to - // the current month. - // NOTE: fixes YearlyCountByYearDay1() handling - if (r.Frequency > FrequencyType.Monthly && r.ByWeekNo.Count == 0 && r.ByYearDay.Count == 0 && r.ByMonth.Count == 0) - { - r.ByMonth.Add(referenceDate.Month); - } + // If BYMONTHDAY is not specified, default to the current day of month. + // NOTE: fixes YearlyByMonth1() handling, added BYYEARDAY exclusion + // to fix YearlyCountByYearDay1() handling + if (r.Frequency > FrequencyType.Weekly && r.ByWeekNo.Count == 0 && r.ByYearDay.Count == 0 && + r.ByMonthDay.Count == 0) { //once, at the referenceDate.Day + r.ByMonthDay.Add(referenceDate.Day); + } + + // If BYMONTH is not specified, default to the current month. + // NOTE: fixes YearlyCountByYearDay1() handling + if (r.Frequency > FrequencyType.Monthly && r.ByWeekNo.Count == 0 && r.ByYearDay.Count == 0 && + r.ByMonth.Count == 0) { //once, at the referenceDate.Month + r.ByMonth.Add(referenceDate.Month); } return r; } - private void EnforceEvaluationRestrictions(RecurrencePattern pattern) + static void EnforceEvaluationRestrictions(RecurrencePattern pattern) { RecurrenceEvaluationModeType? evaluationMode = pattern.EvaluationMode; RecurrenceRestrictionType? evaluationRestriction = pattern.RestrictionType; - if (evaluationRestriction != RecurrenceRestrictionType.NoRestriction) + if (evaluationRestriction == RecurrenceRestrictionType.NoRestriction) { - switch (evaluationMode) - { - case RecurrenceEvaluationModeType.AdjustAutomatically: - switch (pattern.Frequency) + return; + } + + switch (evaluationMode) + { + case RecurrenceEvaluationModeType.AdjustAutomatically: + switch (pattern.Frequency) + { + case FrequencyType.Secondly: { - case FrequencyType.Secondly: + switch (evaluationRestriction) { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.Default: - case RecurrenceRestrictionType.RestrictSecondly: - pattern.Frequency = FrequencyType.Minutely; - break; - case RecurrenceRestrictionType.RestrictMinutely: - pattern.Frequency = FrequencyType.Hourly; - break; - case RecurrenceRestrictionType.RestrictHourly: - pattern.Frequency = FrequencyType.Daily; - break; - } + case RecurrenceRestrictionType.Default: + case RecurrenceRestrictionType.RestrictSecondly: + pattern.Frequency = FrequencyType.Minutely; + break; + case RecurrenceRestrictionType.RestrictMinutely: + pattern.Frequency = FrequencyType.Hourly; + break; + case RecurrenceRestrictionType.RestrictHourly: + pattern.Frequency = FrequencyType.Daily; + break; } - break; - case FrequencyType.Minutely: + } + break; + case FrequencyType.Minutely: + { + pattern.Frequency = evaluationRestriction switch { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.RestrictMinutely: - pattern.Frequency = FrequencyType.Hourly; - break; - case RecurrenceRestrictionType.RestrictHourly: - pattern.Frequency = FrequencyType.Daily; - break; - } - } - break; - case FrequencyType.Hourly: + RecurrenceRestrictionType.RestrictMinutely => FrequencyType.Hourly, + RecurrenceRestrictionType.RestrictHourly => FrequencyType.Daily, + _ => pattern.Frequency + }; + } + break; + case FrequencyType.Hourly: + { + pattern.Frequency = evaluationRestriction switch { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.RestrictHourly: - pattern.Frequency = FrequencyType.Daily; - break; - } - } - break; + RecurrenceRestrictionType.RestrictHourly => FrequencyType.Daily, + _ => pattern.Frequency + }; } - break; - case RecurrenceEvaluationModeType.ThrowException: - case RecurrenceEvaluationModeType.Default: - switch (pattern.Frequency) + break; + } + break; + case RecurrenceEvaluationModeType.ThrowException: + case RecurrenceEvaluationModeType.Default: + switch (pattern.Frequency) + { + case FrequencyType.Secondly: { - case FrequencyType.Secondly: + switch (evaluationRestriction) { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.Default: - case RecurrenceRestrictionType.RestrictSecondly: - case RecurrenceRestrictionType.RestrictMinutely: - case RecurrenceRestrictionType.RestrictHourly: - throw new ArgumentException(); - } + case RecurrenceRestrictionType.Default: + case RecurrenceRestrictionType.RestrictSecondly: + case RecurrenceRestrictionType.RestrictMinutely: + case RecurrenceRestrictionType.RestrictHourly: + throw new ArgumentException(); } - break; - case FrequencyType.Minutely: + } + break; + case FrequencyType.Minutely: + { + switch (evaluationRestriction) { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.RestrictMinutely: - case RecurrenceRestrictionType.RestrictHourly: - throw new ArgumentException(); - } + case RecurrenceRestrictionType.RestrictMinutely: + case RecurrenceRestrictionType.RestrictHourly: + throw new ArgumentException(); } - break; - case FrequencyType.Hourly: + } + break; + case FrequencyType.Hourly: + { + switch (evaluationRestriction) { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.RestrictHourly: - throw new ArgumentException(); - } + case RecurrenceRestrictionType.RestrictHourly: + throw new ArgumentException(); } - break; } - break; - } + break; + } + break; } } - /** - * Returns a list of start dates in the specified period represented by this recur. This method includes a base date - * argument, which indicates the start of the fist occurrence of this recurrence. The base date is used to inject - * default values to return a set of dates in the correct format. For example, if the search start date (start) is - * Wed, Mar 23, 12:19PM, but the recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at - * 9:00AM, and not 12:19PM. - */ - - private HashSet GetDates(IDateTime seed, DateTime periodStart, DateTime periodEnd, int maxCount, RecurrencePattern pattern, - bool includeReferenceDateInResults) + /// Returns a list of dates of this recurrence in the specified period. + /// + /// This method includes a date argument, + /// which indicates the start of the fist occurrence of this recurrence. + /// The base date is used to inject default values to return a set of dates in the correct format. + /// + /// For example, if the search start date (start) is Wed, Mar 23, 12:19PM, + /// but the recurrence is Mon - Fri, 9:00AM - 5:00PM, + /// the start dates returned should all be at 9:00AM, and not 12:19PM. + /// + HashSet GetDates(IDateTime seed, DateTime periodStart, DateTime periodEnd + , int maxCount, RecurrencePattern pattern, bool includeReferenceDateInResults) { var dates = new HashSet(); - var originalDate = DateUtil.GetSimpleDateTimeData(seed); - var seedCopy = DateUtil.GetSimpleDateTimeData(seed); + var originalDate = seed.GetSimpleDateTimeData(); + var seedCopy = seed.GetSimpleDateTimeData(); if (includeReferenceDateInResults) { @@ -239,16 +238,15 @@ private HashSet GetDates(IDateTime seed, DateTime periodStart, DateTim // (only applicable where a COUNT is not specified) if (pattern.Count == int.MinValue) { - var incremented = seedCopy; - IncrementDate(ref incremented, pattern, pattern.Interval); - while (incremented < periodStart) + for(var incremented = pattern.IncrementDate(seedCopy) + ; incremented < periodStart + ; incremented = pattern.IncrementDate(incremented)) { seedCopy = incremented; - IncrementDate(ref incremented, pattern, pattern.Interval); } } - var expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern); + var expandBehavior = pattern.GetExpandBehaviorList(); var noCandidateIncrementCount = 0; var candidate = DateTime.MinValue; @@ -269,7 +267,7 @@ private HashSet GetDates(IDateTime seed, DateTime periodStart, DateTim break; } - var candidates = GetCandidates(seedCopy, pattern, expandBehavior); + var candidates = GetCandidates_(seedCopy, pattern, expandBehavior); if (candidates.Count > 0) { noCandidateIncrementCount = 0; @@ -308,22 +306,16 @@ private HashSet GetDates(IDateTime seed, DateTime periodStart, DateTim } } - IncrementDate(ref seedCopy, pattern, pattern.Interval); + seedCopy = pattern.IncrementDate(seedCopy); } return dates; } - /** - * Returns a list of possible dates generated from the applicable BY* rules, using the specified date as a seed. - * @param date the seed date - * @param value the type of date list to return - * @return a DateList - */ - - private List GetCandidates(DateTime date, RecurrencePattern pattern, bool?[] expandBehaviors) + /// Returns a list of possible dates generated from the applicable BY* rules, using the . + List GetCandidates_(DateTime seedDate, RecurrencePattern pattern, IReadOnlyList expandBehaviors) { - var dates = new List {date}; + var dates = new List {seedDate}; dates = GetMonthVariants(dates, pattern, expandBehaviors[0]); dates = GetWeekNoVariants(dates, pattern, expandBehaviors[1]); dates = GetYearDayVariants(dates, pattern, expandBehaviors[2]); @@ -336,12 +328,13 @@ private List GetCandidates(DateTime date, RecurrencePattern pattern, b return dates; } - /** - * Applies BYSETPOS rules to dates. Valid positions are from 1 to the size of the date list. Invalid - * positions are ignored. - * @param dates - */ - private List ApplySetPosRules(List dates, RecurrencePattern pattern) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + /// Selects only the positions from the list. + /// + /// Invalid positions are ignored. + /// + static List ApplySetPosRules(List dates, RecurrencePattern pattern) { // return if no SETPOS rules specified.. if (pattern.BySetPosition.Count == 0) @@ -354,21 +347,18 @@ private List ApplySetPosRules(List dates, RecurrencePattern var size = dates.Count; var setPosDates = pattern.BySetPosition - .Where(p => p > 0 && p <= size || p < 0 && p >= -size) //Protect against out of range access - .Select(p => p > 0 && p <= size - ? dates[p - 1] - : dates[size + p]) + .Where(p => p > 0 && p <= size + || p < 0 && p >= -size) //Protect against out of range access + .Select(p => p > 0 + ? dates[p - 1] //starting at 1! + : dates[size + p]) //starting at -1! .ToList(); return setPosDates; } - /** - * Applies BYMONTH rules specified in this Recur instance to the specified date list. If no BYMONTH rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - private List GetMonthVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + static List GetMonthVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.ByMonth.Count == 0) { @@ -389,13 +379,9 @@ private List GetMonthVariants(List dates, RecurrencePattern return dateSet.ToList(); } - /** - * Applies BYWEEKNO rules specified in this Recur instance to the specified date list. If no BYWEEKNO rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - private List GetWeekNoVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + List GetWeekNoVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.ByWeekNo.Count == 0) { @@ -445,14 +431,9 @@ private List GetWeekNoVariants(List dates, RecurrencePattern return weekNoDates; } - /** - * Applies BYYEARDAY rules specified in this Recur instance to the specified date list. If no BYYEARDAY rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - - private List GetYearDayVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + static List GetYearDayVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.ByYearDay.Count == 0) { @@ -497,14 +478,9 @@ private List GetYearDayVariants(List dates, RecurrencePatter return dates; } - /** - * Applies BYMONTHDAY rules specified in this Recur instance to the specified date list. If no BYMONTHDAY rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - - private List GetMonthDayVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + List GetMonthDayVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.ByMonthDay.Count == 0) { @@ -512,7 +488,7 @@ private List GetMonthDayVariants(List dates, RecurrencePatte } if (expand.Value) - { + {// expand behavior var monthDayDates = new List(); foreach (var date in dates) { @@ -559,16 +535,11 @@ select monthDay > 0 return dates; } - /** - * Applies BYDAY rules specified in this Recur instance to the specified date list. If no BYDAY rules are specified - * the date list is returned unmodified. - * @param dates - * @return - */ - - private List GetDayVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + List GetDayVariants(List dates, RecurrencePattern pattern, bool? expand) { - if (expand == null || pattern.ByDay.Count == 0) + if (expand == null || pattern.ByWeekDay.Count == 0) { return dates; } @@ -579,7 +550,7 @@ private List GetDayVariants(List dates, RecurrencePattern pa var weekDayDates = new List(); foreach (var date in dates) { - foreach (var day in pattern.ByDay) + foreach (var day in pattern.ByWeekDay) { weekDayDates.AddRange(GetAbsWeekDays(date, day, pattern)); } @@ -592,9 +563,9 @@ private List GetDayVariants(List dates, RecurrencePattern pa for (var i = dates.Count - 1; i >= 0; i--) { var date = dates[i]; - for (var j = 0; j < pattern.ByDay.Count; j++) + for (var j = 0; j < pattern.ByWeekDay.Count; j++) { - var weekDay = pattern.ByDay[j]; + var weekDay = pattern.ByWeekDay[j]; if (weekDay.DayOfWeek.Equals(date.DayOfWeek)) { // If no offset is specified, simply test the day of week! @@ -613,15 +584,8 @@ private List GetDayVariants(List dates, RecurrencePattern pa return dates; } - /** - * Returns a list of applicable dates corresponding to the specified week day in accordance with the frequency - * specified by this recurrence rule. - * @param date - * @param weekDay - * @return - */ - - private List GetAbsWeekDays(DateTime date, WeekDay weekDay, RecurrencePattern pattern) + /// Returns a list of times with frequency on the . + List GetAbsWeekDays(DateTime date, WeekDay weekDay, RecurrencePattern pattern) { var days = new List(); @@ -701,19 +665,15 @@ private List GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence date = date.AddDays(7); } } - return GetOffsetDates(days, weekDay.Offset); + return GetOffsetDate(days, weekDay.Offset); } - /** - * Returns a single-element sublist containing the element of list at offset. Valid - * offsets are from 1 to the size of the list. If an invalid offset is supplied, all elements from list - * are added to sublist. - * @param list - * @param offset - * @param sublist - */ - - private List GetOffsetDates(List dates, int offset) + /// Returns a single-element sublist containing [-1]. + /// + /// Valid are from -.Length to .Length. + /// + /// an empty list, when an invalid offset is supplied. + static List GetOffsetDate(List dates, int offset) { if (offset == int.MinValue) { @@ -733,14 +693,9 @@ private List GetOffsetDates(List dates, int offset) return offsetDates; } - /** - * Applies BYHOUR rules specified in this Recur instance to the specified date list. If no BYHOUR rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - - private List GetHourVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + static List GetHourVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.ByHour.Count == 0) { @@ -783,14 +738,9 @@ private List GetHourVariants(List dates, RecurrencePattern p return dates; } - /** - * Applies BYMINUTE rules specified in this Recur instance to the specified date list. If no BYMINUTE rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - - private List GetMinuteVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + static List GetMinuteVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.ByMinute.Count == 0) { @@ -833,14 +783,9 @@ private List GetMinuteVariants(List dates, RecurrencePattern return dates; } - /** - * Applies BYSECOND rules specified in this Recur instance to the specified date list. If no BYSECOND rules are - * specified the date list is returned unmodified. - * @param dates - * @return - */ - - private List GetSecondVariants(List dates, RecurrencePattern pattern, bool? expand) + /// Applies rules specified in the to the list. + /// the full when no Rules were specified. + static List GetSecondVariants(List dates, RecurrencePattern pattern, bool? expand) { if (expand == null || pattern.BySecond.Count == 0) { @@ -883,18 +828,18 @@ private List GetSecondVariants(List dates, RecurrencePattern return dates; } - private Period CreatePeriod(DateTime dt, IDateTime referenceDate) + /// Create a period with unspecified Duration from the . + static Period CreatePeriod(DateTime startDateTime, IDateTime referenceDate) { // Turn each resulting date/time into an IDateTime and associate it // with the reference date. - IDateTime newDt = new CalDateTime(dt, referenceDate.TzId); + IDateTime newDt = new CalDateTime(startDateTime, referenceDate.TzId); // NOTE: fixes bug #2938007 - hasTime missing newDt.HasTime = referenceDate.HasTime; newDt.AssociateWith(referenceDate); - // Create a period from the new date/time. return new Period(newDt); } diff --git a/src/Ical.Net/Evaluation/RecurrenceUtil.cs b/src/Ical.Net/Evaluation/RecurrenceUtil.cs index 8320954a8..8d50b0b94 100644 --- a/src/Ical.Net/Evaluation/RecurrenceUtil.cs +++ b/src/Ical.Net/Evaluation/RecurrenceUtil.cs @@ -6,21 +6,20 @@ namespace Ical.Net.Evaluation { - internal class RecurrenceUtil + internal static class RecurrenceUtil { - public static void ClearEvaluation(IRecurrable recurrable) + public static void ClearEvaluation(this IRecurrable recurrable) { var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; evaluator?.Clear(); } - public static HashSet GetOccurrences(IRecurrable recurrable, IDateTime dt, bool includeReferenceDateInResults) => GetOccurrences(recurrable, - new CalDateTime(dt.AsSystemLocal.Date), new CalDateTime(dt.AsSystemLocal.Date.AddDays(1).AddSeconds(-1)), includeReferenceDateInResults); + public static HashSet GetOccurrences(this IRecurrable recurrable, IDateTime dt, bool includeReferenceDateInResults) + => GetOccurrences(recurrable, new CalDateTime(dt.AsSystemLocal.Date), new CalDateTime(dt.AsSystemLocal.Date.AddDays(1).AddSeconds(-1)), includeReferenceDateInResults); - public static HashSet GetOccurrences(IRecurrable recurrable, IDateTime periodStart, IDateTime periodEnd, bool includeReferenceDateInResults) + public static HashSet GetOccurrences(this IRecurrable recurrable, IDateTime periodStart, IDateTime periodEnd, bool includeReferenceDateInResults) { - var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; - if (evaluator == null || recurrable.Start == null) + if (recurrable.Start == null || !(recurrable.GetService(typeof(IEvaluator)) is IEvaluator evaluator)) { return new HashSet(); } @@ -35,7 +34,7 @@ public static HashSet GetOccurrences(IRecurrable recurrable, IDateTi periodStart.TzId = start.TzId; periodEnd.TzId = start.TzId; - var periods = evaluator.Evaluate(start, DateUtil.GetSimpleDateTimeData(periodStart), DateUtil.GetSimpleDateTimeData(periodEnd), + var periods = evaluator.Evaluate(start, periodStart.GetSimpleDateTimeData(), periodEnd.GetSimpleDateTimeData(), includeReferenceDateInResults); var otherOccurrences = from p in periods @@ -47,49 +46,24 @@ where endTime.GreaterThan(periodStart) && p.StartTime.LessThanOrEqual(periodEnd) return occurrences; } - public static bool?[] GetExpandBehaviorList(RecurrencePattern p) - { - // See the table in RFC 5545 Section 3.3.10 (Page 43). - switch (p.Frequency) - { - case FrequencyType.Minutely: - return new bool?[] {false, null, false, false, false, false, false, true, false}; - case FrequencyType.Hourly: - return new bool?[] {false, null, false, false, false, false, true, true, false}; - case FrequencyType.Daily: - return new bool?[] {false, null, null, false, false, true, true, true, false}; - case FrequencyType.Weekly: - return new bool?[] {false, null, null, null, true, true, true, true, false}; - case FrequencyType.Monthly: - { - var row = new bool?[] {false, null, null, true, true, true, true, true, false}; - - // Limit if BYMONTHDAY is present; otherwise, special expand for MONTHLY. - if (p.ByMonthDay.Count > 0) - { - row[4] = false; - } - - return row; - } - case FrequencyType.Yearly: - { - var row = new bool?[] {true, true, true, true, true, true, true, true, false}; - - // Limit if BYYEARDAY or BYMONTHDAY is present; otherwise, - // special expand for WEEKLY if BYWEEKNO present; otherwise, - // special expand for MONTHLY if BYMONTH present; otherwise, - // special expand for YEARLY. - if (p.ByYearDay.Count > 0 || p.ByMonthDay.Count > 0) - { - row[4] = false; - } - - return row; - } - default: - return new bool?[] {false, null, false, false, false, false, false, false, false}; - } - } + /// Nullable Flags to control whether to use the .By* Values for Restriction or Expansion or not at all + /// + /// See the table in RFC 5545 Section 3.3.10 (Page 43). + /// + public static bool?[] GetExpandBehaviorList(this RecurrencePattern p) + => p.Frequency switch { // Limit if BYMONTHDAY is present; otherwise, special expand for MONTHLY. + FrequencyType.Monthly => new bool?[] {false, null, null, true, p.ByMonthDay.Count <= 0, true, true, true, false}, + FrequencyType.Minutely => new bool?[] {false, null, false, false, false, false, false, true, false}, + FrequencyType.Hourly => new bool?[] {false, null, false, false, false, false, true, true, false}, + FrequencyType.Daily => new bool?[] {false, null, null, false, false, true, true, true, false}, + FrequencyType.Weekly => new bool?[] {false, null, null, null, true, true, true, true, false}, + FrequencyType.Yearly + // Limit if BYYEARDAY or BYMONTHDAY is present; otherwise, + // special expand for WEEKLY if BYWEEKNO present; otherwise, + // special expand for MONTHLY if BYMONTH present; otherwise, + // special expand for YEARLY. + => new bool?[] {true, true, true, true, p.ByYearDay.Count <= 0 && p.ByMonthDay.Count <= 0, true, true, true, false}, + _ => new bool?[] {false, null, false, false, false, false, false, false, false} + }; } } \ No newline at end of file diff --git a/src/Ical.Net/Evaluation/RecurringEvaluator.cs b/src/Ical.Net/Evaluation/RecurringEvaluator.cs index e249a2b82..3d4cb8c56 100644 --- a/src/Ical.Net/Evaluation/RecurringEvaluator.cs +++ b/src/Ical.Net/Evaluation/RecurringEvaluator.cs @@ -17,24 +17,17 @@ public RecurringEvaluator(IRecurrable obj) // We're not sure if the object is a calendar object // or a calendar data type, so we need to assign // the associated object manually - if (obj is ICalendarObject) + if (obj is ICalendarObject calendarObject) { - AssociatedObject = (ICalendarObject) obj; + AssociatedObject = calendarObject; } - if (obj is ICalendarDataType) + if (obj is ICalendarDataType dt) { - var dt = (ICalendarDataType) obj; AssociatedObject = dt.AssociatedObject; } } - /// - /// Evaulates the RRule component, and adds each specified Period to the Periods collection. - /// - /// - /// The beginning date of the range to evaluate. - /// The end date of the range to evaluate. - /// + /// Evaluates the RRule component, and adds each specified Period to the Periods collection. protected HashSet EvaluateRRule(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) { if (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any()) @@ -42,8 +35,7 @@ protected HashSet EvaluateRRule(IDateTime referenceDate, DateTime period return new HashSet(); } - var evaluator = Recurrable.RecurrenceRules.First().GetService(typeof(IEvaluator)) as IEvaluator; - if (evaluator == null) + if (!(Recurrable.RecurrenceRules.First().GetService(typeof(IEvaluator)) is IEvaluator evaluator)) { return new HashSet(); } @@ -56,7 +48,7 @@ protected HashSet EvaluateRRule(IDateTime referenceDate, DateTime period return periods; } - /// Evalates the RDate component, and adds each specified DateTime or Period to the Periods collection. + /// Evaluates the RDate component, and adds each specified DateTime or Period to the Periods collection. protected HashSet EvaluateRDate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd) { if (Recurrable.RecurrenceDates == null || !Recurrable.RecurrenceDates.Any()) @@ -64,16 +56,11 @@ protected HashSet EvaluateRDate(IDateTime referenceDate, DateTime period return new HashSet(); } - var recurrences = new HashSet(Recurrable.RecurrenceDates.SelectMany(rdate => rdate)); + var recurrences = new HashSet(Recurrable.RecurrenceDates.SelectMany(rDate => rDate)); return recurrences; } - /// - /// Evaulates the ExRule component, and excludes each specified DateTime from the Periods collection. - /// - /// - /// The beginning date of the range to evaluate. - /// The end date of the range to evaluate. + /// Evaluates the ExRule component, and excludes each specified DateTime from the Periods collection. protected HashSet EvaluateExRule(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd) { if (Recurrable.ExceptionRules == null || !Recurrable.ExceptionRules.Any()) @@ -81,8 +68,7 @@ protected HashSet EvaluateExRule(IDateTime referenceDate, DateTime perio return new HashSet(); } - var evaluator = Recurrable.ExceptionRules.First().GetService(typeof(IEvaluator)) as IEvaluator; - if (evaluator == null) + if (!(Recurrable.ExceptionRules.First().GetService(typeof(IEvaluator)) is IEvaluator evaluator)) { return new HashSet(); } @@ -92,9 +78,7 @@ protected HashSet EvaluateExRule(IDateTime referenceDate, DateTime perio return exRuleExclusions; } - /// - /// Evalates the ExDate component, and excludes each specified DateTime or Period from the Periods collection. - /// + /// Evaluates the ExDate component, and excludes each specified DateTime or Period from the Periods collection. /// /// The beginning date of the range to evaluate. /// The end date of the range to evaluate. @@ -113,31 +97,31 @@ public override HashSet Evaluate(IDateTime referenceDate, DateTime perio { Periods.Clear(); - var rruleOccurrences = EvaluateRRule(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + var rRuleOccurrences = EvaluateRRule(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); if (includeReferenceDateInResults) { - rruleOccurrences.UnionWith(new[] { new Period(referenceDate), }); + rRuleOccurrences.UnionWith(new[] { new Period(referenceDate) }); } - var rdateOccurrences = EvaluateRDate(referenceDate, periodStart, periodEnd); + var rDateOccurrences = EvaluateRDate(referenceDate, periodStart, periodEnd); var exRuleExclusions = EvaluateExRule(referenceDate, periodStart, periodEnd); var exDateExclusions = EvaluateExDate(referenceDate, periodStart, periodEnd); //Exclusions trump inclusions - Periods.UnionWith(rruleOccurrences); - Periods.UnionWith(rdateOccurrences); + Periods.UnionWith(rRuleOccurrences); + Periods.UnionWith(rDateOccurrences); Periods.ExceptWith(exRuleExclusions); Periods.ExceptWith(exDateExclusions); var dateOverlaps = FindDateOverlaps(exDateExclusions); Periods.ExceptWith(dateOverlaps); - if (EvaluationStartBounds == DateTime.MaxValue || EvaluationStartBounds > periodStart) + if (EvaluationStartBounds > periodStart) { EvaluationStartBounds = periodStart; } - if (EvaluationEndBounds == DateTime.MinValue || EvaluationEndBounds < periodEnd) + if (EvaluationEndBounds < periodEnd) { EvaluationEndBounds = periodEnd; } @@ -145,7 +129,7 @@ public override HashSet Evaluate(IDateTime referenceDate, DateTime perio return Periods; } - private HashSet FindDateOverlaps(HashSet dates) + HashSet FindDateOverlaps(HashSet dates) { var datesWithoutTimes = new HashSet(dates.Where(d => d.StartTime.Value.TimeOfDay == TimeSpan.Zero).Select(d => d.StartTime.Value)); var overlaps = new HashSet(Periods.Where(p => datesWithoutTimes.Contains(p.StartTime.Value.Date))); diff --git a/src/Ical.Net/Evaluation/TimeZoneEvaluator.cs b/src/Ical.Net/Evaluation/TimeZoneEvaluator.cs index 3bc040f00..156d9d07b 100644 --- a/src/Ical.Net/Evaluation/TimeZoneEvaluator.cs +++ b/src/Ical.Net/Evaluation/TimeZoneEvaluator.cs @@ -10,23 +10,18 @@ public class TimeZoneEvaluator : Evaluator { protected VTimeZone TimeZone { get; set; } - private List _occurrences; - public virtual List Occurrences - { - get => _occurrences; - set => _occurrences = value; - } + public List Occurrences { get; set; } public TimeZoneEvaluator(VTimeZone tz) { TimeZone = tz; - _occurrences = new List(); + Occurrences = new List(); } void ProcessOccurrences(IDateTime referenceDate) { // Sort the occurrences by start time - _occurrences.Sort( + Occurrences.Sort( delegate (Occurrence o1, Occurrence o2) { if (o1.Period?.StartTime == null) @@ -39,10 +34,10 @@ void ProcessOccurrences(IDateTime referenceDate) } ); - for (var i = 0; i < _occurrences.Count; i++) + for (var i = 0; i < Occurrences.Count; i++) { - var curr = _occurrences[i]; - var next = i < _occurrences.Count - 1 ? _occurrences[i + 1] : null; + var curr = Occurrences[i]; + var next = i < Occurrences.Count - 1 ? Occurrences[i + 1] : null; // Determine end times for our periods, overwriting previously calculated end times. // This is important because we don't want to overcalculate our time zone information, @@ -58,7 +53,7 @@ void ProcessOccurrences(IDateTime referenceDate) public override void Clear() { base.Clear(); - _occurrences.Clear(); + Occurrences.Clear(); } public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) @@ -109,9 +104,9 @@ public override HashSet Evaluate(IDateTime referenceDate, DateTime perio { Periods.Add(period); var o = new Occurrence(curr, period); - if (!_occurrences.Contains(o)) + if (!Occurrences.Contains(o)) { - _occurrences.Add(o); + Occurrences.Add(o); } } diff --git a/src/Ical.Net/Evaluation/TodoEvaluator.cs b/src/Ical.Net/Evaluation/TodoEvaluator.cs index 37a943ed2..78f535a44 100644 --- a/src/Ical.Net/Evaluation/TodoEvaluator.cs +++ b/src/Ical.Net/Evaluation/TodoEvaluator.cs @@ -46,10 +46,10 @@ public void EvaluateToPreviousOccurrence(IDateTime completedDate, IDateTime curr } } - Evaluate(Todo.Start, DateUtil.GetSimpleDateTimeData(beginningDate), DateUtil.GetSimpleDateTimeData(currDt).AddTicks(1), true); + Evaluate(Todo.Start, beginningDate.GetSimpleDateTimeData(), currDt.GetSimpleDateTimeData().AddTicks(1), true); } - public void DetermineStartingRecurrence(PeriodList rdate, ref IDateTime referenceDateTime) + public static void DetermineStartingRecurrence(PeriodList rdate, ref IDateTime referenceDateTime) { var evaluator = rdate.GetService(); @@ -69,7 +69,7 @@ public void DetermineStartingRecurrence(RecurrencePattern recur, ref IDateTime r else { var dtVal = referenceDateTime.Value; - IncrementDate(ref dtVal, recur, -recur.Interval); + dtVal = recur.DecrementDate(dtVal); referenceDateTime.Value = dtVal; } } diff --git a/src/Ical.Net/ICopyable.cs b/src/Ical.Net/ICopyable.cs index 9b169a54b..412abe604 100644 --- a/src/Ical.Net/ICopyable.cs +++ b/src/Ical.Net/ICopyable.cs @@ -2,18 +2,18 @@ { public interface ICopyable { - /// - /// Copies all relevant fields/properties from - /// the target object to the current one. - /// + /// Copies all relevant fields/properties from the target object to the current one. void CopyFrom(ICopyable obj); - /// - /// Returns a deep copy of the current object. For the most part, this is only necessary when working with mutable reference types, - /// (i.e. iCalDateTime). For most other types, it's unnecessary overhead. The pattern that identifies whether it's necessary to copy - /// or not is whether arithmetic operations mutate fields or properties. iCalDateTime is a good example where + and - would otherwise - /// change the Value of the underlying DateTime. - /// + /// Returns a deep copy of the current object. + /// + /// For the most part, this is only necessary when working with mutable reference types, (i.e. iCalDateTime). + /// For most other types, it's unnecessary overhead. + /// The pattern that identifies whether it's necessary to copy or not + /// is whether arithmetic operations mutate fields or properties. + /// iCalDateTime is a good example + /// where + and - would otherwise change the Value of the underlying DateTime. + /// T Copy(); } } \ No newline at end of file diff --git a/src/Ical.Net/ILoadable.cs b/src/Ical.Net/ILoadable.cs index c238d584e..bee11f43f 100644 --- a/src/Ical.Net/ILoadable.cs +++ b/src/Ical.Net/ILoadable.cs @@ -1,22 +1,17 @@ -using System; +#nullable enable +using System; namespace Ical.Net { public interface ILoadable { - /// - /// Gets whether or not the object has been loaded. - /// + /// Gets whether or not the object has been loaded. bool IsLoaded { get; } - /// - /// An event that fires when the object has been loaded. - /// - event EventHandler Loaded; + /// An event that fires when the object has been loaded. + event EventHandler? Loaded; - /// - /// Fires the Loaded event. - /// + /// Fires the Loaded event. void OnLoaded(); } } \ No newline at end of file diff --git a/src/Ical.Net/IMergeable.cs b/src/Ical.Net/IMergeable.cs index 6ace17705..0fb2afc88 100644 --- a/src/Ical.Net/IMergeable.cs +++ b/src/Ical.Net/IMergeable.cs @@ -1,10 +1,9 @@ namespace Ical.Net { + /// public interface IMergeable - { - /// - /// Merges this object with another. - /// + { + /// Merges this object with another. void MergeWith(IMergeable obj); } } \ No newline at end of file diff --git a/src/Ical.Net/IServiceProvider.cs b/src/Ical.Net/IServiceProvider.cs index 26793921b..9963316cb 100644 --- a/src/Ical.Net/IServiceProvider.cs +++ b/src/Ical.Net/IServiceProvider.cs @@ -4,8 +4,8 @@ namespace Ical.Net { public interface IServiceProvider { - object GetService(string name); - object GetService(Type type); + object? GetService(string name); + object? GetService(Type type); T GetService(); T GetService(string name); void SetService(string name, object obj); diff --git a/src/Ical.Net/Ical.Net.csproj b/src/Ical.Net/Ical.Net.csproj index c726486a0..c4ddcd8d4 100644 --- a/src/Ical.Net/Ical.Net.csproj +++ b/src/Ical.Net/Ical.Net.csproj @@ -7,7 +7,7 @@ MIT https://github.com/rianjs/ical.net hhttps://github.com/rianjs/ical.net/blob/master/release-notes.md - netstandard2.0;net50 + netstandard2.0;net50;net48 Rian Stockbower, Douglas Day, & Contributors true @@ -17,12 +17,13 @@ Ical.Net true An iCalendar (RFC 5545) library. See https://github.com/rianjs/ical.net for details. + enable bin\Release\Ical.Net.xml - + diff --git a/src/Ical.Net/ParameterList.cs b/src/Ical.Net/ParameterList.cs index f43c6d184..dbd852038 100644 --- a/src/Ical.Net/ParameterList.cs +++ b/src/Ical.Net/ParameterList.cs @@ -5,7 +5,7 @@ namespace Ical.Net { public class ParameterList : GroupedValueList, IParameterCollection { - public virtual void SetParent(ICalendarObject parent) + public void SetParent(ICalendarObject parent) { foreach (var parameter in this) { @@ -13,13 +13,10 @@ public virtual void SetParent(ICalendarObject parent) } } - public virtual void Add(string name, string value) - { - Add(new CalendarParameter(name, value)); - } + public void Add(string name, string value) => Add(new CalendarParameter(name, value)); - public virtual string Get(string name) => Get(name); + public string Get(string name) => Get(name); - public virtual IList GetMany(string name) => GetMany(name); + public IList GetMany(string name) => GetMany(name); } } \ No newline at end of file diff --git a/src/Ical.Net/Proxies/CalendarObjectListProxy.cs b/src/Ical.Net/Proxies/CalendarObjectListProxy.cs index 09a6170b7..632f7c219 100644 --- a/src/Ical.Net/Proxies/CalendarObjectListProxy.cs +++ b/src/Ical.Net/Proxies/CalendarObjectListProxy.cs @@ -9,6 +9,6 @@ public class CalendarObjectListProxy : GroupedCollectionProxy list) : base(list) {} - public virtual TType this[int index] => this.Skip(index).FirstOrDefault(); + public TType this[int index] => this.Skip(index).FirstOrDefault(); } } \ No newline at end of file diff --git a/src/Ical.Net/Proxies/ParameterCollectionProxy.cs b/src/Ical.Net/Proxies/ParameterCollectionProxy.cs index ed8c5ed09..e68c96075 100644 --- a/src/Ical.Net/Proxies/ParameterCollectionProxy.cs +++ b/src/Ical.Net/Proxies/ParameterCollectionProxy.cs @@ -13,7 +13,7 @@ protected GroupedValueList public ParameterCollectionProxy(IGroupedList realObject) : base(realObject) {} - public virtual void SetParent(ICalendarObject parent) + public void SetParent(ICalendarObject? parent) { foreach (var parameter in this) { @@ -21,21 +21,18 @@ public virtual void SetParent(ICalendarObject parent) } } - public virtual void Add(string name, string value) - { - RealObject.Add(new CalendarParameter(name, value)); - } + public void Add(string name, string value) => RealObject.Add(new CalendarParameter(name, value)); - public virtual string Get(string name) + public string Get(string name) { var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); return parameter?.Value; } - public virtual IList GetMany(string name) => new GroupedValueListProxy(Parameters, name); + public IList GetMany(string name) => new GroupedValueListProxy(Parameters, name); - public virtual void Set(string name, string value) + public void Set(string name, string value) { var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); @@ -49,7 +46,7 @@ public virtual void Set(string name, string value) } } - public virtual void Set(string name, IEnumerable values) + public void Set(string name, IEnumerable values) { var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); @@ -63,13 +60,13 @@ public virtual void Set(string name, IEnumerable values) } } - public virtual int IndexOf(CalendarParameter obj) => 0; + public int IndexOf(CalendarParameter obj) => 0; - public virtual void Insert(int index, CalendarParameter item) {} + public void Insert(int index, CalendarParameter item) {} - public virtual void RemoveAt(int index) {} + public void RemoveAt(int index) {} - public virtual CalendarParameter this[int index] + public CalendarParameter this[int index] { get { return Parameters[index]; } set { } diff --git a/src/Ical.Net/Proxies/UniqueComponentListProxy.cs b/src/Ical.Net/Proxies/UniqueComponentListProxy.cs index 45e6cd1f6..88d53ba2f 100644 --- a/src/Ical.Net/Proxies/UniqueComponentListProxy.cs +++ b/src/Ical.Net/Proxies/UniqueComponentListProxy.cs @@ -6,19 +6,21 @@ namespace Ical.Net.Proxies { + /// Typed Proxy/Filter for a List of generic Instances + /// the Type to filter for public class UniqueComponentListProxy : CalendarObjectListProxy, IUniqueComponentList where TComponentType : class, IUniqueComponent { - private readonly Dictionary _lookup; + readonly Dictionary _lookup; public UniqueComponentListProxy(IGroupedCollection children) : base(children) { _lookup = new Dictionary(); } - private TComponentType Search(string uid) + TComponentType Search(string uid) { if (_lookup.TryGetValue(uid, out var componentType)) { @@ -29,14 +31,14 @@ private TComponentType Search(string uid) if (item == null) { - return default(TComponentType); + return default; } _lookup[uid] = item; return item; } - public virtual TComponentType this[string uid] + public TComponentType this[string uid] { get => Search(uid); set diff --git a/src/Ical.Net/Serialization/CalendarComponentFactory.cs b/src/Ical.Net/Serialization/CalendarComponentFactory.cs index 6f68ef680..952db5519 100644 --- a/src/Ical.Net/Serialization/CalendarComponentFactory.cs +++ b/src/Ical.Net/Serialization/CalendarComponentFactory.cs @@ -4,44 +4,27 @@ namespace Ical.Net.Serialization { public class CalendarComponentFactory { - public virtual ICalendarComponent Build(string objectName) + public static ICalendarComponent Build(string objectName) { - ICalendarComponent c; var name = objectName.ToUpper(); - - switch (name) - { - case Components.Alarm: - c = new Alarm(); - break; - case EventStatus.Name: - c = new CalendarEvent(); - break; - case Components.Freebusy: - c = new FreeBusy(); - break; - case JournalStatus.Name: - c = new Journal(); - break; - case Components.Timezone: - c = new VTimeZone(); - break; - case TodoStatus.Name: - c = new Todo(); - break; - case Components.Calendar: - c = new Calendar(); - break; - case Components.Daylight: - case Components.Standard: - c = new VTimeZoneInfo(); - break; - default: - c = new CalendarComponent(); - break; - } + var c = Create_(name); c.Name = name; return c; } + + static ICalendarComponent Create_(string name) + => name switch + { + Components.Alarm => new Alarm(), + EventStatus.Name => new CalendarEvent(), + Components.Freebusy => new FreeBusy(), + JournalStatus.Name => new Journal(), + Components.Timezone => new VTimeZone(), + TodoStatus.Name => new Todo(), + Components.Calendar => new Calendar(), + Components.Daylight => new VTimeZoneInfo(), + Components.Standard => new VTimeZoneInfo(), + _ => new CalendarComponent() + }; } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/CalendarSerializer.cs b/src/Ical.Net/Serialization/CalendarSerializer.cs index 9b36efd43..cb8045979 100644 --- a/src/Ical.Net/Serialization/CalendarSerializer.cs +++ b/src/Ical.Net/Serialization/CalendarSerializer.cs @@ -6,7 +6,7 @@ namespace Ical.Net.Serialization { public class CalendarSerializer : ComponentSerializer { - private readonly Calendar _calendar; + readonly Calendar _calendar; public CalendarSerializer() :this(new SerializationContext()) { } @@ -18,28 +18,27 @@ public CalendarSerializer(Calendar cal) public CalendarSerializer(SerializationContext ctx) : base(ctx) {} - public virtual string SerializeToString() => SerializeToString(_calendar); + public string SerializeToString() => SerializeToString(_calendar); protected override IComparer PropertySorter => new CalendarPropertySorter(); public override string SerializeToString(object obj) { - if (obj is Calendar) + if (!(obj is Calendar calendar)) { - // If we're serializing a calendar, we should indicate that we're using ical.net to do the work - var calendar = (Calendar) obj; - calendar.Version = LibraryMetadata.Version; - calendar.ProductId = LibraryMetadata.ProdId; - - return base.SerializeToString(calendar); + return base.SerializeToString(obj); } + // If we're serializing a calendar, we should indicate that we're using ical.net to do the work + calendar.Version = LibraryMetadata.Version; + calendar.ProductId = LibraryMetadata.ProdId; + + return base.SerializeToString(calendar); - return base.SerializeToString(obj); } public override object Deserialize(TextReader tr) => null; - private class CalendarPropertySorter : IComparer + class CalendarPropertySorter : IComparer { public int Compare(ICalendarProperty x, ICalendarProperty y) { diff --git a/src/Ical.Net/Serialization/ComponentSerializer.cs b/src/Ical.Net/Serialization/ComponentSerializer.cs index bc16385a3..b13fdfe43 100644 --- a/src/Ical.Net/Serialization/ComponentSerializer.cs +++ b/src/Ical.Net/Serialization/ComponentSerializer.cs @@ -18,13 +18,10 @@ public ComponentSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(CalendarComponent); - public override string SerializeToString(object obj) - { - if (!(obj is ICalendarComponent c)) - { - return null; - } + public override string SerializeToString(object obj) => Serialize((ICalendarComponent)obj); + public string Serialize(ICalendarComponent c) + { var sb = new StringBuilder(); var upperName = c.Name.ToUpperInvariant(); sb.Append(TextUtil.FoldLines($"BEGIN:{upperName}")); @@ -55,11 +52,11 @@ public override string SerializeToString(object obj) return sb.ToString(); } - public override object Deserialize(TextReader tr) => null; + public override object? Deserialize(TextReader tr) => null; public class PropertyAlphabetizer : IComparer { - public int Compare(ICalendarProperty x, ICalendarProperty y) + public int Compare(ICalendarProperty? x, ICalendarProperty? y) { if (x == y) { diff --git a/src/Ical.Net/Serialization/DataTypeMapper.cs b/src/Ical.Net/Serialization/DataTypeMapper.cs index 51d175e41..48084a096 100644 --- a/src/Ical.Net/Serialization/DataTypeMapper.cs +++ b/src/Ical.Net/Serialization/DataTypeMapper.cs @@ -9,14 +9,14 @@ namespace Ical.Net.Serialization internal class DataTypeMapper { - private class PropertyMapping + class PropertyMapping { public Type ObjectType { get; set; } public TypeResolverDelegate Resolver { get; set; } public bool AllowsMultipleValuesPerProperty { get; set; } } - private readonly IDictionary _propertyMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + readonly IDictionary _propertyMap = new Dictionary(StringComparer.OrdinalIgnoreCase); public DataTypeMapper() { @@ -66,17 +66,13 @@ protected Type ResolveStatusProperty(object context) return null; } - switch (obj.Parent) + return obj.Parent switch { - case CalendarEvent _: - return typeof (EventStatus); - case Todo _: - return typeof (TodoStatus); - case Journal _: - return typeof (JournalStatus); - } - - return null; + CalendarEvent _ => typeof(EventStatus), + Todo _ => typeof(TodoStatus), + Journal _ => typeof(JournalStatus), + _ => null + }; } public void AddPropertyMapping(string name, Type objectType, bool allowsMultipleValues) @@ -119,7 +115,7 @@ public void RemovePropertyMapping(string name) } } - public virtual bool GetPropertyAllowsMultipleValues(object obj) + public bool GetPropertyAllowsMultipleValues(object obj) { var p = obj as ICalendarProperty; return !string.IsNullOrWhiteSpace(p?.Name) @@ -127,7 +123,7 @@ public virtual bool GetPropertyAllowsMultipleValues(object obj) && m.AllowsMultipleValuesPerProperty; } - public virtual Type GetPropertyMapping(object obj) + public Type GetPropertyMapping(object obj) { var p = obj as ICalendarProperty; if (p?.Name == null) diff --git a/src/Ical.Net/Serialization/DataTypeSerializerFactory.cs b/src/Ical.Net/Serialization/DataTypeSerializerFactory.cs index 3fd983437..a17ec28cd 100644 --- a/src/Ical.Net/Serialization/DataTypeSerializerFactory.cs +++ b/src/Ical.Net/Serialization/DataTypeSerializerFactory.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection; using Ical.Net.DataTypes; using Ical.Net.Serialization.DataTypes; @@ -16,7 +15,7 @@ public class DataTypeSerializerFactory : ISerializerFactory /// /// The type of object to be serialized. /// The serialization context. - public virtual ISerializer Build(Type objectType, SerializationContext ctx) + public ISerializer Build(Type objectType, SerializationContext ctx) { if (objectType != null) { diff --git a/src/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs b/src/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs index e0f2a7717..5973915ea 100644 --- a/src/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs @@ -12,14 +12,10 @@ public AttachmentSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (Attachment); - public override string SerializeToString(object obj) - { - var a = obj as Attachment; - if (a == null) - { - return null; - } + public override string? SerializeToString(object? obj) => obj is Attachment a ? SerializeToString(a) : null; + public string? SerializeToString(Attachment a) + { if (a.Uri != null) { if (a.Parameters.ContainsKey("VALUE")) @@ -44,7 +40,7 @@ public override string SerializeToString(object obj) return Encode(a, a.Data); } - public Attachment Deserialize(string attachment) + public Attachment? Deserialize(string attachment) { try { @@ -61,7 +57,7 @@ public Attachment Deserialize(string attachment) if (valueType == typeof(byte[])) { // If the VALUE type is specifically set to BINARY, - // then set the Data property instead. + // then set the Data property instead. return new Attachment(data) { ValueEncoding = a.ValueEncoding, diff --git a/src/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs b/src/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs index 3c635b0f4..42b95f762 100644 --- a/src/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs @@ -12,15 +12,11 @@ public AttendeeSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (Attendee); - public override string SerializeToString(object obj) - { - var a = obj as Attendee; - return a?.Value == null - ? null - : Encode(a, a.Value.OriginalString); - } + public override string? SerializeToString(object? obj) => obj is Attendee a ? SerializeToString(a): null; + + public string? SerializeToString(Attendee a) => Encode(a, a.Value.OriginalString); - public Attendee Deserialize(string attendee) + public Attendee? Deserialize(string attendee) { try { @@ -44,6 +40,6 @@ public Attendee Deserialize(string attendee) return null; } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + public override object? Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs b/src/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs index 9ae792cca..c48a5d49c 100644 --- a/src/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs @@ -9,7 +9,7 @@ protected DataTypeSerializer() {} protected DataTypeSerializer(SerializationContext ctx) : base(ctx) {} - protected virtual ICalendarDataType CreateAndAssociate() + protected virtual ICalendarDataType? CreateAndAssociate() { // Create an instance of the object if (!(Activator.CreateInstance(TargetType) is ICalendarDataType dt)) diff --git a/src/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs b/src/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs index b6a60d75c..8d51d21a3 100644 --- a/src/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs @@ -12,7 +12,7 @@ public DateTimeSerializer() { } public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } - private DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + static DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) { var dt = DateTime.MinValue; @@ -39,20 +39,19 @@ private DateTime CoerceDateTime(int year, int month, int day, int hour, int minu public override Type TargetType => typeof (CalDateTime); - public override string SerializeToString(object obj) - { - var dt = obj as IDateTime; - if (dt == null) - { - return null; - } - - // RFC 5545 3.3.5: - // The date with UTC time, or absolute time, is identified by a LATIN - // CAPITAL LETTER Z suffix character, the UTC designator, appended to - // the time value. The "TZID" property parameter MUST NOT be applied to DATE-TIME - // properties whose time values are specified in UTC. - + public override string? SerializeToString(object? obj) => obj is IDateTime dt ? SerializeToString(dt) : null; + + /// + /// + /// + /// + /// RFC 5545 3.3.5: The date with UTC time, or absolute time, + /// is identified by a LATIN CAPITAL LETTER Z suffix character, + /// the UTC designator, appended to the time value. + /// The "TZID" property parameter MUST NOT be applied to DATE-TIME properties + /// whose time values are specified in UTC. + /// + public string? SerializeToString(IDateTime dt) { var kind = dt.IsUtc ? DateTimeKind.Utc : DateTimeKind.Local; @@ -92,16 +91,21 @@ public override string SerializeToString(object obj) return Encode(dt, value.ToString()); } - private const RegexOptions _ciCompiled = RegexOptions.Compiled | RegexOptions.IgnoreCase; - internal static readonly Regex DateOnlyMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))?$", _ciCompiled); - internal static readonly Regex FullDateTimePatternMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))T((\d{2})(\d{2})(\d{2})(Z)?)$", _ciCompiled); + const RegexOptions _ciCompiled = RegexOptions.Compiled | RegexOptions.IgnoreCase; + internal static readonly Regex DateOnlyMatch = new Regex( + @"^((\d{4})(\d{2})(\d{2}))?$", _ciCompiled); + internal static readonly Regex FullDateTimePatternMatch = new Regex( + @"^((\d{4})(\d{2})(\d{2}))\w((\d{2})(\d{2})(\d{2})(Z)?)$", _ciCompiled); - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { var value = tr.ReadToEnd(); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } - var dt = CreateAndAssociate() as IDateTime; - if (dt == null) + if (!(CreateAndAssociate() is IDateTime dt)) { return null; } @@ -117,7 +121,7 @@ public override object Deserialize(TextReader tr) if (!match.Success) { - return null; + throw new FormatException("Could not be parsed as a Date or DateTime: " + value); } var now = DateTime.Now; diff --git a/src/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs b/src/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs index a1d5d5c5c..1a78b1045 100644 --- a/src/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs @@ -8,7 +8,7 @@ protected EncodableDataTypeSerializer() {} protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) {} - protected string Encode(IEncodableDataType dt, string value) + protected string? Encode(IEncodableDataType? dt, string? value) { if (value == null) { @@ -25,7 +25,7 @@ protected string Encode(IEncodableDataType dt, string value) return Encode(dt, encodingStack.Current.GetBytes(value)); } - protected string Encode(IEncodableDataType dt, byte[] data) + protected string? Encode(IEncodableDataType dt, byte[]? data) { if (data == null) { @@ -43,7 +43,7 @@ protected string Encode(IEncodableDataType dt, byte[] data) return encodingProvider?.Encode(dt.Encoding, data); } - protected string Decode(IEncodableDataType dt, string value) + protected string? Decode(IEncodableDataType? dt, string value) { if (dt?.Encoding == null) { @@ -61,7 +61,7 @@ protected string Decode(IEncodableDataType dt, string value) return encodingStack.Current.GetString(data); } - protected byte[] DecodeData(IEncodableDataType dt, string value) + protected byte[]? DecodeData(IEncodableDataType? dt, string? value) { if (value == null) { diff --git a/src/Ical.Net/Serialization/DataTypes/EnumSerializer.cs b/src/Ical.Net/Serialization/DataTypes/EnumSerializer.cs index c2855ffcb..1256a0350 100644 --- a/src/Ical.Net/Serialization/DataTypes/EnumSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/EnumSerializer.cs @@ -6,7 +6,7 @@ namespace Ical.Net.Serialization.DataTypes { public class EnumSerializer : EncodableDataTypeSerializer { - private readonly Type _mEnumType; + readonly Type _mEnumType; public EnumSerializer(Type enumType) { @@ -20,12 +20,11 @@ public EnumSerializer(Type enumType, SerializationContext ctx) : base(ctx) public override Type TargetType => _mEnumType; - public override string SerializeToString(object enumValue) + public override string? SerializeToString(object? enumValue) { try { - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + if (SerializationContext.Peek() is ICalendarObject obj) { // Encode the value as needed. var dt = new EncodableDataType @@ -48,8 +47,7 @@ public override object Deserialize(TextReader tr) try { - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + if (SerializationContext.Peek() is ICalendarObject obj) { // Decode the value, if necessary! var dt = new EncodableDataType diff --git a/src/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs b/src/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs index da2ac9c65..e8c11378e 100644 --- a/src/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs @@ -12,10 +12,9 @@ public FreeBusyEntrySerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (FreeBusyEntry); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - var entry = obj as FreeBusyEntry; - if (entry == null) + if (!(obj is FreeBusyEntry entry)) { return base.SerializeToString(obj); } @@ -58,21 +57,14 @@ public override object Deserialize(TextReader tr) return entry; } - switch (value.ToUpperInvariant()) + entry.Status = value.ToUpperInvariant() switch { - case "FREE": - entry.Status = FreeBusyStatus.Free; - break; - case "BUSY": - entry.Status = FreeBusyStatus.Busy; - break; - case "BUSY-UNAVAILABLE": - entry.Status = FreeBusyStatus.BusyUnavailable; - break; - case "BUSY-TENTATIVE": - entry.Status = FreeBusyStatus.BusyTentative; - break; - } + "FREE" => FreeBusyStatus.Free, + "BUSY" => FreeBusyStatus.Busy, + "BUSY-UNAVAILABLE" => FreeBusyStatus.BusyUnavailable, + "BUSY-TENTATIVE" => FreeBusyStatus.BusyTentative, + _ => entry.Status + }; return entry; } diff --git a/src/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs b/src/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs index 73d998b13..69ca62f1a 100644 --- a/src/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs @@ -15,12 +15,16 @@ public GeographicLocationSerializer(SerializationContext ctx) : base(ctx) { } public override string SerializeToString(object obj) { - var g = obj as GeographicLocation; - if (g == null) + if (!(obj is GeographicLocation g)) { return null; } + return SerializeToString(g); + } + + public string SerializeToString(GeographicLocation g) + { var value = g.Latitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat) + ";" + g.Longitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat); return Encode(g, value); @@ -33,8 +37,7 @@ public GeographicLocation Deserialize(string value) return null; } - var g = CreateAndAssociate() as GeographicLocation; - if (g == null) + if (!(CreateAndAssociate() is GeographicLocation g)) { return null; } diff --git a/src/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs b/src/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs index 0e5df307b..d4c3fe9bd 100644 --- a/src/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs @@ -12,14 +12,13 @@ public IntegerSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (int); - public override string SerializeToString(object integer) + public override string? SerializeToString(object? integer) { try { var i = Convert.ToInt32(integer); - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + if (SerializationContext.Peek() is ICalendarObject obj) { // Encode the value as needed. var dt = new EncodableDataType @@ -42,8 +41,7 @@ public override object Deserialize(TextReader tr) try { - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + if (SerializationContext.Peek() is ICalendarObject obj) { // Decode the value, if necessary! var dt = new EncodableDataType diff --git a/src/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs b/src/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs index e4da28ab8..067644bf4 100644 --- a/src/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs @@ -12,7 +12,7 @@ public OrganizerSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (Organizer); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { try { diff --git a/src/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs b/src/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs index d4d4fca06..25bdc57ce 100644 --- a/src/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs @@ -22,6 +22,11 @@ public override string SerializeToString(object obj) return null; } + return SerializeToString(factory, periodList); + } + + string? SerializeToString(ISerializerFactory factory, PeriodList periodList) + { var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; var periodSerializer = factory.Build(typeof(Period), SerializationContext) as IStringSerializer; if (dtSerializer == null || periodSerializer == null) @@ -71,15 +76,12 @@ public override object Deserialize(TextReader tr) var values = value.Split(','); foreach (var v in values) { - var dt = dtSerializer.Deserialize(new StringReader(v)) as IDateTime; - var p = periodSerializer.Deserialize(new StringReader(v)) as Period; - - if (dt != null) + if (dtSerializer.Deserialize(new StringReader(v)) is IDateTime dt) { dt.AssociatedObject = rdt.AssociatedObject; rdt.Add(dt); } - else if (p != null) + else if (periodSerializer.Deserialize(new StringReader(v)) is Period p) { p.AssociatedObject = rdt.AssociatedObject; rdt.Add(p); diff --git a/src/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs b/src/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs index 2f5a5bd6b..c89d5e639 100644 --- a/src/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs @@ -13,7 +13,7 @@ public PeriodSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (Period); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var p = obj as Period; var factory = GetService(); @@ -28,33 +28,41 @@ public override string SerializeToString(object obj) try { - var dtSerializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; - var timeSpanSerializer = factory.Build(typeof (TimeSpan), SerializationContext) as IStringSerializer; - if (dtSerializer == null || timeSpanSerializer == null) + return SerializeToString_(factory, p); + } + finally + { + // Pop the period off the serialization context stack + SerializationContext.Pop(); + } + } + + string? SerializeToString_(ISerializerFactory factory, Period p) + { + var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; + var timeSpanSerializer = factory.Build(typeof(TimeSpan), SerializationContext) as IStringSerializer; + if (dtSerializer == null || timeSpanSerializer == null) + { { return null; } - var sb = new StringBuilder(); + } - // Serialize the start time - sb.Append(dtSerializer.SerializeToString(p.StartTime)); + var sb = new StringBuilder(); - // Serialize the duration - if (!p.StartTime.HasTime) - { - // Serialize the duration - sb.Append("/"); - sb.Append(timeSpanSerializer.SerializeToString(p.Duration)); - } + // Serialize the start time + sb.Append(dtSerializer.SerializeToString(p.StartTime)); - // Encode the value as necessary - return Encode(p, sb.ToString()); - } - finally + // Serialize the duration + if (!p.StartTime.HasTime) { - // Pop the period off the serialization context stack - SerializationContext.Pop(); + // Serialize the duration + sb.Append("/"); + sb.Append(timeSpanSerializer.SerializeToString(p.Duration)); } + + // Encode the value as necessary + return Encode(p, sb.ToString()); } public override object Deserialize(TextReader tr) diff --git a/src/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs b/src/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs index 5bb506f38..9be477edb 100644 --- a/src/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Text.RegularExpressions; using Ical.Net.DataTypes; @@ -14,30 +13,24 @@ public RecurrencePatternSerializer() { } public RecurrencePatternSerializer(SerializationContext ctx) : base(ctx) { } - public static DayOfWeek GetDayOfWeek(string value) + public static DayOfWeek GetDayOfWeek(string value) => value.ToUpper() switch { - switch (value.ToUpper()) - { - case "SU": - return DayOfWeek.Sunday; - case "MO": - return DayOfWeek.Monday; - case "TU": - return DayOfWeek.Tuesday; - case "WE": - return DayOfWeek.Wednesday; - case "TH": - return DayOfWeek.Thursday; - case "FR": - return DayOfWeek.Friday; - case "SA": - return DayOfWeek.Saturday; - } - throw new ArgumentException(value + " is not a valid iCal day-of-week indicator."); - } + "SU" => DayOfWeek.Sunday, + "MO" => DayOfWeek.Monday, + "TU" => DayOfWeek.Tuesday, + "WE" => DayOfWeek.Wednesday, + "TH" => DayOfWeek.Thursday, + "FR" => DayOfWeek.Friday, + "SA" => DayOfWeek.Saturday, + _ => throw new ArgumentException(value + " is not a valid iCal day-of-week indicator.") + }; protected static void AddInt32Values(IList list, string value) { + if (string.IsNullOrWhiteSpace(value)) + { + return; + } var values = value.Split(','); foreach (var v in values) { @@ -45,7 +38,7 @@ protected static void AddInt32Values(IList list, string value) } } - public virtual void CheckRange(string name, IList values, int min, int max) + public void CheckRange(string name, IList values, int min, int max) { var allowZero = (min == 0 || max == 0); foreach (var value in values) @@ -54,13 +47,13 @@ public virtual void CheckRange(string name, IList values, int min, int max) } } - public virtual void CheckRange(string name, int value, int min, int max) + public void CheckRange(string name, int value, int min, int max) { var allowZero = min == 0 || max == 0; CheckRange(name, value, min, max, allowZero); } - public virtual void CheckRange(string name, int value, int min, int max, bool allowZero) + public static void CheckRange(string name, int value, int min, int max, bool allowZero) { if (value != int.MinValue && (value < min || value > max || (!allowZero && value == 0))) { @@ -69,7 +62,7 @@ public virtual void CheckRange(string name, int value, int min, int max, bool al } } - public virtual void CheckMutuallyExclusive(string name1, string name2, T obj1, TU obj2) + public static void CheckMutuallyExclusive(string name1, string name2, T obj1, TU obj2) { if (Equals(obj1, default(T)) || Equals(obj2, default(TU))) { @@ -93,7 +86,7 @@ public virtual void CheckMutuallyExclusive(string name1, string name2, T throw new ArgumentException("Both " + name1 + " and " + name2 + " cannot be supplied together; they are mutually exclusive."); } - private void SerializeByValue(List aggregate, IList byValue, string name) + static void SerializeByValue(List aggregate, IList byValue, string name) { if (byValue.Any()) { @@ -114,7 +107,7 @@ public override string SerializeToString(object obj) // Push the recurrence pattern onto the serialization stack SerializationContext.Push(recur); - var values = new List() + var values = new List { "FREQ=" + Enum.GetName(typeof(FrequencyType), recur.Frequency).ToUpper() }; @@ -140,8 +133,7 @@ public override string SerializeToString(object obj) if (recur.Until != DateTime.MinValue) { - var serializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; - if (serializer != null) + if (factory.Build(typeof (IDateTime), SerializationContext) is IStringSerializer serializer) { IDateTime until = new CalDateTime(recur.Until); until.HasTime = true; @@ -159,14 +151,13 @@ public override string SerializeToString(object obj) values.Add("COUNT=" + recur.Count); } - if (recur.ByDay.Count > 0) + if (recur.ByWeekDay.Count > 0) { - var bydayValues = new List(recur.ByDay.Count); + var bydayValues = new List(recur.ByWeekDay.Count); - var serializer = factory.Build(typeof (WeekDay), SerializationContext) as IStringSerializer; - if (serializer != null) + if (factory.Build(typeof (WeekDay), SerializationContext) is IStringSerializer serializer) { - bydayValues.AddRange(recur.ByDay.Select(byday => serializer.SerializeToString(byday))); + bydayValues.AddRange(recur.ByWeekDay.Select(byday => serializer.SerializeToString(byday))); } values.Add("BYDAY=" + string.Join(",", bydayValues)); @@ -188,7 +179,7 @@ public override string SerializeToString(object obj) } //Compiling these is a one-time penalty of about 80ms - private const RegexOptions _ciCompiled = RegexOptions.IgnoreCase | RegexOptions.Compiled; + const RegexOptions _ciCompiled = RegexOptions.IgnoreCase | RegexOptions.Compiled; internal static readonly Regex OtherInterval = new Regex(@"every\s+(?other|\d+)?\w{0,2}\s*(?second|minute|hour|day|week|month|year)s?,?\s*(?.+)", _ciCompiled); @@ -249,8 +240,7 @@ public override object Deserialize(TextReader tr) case "UNTIL": { var serializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; - var dt = serializer?.Deserialize(new StringReader(keyValue)) as IDateTime; - if (dt != null) + if (serializer?.Deserialize(new StringReader(keyValue)) is IDateTime dt) { r.Until = dt.Value; } @@ -276,7 +266,7 @@ public override object Deserialize(TextReader tr) var days = keyValue.Split(','); foreach (var day in days) { - r.ByDay.Add(new WeekDay(day)); + r.ByWeekDay.Add(new WeekDay(day)); } } break; @@ -323,30 +313,17 @@ public override object Deserialize(TextReader tr) r.Interval = 1; } - switch (match.Groups["Freq"].Value.ToLower()) + r.Frequency = match.Groups["Freq"].Value.ToLower() switch { - case "second": - r.Frequency = FrequencyType.Secondly; - break; - case "minute": - r.Frequency = FrequencyType.Minutely; - break; - case "hour": - r.Frequency = FrequencyType.Hourly; - break; - case "day": - r.Frequency = FrequencyType.Daily; - break; - case "week": - r.Frequency = FrequencyType.Weekly; - break; - case "month": - r.Frequency = FrequencyType.Monthly; - break; - case "year": - r.Frequency = FrequencyType.Yearly; - break; - } + "second" => FrequencyType.Secondly, + "minute" => FrequencyType.Minutely, + "hour" => FrequencyType.Hourly, + "day" => FrequencyType.Daily, + "week" => FrequencyType.Weekly, + "month" => FrequencyType.Monthly, + "year" => FrequencyType.Yearly, + _ => r.Frequency + }; var values = match.Groups["More"].Value.Split(','); foreach (var item in values) @@ -416,7 +393,7 @@ public override object Deserialize(TextReader tr) select (DayOfWeek) Enum.Parse(typeof(DayOfWeek), capture.Value, true) into dayOfWeek select new WeekDay(dayOfWeek) {Offset = num}; - r.ByDay.AddRange(dayOfWeekQuery); + r.ByWeekDay.AddRange(dayOfWeekQuery); } else if ((match = Time.Match(item)).Success) { diff --git a/src/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs b/src/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs index 4c491ba83..f44cde4a3 100644 --- a/src/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs @@ -14,48 +14,47 @@ public RequestStatusSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (RequestStatus); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { try { - var rs = obj as RequestStatus; - if (rs == null) + return obj is RequestStatus rs ? SerializeToString(rs) : null; + } + catch + { + return null; + } + } + + public string? SerializeToString(RequestStatus rs) + { + // Push the object onto the serialization stack + SerializationContext.Push(rs); + + try + { + var factory = GetService(); + var serializer = factory?.Build(typeof(StatusCode), SerializationContext) as IStringSerializer; + if (serializer == null) { return null; } - // Push the object onto the serialization stack - SerializationContext.Push(rs); - - try + var builder = new StringBuilder(); + builder.Append(Escape(serializer.SerializeToString(rs.StatusCode))); + builder.Append(";"); + builder.Append(Escape(rs.Description)); + if (!string.IsNullOrWhiteSpace(rs.ExtraData)) { - var factory = GetService(); - var serializer = factory?.Build(typeof (StatusCode), SerializationContext) as IStringSerializer; - if (serializer == null) - { - return null; - } - - var builder = new StringBuilder(); - builder.Append(Escape(serializer.SerializeToString(rs.StatusCode))); builder.Append(";"); - builder.Append(Escape(rs.Description)); - if (!string.IsNullOrWhiteSpace(rs.ExtraData)) - { - builder.Append(";"); - builder.Append(Escape(rs.ExtraData)); - } - return Encode(rs, builder.ToString()); - } - finally - { - // Pop the object off the serialization stack - SerializationContext.Pop(); + builder.Append(Escape(rs.ExtraData)); } + return Encode(rs, builder.ToString()); } - catch + finally { - return null; + // Pop the object off the serialization stack + SerializationContext.Pop(); } } @@ -66,8 +65,7 @@ public override object Deserialize(TextReader tr) { var value = tr.ReadToEnd(); - var rs = CreateAndAssociate() as RequestStatus; - if (rs == null) + if (!(CreateAndAssociate() is RequestStatus rs)) { return null; } diff --git a/src/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs b/src/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs index 0604bea77..e9ab3a00b 100644 --- a/src/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs @@ -13,14 +13,10 @@ public StatusCodeSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (StatusCode); - public override string SerializeToString(object obj) - { - var sc = obj as StatusCode; - if (sc == null) - { - return null; - } + public override string? SerializeToString(object? obj) => obj is StatusCode sc ? SerializeToString(sc) : null; + public string SerializeToString(StatusCode sc) + { var vals = new string[sc.Parts.Length]; for (var i = 0; i < sc.Parts.Length; i++) { @@ -35,8 +31,7 @@ public override object Deserialize(TextReader tr) { var value = tr.ReadToEnd(); - var sc = CreateAndAssociate() as StatusCode; - if (sc == null) + if (!(CreateAndAssociate() is StatusCode sc)) { return null; } diff --git a/src/Ical.Net/Serialization/DataTypes/StringSerializer.cs b/src/Ical.Net/Serialization/DataTypes/StringSerializer.cs index 3e7c1a99e..dd74fc785 100644 --- a/src/Ical.Net/Serialization/DataTypes/StringSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/StringSerializer.cs @@ -58,7 +58,7 @@ protected virtual string Escape(string value) public override Type TargetType => typeof (string); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { if (obj == null) { @@ -66,31 +66,26 @@ public override string SerializeToString(object obj) } var values = new List(); - if (obj is string) + if (obj is string s) { - values.Add((string) obj); + values.Add(s); } - else if (obj is IEnumerable) + else if (obj is IEnumerable enumerable) { - values.AddRange(from object child in (IEnumerable) obj select child.ToString()); + values.AddRange(from object child in enumerable select child.ToString()); } - var co = SerializationContext.Peek() as ICalendarObject; - if (co != null) + if (SerializationContext.Peek() is ICalendarObject co) { // Encode the string as needed. - var dt = new EncodableDataType - { - AssociatedObject = co - }; - for (var i = 0; i < values.Count; i++) - { - values[i] = Encode(dt, Escape(values[i])); - } - - return string.Join(",", values); + return SerializeToString(values, co); } + return SerializeToString(values); + } + + public string SerializeToString(IList values) + { for (var i = 0; i < values.Count; i++) { values[i] = Escape(values[i]); @@ -98,8 +93,22 @@ public override string SerializeToString(object obj) return string.Join(",", values); } + public string? SerializeToString(IList values, ICalendarObject co) + { + var dt = new EncodableDataType + { + AssociatedObject = co + }; + for (var i = 0; i < values.Count; i++) + { + values[i] = Encode(dt, Escape(values[i])); + } + + return string.Join(",", values); + } + internal static readonly Regex UnescapedCommas = new Regex(@"(? typeof (TimeSpan); - public override string SerializeToString(object obj) - { - if (!(obj is TimeSpan)) - { - return null; - } + public override string? SerializeToString(object? obj) => obj is TimeSpan ts ? SerializeToString(ts) : null; - var ts = (TimeSpan) obj; - - if (ts == TimeSpan.Zero) + /// Converts the to a Standard Period "P..." String + public static string SerializeToString(TimeSpan timeSpan) + { + if (timeSpan == TimeSpan.Zero) { return "P0D"; } var sb = new StringBuilder(); - if (ts < TimeSpan.Zero) + if (timeSpan < TimeSpan.Zero) { sb.Append("-"); } sb.Append("P"); - if (ts.Days > 7 && ts.Days % 7 == 0 && ts.Hours == 0 && ts.Minutes == 0 && ts.Seconds == 0) + if (timeSpan.Days > 7 && timeSpan.Days % 7 == 0 && timeSpan.Hours == 0 && timeSpan.Minutes == 0 && timeSpan.Seconds == 0) { - sb.Append(Math.Round(Math.Abs((double) ts.Days) / 7) + "W"); + sb.Append(Math.Round(Math.Abs((double) timeSpan.Days) / 7) + "W"); } else { - if (ts.Days != 0) + if (timeSpan.Days != 0) { - sb.Append(Math.Abs(ts.Days) + "D"); + sb.Append(Math.Abs(timeSpan.Days) + "D"); } - if (ts.Hours != 0 || ts.Minutes != 0 || ts.Seconds != 0) + if (timeSpan.Hours != 0 || timeSpan.Minutes != 0 || timeSpan.Seconds != 0) { sb.Append("T"); - if (ts.Hours != 0) + if (timeSpan.Hours != 0) { - sb.Append(Math.Abs(ts.Hours) + "H"); + sb.Append(Math.Abs(timeSpan.Hours) + "H"); } - if (ts.Minutes != 0) + if (timeSpan.Minutes != 0) { - sb.Append(Math.Abs(ts.Minutes) + "M"); + sb.Append(Math.Abs(timeSpan.Minutes) + "M"); } - if (ts.Seconds != 0) + if (timeSpan.Seconds != 0) { - sb.Append(Math.Abs(ts.Seconds) + "S"); + sb.Append(Math.Abs(timeSpan.Seconds) + "S"); } } } diff --git a/src/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs b/src/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs index 54e8d9136..0063608eb 100644 --- a/src/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs @@ -12,45 +12,45 @@ public TriggerSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (Trigger); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { try { - if (!(obj is Trigger t)) + return obj is Trigger t ? SerializeToString(t) : null; + } + catch + { + return null; + } + } + + public string? SerializeToString(Trigger t) + { + // Push the trigger onto the serialization stack + SerializationContext.Push(t); + try + { + var factory = GetService(); + if (factory == null) { return null; } - // Push the trigger onto the serialization stack - SerializationContext.Push(t); - try - { - var factory = GetService(); - if (factory == null) - { - return null; - } - - var valueType = t.GetValueType() ?? typeof(TimeSpan); - if (!(factory.Build(valueType, SerializationContext) is IStringSerializer serializer)) - { - return null; - } - - var value = valueType == typeof(IDateTime) - ? t.DateTime - : (object) t.Duration; - return serializer.SerializeToString(value); - } - finally + var valueType = t.GetValueType() ?? typeof(TimeSpan); + if (!(factory.Build(valueType, SerializationContext) is IStringSerializer serializer)) { - // Pop the trigger off the serialization stack - SerializationContext.Pop(); + return null; } + + var value = valueType == typeof(IDateTime) + ? t.DateTime + : (object?) t.Duration; + return serializer.SerializeToString(value); } - catch + finally { - return null; + // Pop the trigger off the serialization stack + SerializationContext.Pop(); } } diff --git a/src/Ical.Net/Serialization/DataTypes/UriSerializer.cs b/src/Ical.Net/Serialization/DataTypes/UriSerializer.cs index 9d8a3e7be..c6db93c4b 100644 --- a/src/Ical.Net/Serialization/DataTypes/UriSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/UriSerializer.cs @@ -12,24 +12,19 @@ public UriSerializer(SerializationContext ctx) : base(ctx) {} public override Type TargetType => typeof (string); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) => obj is Uri uri ? SerializeToString(uri) : null; + + public string SerializeToString(Uri uri) { - if (!(obj is Uri)) + if (!(SerializationContext.Peek() is ICalendarObject co)) { - return null; + return uri.OriginalString; } - - var uri = (Uri) obj; - - if (SerializationContext.Peek() is ICalendarObject co) + var dt = new EncodableDataType { - var dt = new EncodableDataType - { - AssociatedObject = co - }; - return Encode(dt, uri.OriginalString); - } - return uri.OriginalString; + AssociatedObject = co + }; + return Encode(dt, uri.OriginalString); } public override object Deserialize(TextReader tr) diff --git a/src/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs b/src/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs index 93ee52621..65a254949 100644 --- a/src/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs @@ -14,18 +14,15 @@ public UtcOffsetSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (UtcOffset); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) => obj is UtcOffset offset ? SerializeToString(offset) : null; + + public string SerializeToString(UtcOffset offset) { - var offset = obj as UtcOffset; - if (offset != null) - { - var value = (offset.Positive ? "+" : "-") + offset.Hours.ToString("00") + offset.Minutes.ToString("00") + - (offset.Seconds != 0 ? offset.Seconds.ToString("00") : string.Empty); + var value = (offset.Positive ? "+" : "-") + offset.Hours.ToString("00") + offset.Minutes.ToString("00") + + (offset.Seconds != 0 ? offset.Seconds.ToString("00") : string.Empty); - // Encode the value as necessary - return Encode(offset, value); - } - return null; + // Encode the value as necessary + return Encode(offset, value); } internal static readonly Regex DecodeOffset = new Regex(@"(\+|-)(\d{2})(\d{2})(\d{2})?", RegexOptions.Compiled | RegexOptions.IgnoreCase); diff --git a/src/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs b/src/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs index 8cd04dff8..938e7915e 100644 --- a/src/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs +++ b/src/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs @@ -13,24 +13,21 @@ public WeekDaySerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof (WeekDay); - public override string SerializeToString(object obj) - { - if (!(obj is WeekDay ds)) - { - return null; - } + public override string? SerializeToString(object? obj) => obj is WeekDay ds ? SerializeToString(ds) : null; + string? SerializeToString(WeekDay ds) + { var value = string.Empty; if (ds.Offset != int.MinValue) { value += ds.Offset; } - value += Enum.GetName(typeof (DayOfWeek), ds.DayOfWeek).ToUpper().Substring(0, 2); + value += Enum.GetName(typeof(DayOfWeek), ds.DayOfWeek).ToUpper().Substring(0, 2); return Encode(ds, value); } - private static readonly Regex _dayOfWeek = new Regex(@"(\+|-)?(\d{1,2})?(\w{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase); + static readonly Regex _dayOfWeek = new Regex(@"(\+|-)?(\d{1,2})?(\w{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase); public override object Deserialize(TextReader tr) { diff --git a/src/Ical.Net/Serialization/EncodingProvider.cs b/src/Ical.Net/Serialization/EncodingProvider.cs index 9c27b7055..21cb4289e 100644 --- a/src/Ical.Net/Serialization/EncodingProvider.cs +++ b/src/Ical.Net/Serialization/EncodingProvider.cs @@ -9,7 +9,7 @@ internal class EncodingProvider : IEncodingProvider public delegate byte[] DecoderDelegate(string value); - private readonly SerializationContext _mSerializationContext; + readonly SerializationContext _mSerializationContext; public EncodingProvider(SerializationContext ctx) { @@ -61,17 +61,13 @@ protected virtual DecoderDelegate GetDecoderFor(string encoding) return null; } - switch (encoding.ToUpper()) + return encoding.ToUpper() switch { - case "7BIT": - return Decode7Bit; - case "8BIT": - return Decode8Bit; - case "BASE64": - return DecodeBase64; - default: - return null; - } + "7BIT" => Decode7Bit, + "8BIT" => Decode8Bit, + "BASE64" => DecodeBase64, + _ => null + }; } protected string Encode7Bit(byte[] data) @@ -119,17 +115,13 @@ protected virtual EncoderDelegate GetEncoderFor(string encoding) return null; } - switch (encoding.ToUpper()) + return encoding.ToUpper() switch { - case "7BIT": - return Encode7Bit; - case "8BIT": - return Encode8Bit; - case "BASE64": - return EncodeBase64; - default: - return null; - } + "7BIT" => Encode7Bit, + "8BIT" => Encode8Bit, + "BASE64" => EncodeBase64, + _ => null + }; } public string Encode(string encoding, byte[] data) diff --git a/src/Ical.Net/Serialization/EncodingStack.cs b/src/Ical.Net/Serialization/EncodingStack.cs index 7cfdec501..c88a4faa6 100644 --- a/src/Ical.Net/Serialization/EncodingStack.cs +++ b/src/Ical.Net/Serialization/EncodingStack.cs @@ -5,7 +5,7 @@ namespace Ical.Net.Serialization { internal class EncodingStack { - private readonly Stack _mStack; + readonly Stack _mStack; public EncodingStack() { diff --git a/src/Ical.Net/Serialization/GenericListSerializer.cs b/src/Ical.Net/Serialization/GenericListSerializer.cs index a417a0d4b..5d4a2b9c4 100644 --- a/src/Ical.Net/Serialization/GenericListSerializer.cs +++ b/src/Ical.Net/Serialization/GenericListSerializer.cs @@ -8,8 +8,8 @@ namespace Ical.Net.Serialization { public class GenericListSerializer : SerializerBase { - private readonly Type _innerType; - private readonly Type _objectType; + readonly Type _innerType; + readonly Type _objectType; public GenericListSerializer(Type objectType) { @@ -23,11 +23,10 @@ public GenericListSerializer(Type objectType) public override string SerializeToString(object obj) => throw new NotImplementedException(); - private MethodInfo _addMethodInfo; + MethodInfo _addMethodInfo; public override object Deserialize(TextReader tr) { - var p = SerializationContext.Peek() as ICalendarProperty; - if (p == null) + if (!(SerializationContext.Peek() is ICalendarProperty p)) { return null; } @@ -41,8 +40,7 @@ public override object Deserialize(TextReader tr) // Get a serializer for the inner type var sf = GetService(); - var stringSerializer = sf.Build(_innerType, SerializationContext) as IStringSerializer; - if (stringSerializer == null) + if (!(sf.Build(_innerType, SerializationContext) is IStringSerializer stringSerializer)) { return null; } @@ -59,8 +57,7 @@ public override object Deserialize(TextReader tr) } // Determine if the returned object is an IList, rather than just an ObjectType. - var add = objToAdd as IList; - if (add != null) + if (objToAdd is IList add) { //Deserialization returned an IList, instead of an ObjectType. So enumerate through the items in the list and add //them individually to our list. diff --git a/src/Ical.Net/Serialization/ISerializer.cs b/src/Ical.Net/Serialization/ISerializer.cs index ef9f0db9a..015dbe13e 100644 --- a/src/Ical.Net/Serialization/ISerializer.cs +++ b/src/Ical.Net/Serialization/ISerializer.cs @@ -10,6 +10,6 @@ public interface ISerializer : IServiceProvider Type TargetType { get; } void Serialize(object obj, Stream stream, Encoding encoding); - object Deserialize(Stream stream, Encoding encoding); + object? Deserialize(Stream stream, Encoding encoding); } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/IStringSerializer.cs b/src/Ical.Net/Serialization/IStringSerializer.cs index 087dfdacb..0ee65a758 100644 --- a/src/Ical.Net/Serialization/IStringSerializer.cs +++ b/src/Ical.Net/Serialization/IStringSerializer.cs @@ -1,10 +1,11 @@ -using System.IO; +#nullable enable +using System.IO; namespace Ical.Net.Serialization { public interface IStringSerializer : ISerializer { string SerializeToString(object obj); - object Deserialize(TextReader tr); + object? Deserialize(TextReader tr); } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/ParameterSerializer.cs b/src/Ical.Net/Serialization/ParameterSerializer.cs index 842da5921..91946a311 100644 --- a/src/Ical.Net/Serialization/ParameterSerializer.cs +++ b/src/Ical.Net/Serialization/ParameterSerializer.cs @@ -12,13 +12,15 @@ public ParameterSerializer(SerializationContext ctx) : base(ctx) {} public override Type TargetType => typeof (CalendarParameter); - public override string SerializeToString(object obj) - { - if (!(obj is CalendarParameter p)) - { - return null; - } + public override object? Deserialize(TextReader tr) => null; + public override string? SerializeToString(object? obj) => obj is CalendarParameter p ? p.SerializeToString() : null; + + } + public static class XCalendarParameter + { + public static string SerializeToString(this CalendarParameter p) + { var builder = new StringBuilder(); builder.Append(p.Name + "="); @@ -36,6 +38,6 @@ public override string SerializeToString(object obj) return builder.ToString(); } - public override object Deserialize(TextReader tr) => null; + } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/PropertySerializer.cs b/src/Ical.Net/Serialization/PropertySerializer.cs index 01b9da777..1c8ef3b3d 100644 --- a/src/Ical.Net/Serialization/PropertySerializer.cs +++ b/src/Ical.Net/Serialization/PropertySerializer.cs @@ -15,7 +15,7 @@ public PropertySerializer(SerializationContext ctx) : base(ctx) {} public override Type TargetType => typeof (CalendarProperty); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var prop = obj as ICalendarProperty; if (prop?.Values == null || !prop.Values.Any()) @@ -31,72 +31,82 @@ public override string SerializeToString(object obj) var sf = GetService(); var result = new StringBuilder(); + SerializeToString_(prop, sf, result); + + // Pop the object off the serialization context. + SerializationContext.Pop(); + return result.ToString(); + } + + void SerializeToString_(ICalendarProperty prop, ISerializerFactory sf, StringBuilder result) + { foreach (var v in prop.Values.Where(value => value != null)) { - // Get a serializer to serialize the property's value. - // If we can't serialize the property's value, the next step is worthless anyway. - var valueSerializer = sf.Build(v.GetType(), SerializationContext) as IStringSerializer; - - // Iterate through each value to be serialized, - // and give it a property (with parameters). - // FIXME: this isn't always the way this is accomplished. - // Multiple values can often be serialized within the - // same property. How should we fix this? - - // NOTE: - // We Serialize the property's value first, as during - // serialization it may modify our parameters. - // FIXME: the "parameter modification" operation should - // be separated from serialization. Perhaps something - // like PreSerialize(), etc. - var value = valueSerializer.SerializeToString(v); - - // Get the list of parameters we'll be serializing - var parameterList = prop.Parameters; - if (v is ICalendarDataType) - { - parameterList = (v as ICalendarDataType).Parameters; - } + SerializeToString_(prop, sf, result, v); + } + } + + void SerializeToString_(ICalendarProperty prop, ISerializerFactory sf, StringBuilder result, object v) + { + // Get a serializer to serialize the property's value. + // If we can't serialize the property's value, the next step is worthless anyway. + var valueSerializer = sf.Build(v.GetType(), SerializationContext) as IStringSerializer; - //This says that the TZID property of an RDATE/EXDATE collection is owned by the PeriodList that contains it. There's nothing in the spec that - //prohibits having multiple EXDATE or RDATE collections, each of which specifies a different TZID. What *should* happen during serialization is - //that we should work with a single collection of zoned datetime objects, and we should create distinct RDATE and EXDATE collections based on - //those values. Right now, if you add CalDateTime objects, each of which specifies a different time zone, the first one "wins". This means - //application developers will need to handle those cases outside the library. - if (v is PeriodList) + // Iterate through each value to be serialized, + // and give it a property (with parameters). + // FIXME: this isn't always the way this is accomplished. + // Multiple values can often be serialized within the + // same property. How should we fix this? + + // NOTE: + // We Serialize the property's value first, as during + // serialization it may modify our parameters. + // FIXME: the "parameter modification" operation should + // be separated from serialization. Perhaps something + // like PreSerialize(), etc. + var value = valueSerializer.SerializeToString(v); + + // Get the list of parameters we'll be serializing + var parameterList = prop.Parameters; + if (v is ICalendarDataType type) + { + parameterList = type.Parameters; + } + + //This says that the TZID property of an RDATE/EXDATE collection is owned by the PeriodList that contains it. There's nothing in the spec that + //prohibits having multiple EXDATE or RDATE collections, each of which specifies a different TZID. What *should* happen during serialization is + //that we should work with a single collection of zoned datetime objects, and we should create distinct RDATE and EXDATE collections based on + //those values. Right now, if you add CalDateTime objects, each of which specifies a different time zone, the first one "wins". This means + //application developers will need to handle those cases outside the library. + if (v is PeriodList typed) + { + if (!string.IsNullOrWhiteSpace(typed.TzId) && parameterList.All(p => + string.Equals("TZID", p.Value, StringComparison.OrdinalIgnoreCase))) { - var typed = (PeriodList)v; - if (!string.IsNullOrWhiteSpace(typed.TzId) && parameterList.All(p => string.Equals("TZID", p.Value, StringComparison.OrdinalIgnoreCase))) - { - parameterList.Set("TZID", typed.TzId); - } + parameterList.Set("TZID", typed.TzId); } + } - var sb = new StringBuilder(); - sb.Append(prop.Name); - if (parameterList.Any()) + var sb = new StringBuilder(); + sb.Append(prop.Name); + if (parameterList.Any()) + { + // Get a serializer for parameters + if (sf.Build(typeof(CalendarParameter), SerializationContext) is IStringSerializer parameterSerializer) { - // Get a serializer for parameters - var parameterSerializer = sf.Build(typeof (CalendarParameter), SerializationContext) as IStringSerializer; - if (parameterSerializer != null) - { - // Serialize each parameter - // Separate parameters with semicolons - sb.Append(";"); - sb.Append(string.Join(";", parameterList.Select(param => parameterSerializer.SerializeToString(param)))); - } + // Serialize each parameter + // Separate parameters with semicolons + sb.Append(";"); + sb.Append(string.Join(";", + parameterList.Select(param => parameterSerializer.SerializeToString(param)))); } - sb.Append(":"); - sb.Append(value); - - result.Append(TextUtil.FoldLines(sb.ToString())); } + sb.Append(":"); + sb.Append(value); - // Pop the object off the serialization context. - SerializationContext.Pop(); - return result.ToString(); + result.Append(TextUtil.FoldLines(sb.ToString())); } - public override object Deserialize(TextReader tr) => null; + public override object? Deserialize(TextReader tr) => null; } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/SerializationContext.cs b/src/Ical.Net/Serialization/SerializationContext.cs index 2553d273d..0e0ea6d1a 100644 --- a/src/Ical.Net/Serialization/SerializationContext.cs +++ b/src/Ical.Net/Serialization/SerializationContext.cs @@ -5,7 +5,7 @@ namespace Ical.Net.Serialization { public class SerializationContext { - private static SerializationContext _default; + static readonly SerializationContext Default_ = new SerializationContext(); /// /// Gets the Singleton instance of the SerializationContext class. @@ -14,11 +14,6 @@ public static SerializationContext Default { get { - if (_default == null) - { - _default = new SerializationContext(); - } - // Create a new serialization context that doesn't contain any objects // (and is non-static). That way, if any objects get pushed onto // the serialization stack when the Default serialization context is used, @@ -27,14 +22,14 @@ public static SerializationContext Default // objects weren't pushed onto a stack referenced by a static variable. var ctx = new SerializationContext { - _mServiceProvider = _default._mServiceProvider + ServiceProvider_ = Default_.ServiceProvider_ }; return ctx; } } - private readonly Stack _mStack = new Stack(); - private ServiceProvider _mServiceProvider = new ServiceProvider(); + readonly Stack Stack_ = new Stack(); + ServiceProvider ServiceProvider_ = new ServiceProvider(); public SerializationContext() { @@ -46,66 +41,56 @@ public SerializationContext() SetService(new EncodingProvider(this)); } - public virtual void Push(object item) + public void Push(object? item) { if (item != null) { - _mStack.Push(new WeakReference(item)); + Stack_.Push(new WeakReference(item)); } } - public virtual object Pop() + public object? Pop() { - if (_mStack.Count > 0) + if (Stack_.Count <= 0) { - var r = _mStack.Pop(); - if (r.IsAlive) - { - return r.Target; - } + return null; + } + var r = Stack_.Pop(); + if (r.IsAlive) + { + return r.Target; } return null; } - public virtual object Peek() + public object? Peek() { - if (_mStack.Count > 0) + if (Stack_.Count <= 0) { - var r = _mStack.Peek(); - if (r.IsAlive) - { - return r.Target; - } + return null; + } + var r = Stack_.Peek(); + if (r.IsAlive) + { + return r.Target; } return null; } - public virtual object GetService(Type serviceType) => _mServiceProvider.GetService(serviceType); + public object? GetService(Type serviceType) => ServiceProvider_.GetService(serviceType); - public virtual object GetService(string name) => _mServiceProvider.GetService(name); + public object? GetService(string name) => ServiceProvider_.GetService(name); - public virtual T GetService() => _mServiceProvider.GetService(); + public T GetService() => ServiceProvider_.GetService(); - public virtual T GetService(string name) => _mServiceProvider.GetService(name); + public T GetService(string name) => ServiceProvider_.GetService(name); - public virtual void SetService(string name, object obj) - { - _mServiceProvider.SetService(name, obj); - } + public void SetService(string name, object obj) => ServiceProvider_.SetService(name, obj); - public virtual void SetService(object obj) - { - _mServiceProvider.SetService(obj); - } + public void SetService(object obj) => ServiceProvider_.SetService(obj); - public virtual void RemoveService(Type type) - { - _mServiceProvider.RemoveService(type); - } + public void RemoveService(Type type) => ServiceProvider_.RemoveService(type); - public virtual void RemoveService(string name) - { - _mServiceProvider.RemoveService(name); - } + public void RemoveService(string name) => ServiceProvider_.RemoveService(name); } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/SerializationUtil.cs b/src/Ical.Net/Serialization/SerializationUtil.cs index ea6326742..4844b76b9 100644 --- a/src/Ical.Net/Serialization/SerializationUtil.cs +++ b/src/Ical.Net/Serialization/SerializationUtil.cs @@ -25,10 +25,11 @@ public static void OnDeserialized(object obj) } } - private const BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + const BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; - private static readonly ConcurrentDictionary> _onDeserializingMethods = new ConcurrentDictionary>(); - private static List GetDeserializingMethods(Type targetType) + static readonly ConcurrentDictionary> _onDeserializingMethods = new ConcurrentDictionary>(); + + static List GetDeserializingMethods(Type targetType) { if (targetType == null) { @@ -47,8 +48,9 @@ private static List GetDeserializingMethods(Type targetType) .ToList()); } - private static ConcurrentDictionary> _onDeserializedMethods = new ConcurrentDictionary>(); - private static List GetDeserializedMethods(Type targetType) + static ConcurrentDictionary> _onDeserializedMethods = new ConcurrentDictionary>(); + + static List GetDeserializedMethods(Type targetType) { if (targetType == null) { diff --git a/src/Ical.Net/Serialization/SerializerBase.cs b/src/Ical.Net/Serialization/SerializerBase.cs index ba065568a..470b3e21f 100644 --- a/src/Ical.Net/Serialization/SerializerBase.cs +++ b/src/Ical.Net/Serialization/SerializerBase.cs @@ -6,38 +6,32 @@ namespace Ical.Net.Serialization { public abstract class SerializerBase : IStringSerializer { - private SerializationContext _mSerializationContext; - protected SerializerBase() { - _mSerializationContext = SerializationContext.Default; + SerializationContext = SerializationContext.Default; } protected SerializerBase(SerializationContext ctx) { - _mSerializationContext = ctx; + SerializationContext = ctx; } - public virtual SerializationContext SerializationContext - { - get => _mSerializationContext; - set => _mSerializationContext = value; - } + public SerializationContext SerializationContext { get; set; } public abstract Type TargetType { get; } + + /// Converts into the vCard Value Format + /// null when is null or not of the proper Type public abstract string SerializeToString(object obj); - public abstract object Deserialize(TextReader tr); + public abstract object? Deserialize(TextReader tr); - public object Deserialize(Stream stream, Encoding encoding) + public object? Deserialize(Stream stream, Encoding encoding) { - object obj; - using (var sr = new StreamReader(stream, encoding)) - { - var encodingStack = GetService(); - encodingStack.Push(encoding); - obj = Deserialize(sr); - encodingStack.Pop(); - } + using var sr = new StreamReader(stream, encoding); + var encodingStack = GetService(); + encodingStack.Push(encoding); + var obj = Deserialize(sr); + encodingStack.Pop(); return obj; } @@ -48,65 +42,37 @@ public void Serialize(object obj, Stream stream, Encoding encoding) // Fixes bug #3177278 - Serialize closes stream const int defaultBuffer = 1024; //This is StreamWriter's built-in default buffer size - using (var sw = new StreamWriter(stream, encoding, defaultBuffer, leaveOpen: true)) - { - // Push the current object onto the serialization stack - SerializationContext.Push(obj); + using var sw = new StreamWriter(stream, encoding, defaultBuffer, leaveOpen: true); + // Push the current object onto the serialization stack + SerializationContext.Push(obj); - // Push the current encoding on the stack - var encodingStack = GetService(); - encodingStack.Push(encoding); + // Push the current encoding on the stack + var encodingStack = GetService(); + encodingStack.Push(encoding); - sw.Write(SerializeToString(obj)); + sw.Write(SerializeToString(obj)); - // Pop the current encoding off the serialization stack - encodingStack.Pop(); + // Pop the current encoding off the serialization stack + encodingStack.Pop(); - // Pop the current object off the serialization stack - SerializationContext.Pop(); - } + // Pop the current object off the serialization stack + SerializationContext.Pop(); } - public virtual object GetService(Type serviceType) => SerializationContext?.GetService(serviceType); + public object GetService(Type serviceType) => SerializationContext.GetService(serviceType); - public virtual object GetService(string name) => SerializationContext?.GetService(name); + public object GetService(string name) => SerializationContext.GetService(name); - public virtual T GetService() - { - if (SerializationContext != null) - { - return SerializationContext.GetService(); - } - return default(T); - } + public T GetService() => SerializationContext.GetService(); - public virtual T GetService(string name) - { - if (SerializationContext != null) - { - return SerializationContext.GetService(name); - } - return default(T); - } + public T GetService(string name) => SerializationContext.GetService(name); - public void SetService(string name, object obj) - { - SerializationContext?.SetService(name, obj); - } + public void SetService(string name, object obj) => SerializationContext.SetService(name, obj); - public void SetService(object obj) - { - SerializationContext?.SetService(obj); - } + public void SetService(object obj) => SerializationContext.SetService(obj); - public void RemoveService(Type type) - { - SerializationContext?.RemoveService(type); - } + public void RemoveService(Type type) => SerializationContext.RemoveService(type); - public void RemoveService(string name) - { - SerializationContext?.RemoveService(name); - } + public void RemoveService(string name) => SerializationContext.RemoveService(name); } } \ No newline at end of file diff --git a/src/Ical.Net/Serialization/SerializerFactory.cs b/src/Ical.Net/Serialization/SerializerFactory.cs index a9df8edcf..0aa78658c 100644 --- a/src/Ical.Net/Serialization/SerializerFactory.cs +++ b/src/Ical.Net/Serialization/SerializerFactory.cs @@ -8,7 +8,7 @@ namespace Ical.Net.Serialization { public class SerializerFactory : ISerializerFactory { - private readonly ISerializerFactory _mDataTypeSerializerFactory; + readonly ISerializerFactory _mDataTypeSerializerFactory; public SerializerFactory() { @@ -24,7 +24,7 @@ public SerializerFactory() /// /// The type of object to be serialized. /// The serialization context. - public virtual ISerializer Build(Type objectType, SerializationContext ctx) + public ISerializer Build(Type objectType, SerializationContext ctx) { if (objectType == null) { diff --git a/src/Ical.Net/Serialization/SimpleDeserializer.cs b/src/Ical.Net/Serialization/SimpleDeserializer.cs index 30ff08bcc..767d9c514 100644 --- a/src/Ical.Net/Serialization/SimpleDeserializer.cs +++ b/src/Ical.Net/Serialization/SimpleDeserializer.cs @@ -25,36 +25,46 @@ internal SimpleDeserializer( new SerializerFactory(), new CalendarComponentFactory()); - private const string _nameGroup = "name"; - private const string _valueGroup = "value"; - private const string _paramNameGroup = "paramName"; - private const string _paramValueGroup = "paramValue"; - - private static readonly Regex _contentLineRegex = new Regex(BuildContentLineRegex(), RegexOptions.Compiled); - - private readonly DataTypeMapper _dataTypeMapper; - private readonly ISerializerFactory _serializerFactory; - private readonly CalendarComponentFactory _componentFactory; - - private static string BuildContentLineRegex() + #region RegEx Group Names + + const string _nameGroup = "name"; + const string _valueGroup = "value"; + const string _paramNameGroup = "paramName"; + const string _paramValueGroup = "paramValue"; + + #endregion RegEx Group Names + + readonly DataTypeMapper _dataTypeMapper; + readonly ISerializerFactory _serializerFactory; + readonly CalendarComponentFactory _componentFactory; + + /// Regular Expression for an Identifier + /// + /// name = iana-token / x-name + /// iana-token = 1*(ALPHA / DIGIT / "-") + /// x-name = "X-" [vendorid "-"] 1*(ALPHA / DIGIT / "-") + /// vendorid = 3*(ALPHA / DIGIT) + /// Added underscore to match behavior of bug 2033495 + /// + static readonly Regex identifier = new Regex("[-A-Za-z0-9_]+"); + + /// Regular Expression for a Parameter Value + /// + /// param-value = paramtext / quoted-string + /// paramtext = *SAFE-CHAR + /// quoted-string = DQUOTE *QSAFE-CHAR DQUOTE + /// QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII + /// ; Any character except CONTROL and DQUOTE + /// SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E + /// / NON-US-ASCII + /// ; Any character except CONTROL, DQUOTE, ";", ":", "," + /// + static readonly string paramValue = $"((?<{_paramValueGroup}>[^\\x00-\\x08\\x0A-\\x1F\\x7F\";:,]*)|\"(?<{_paramValueGroup}>[^\\x00-\\x08\\x0A-\\x1F\\x7F\"]*)\")"; + + static readonly Regex _contentLineRegex = new Regex(BuildContentLineRegex(), RegexOptions.Compiled); + + static string BuildContentLineRegex() { - // name = iana-token / x-name - // iana-token = 1*(ALPHA / DIGIT / "-") - // x-name = "X-" [vendorid "-"] 1*(ALPHA / DIGIT / "-") - // vendorid = 3*(ALPHA / DIGIT) - // Add underscore to match behavior of bug 2033495 - const string identifier = "[-A-Za-z0-9_]+"; - - // param-value = paramtext / quoted-string - // paramtext = *SAFE-CHAR - // quoted-string = DQUOTE *QSAFE-CHAR DQUOTE - // QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII - // ; Any character except CONTROL and DQUOTE - // SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E - // / NON-US-ASCII - // ; Any character except CONTROL, DQUOTE, ";", ":", "," - var paramValue = $"((?<{_paramValueGroup}>[^\\x00-\\x08\\x0A-\\x1F\\x7F\";:,]*)|\"(?<{_paramValueGroup}>[^\\x00-\\x08\\x0A-\\x1F\\x7F\"]*)\")"; - // param = param-name "=" param-value *("," param-value) // param-name = iana-token / x-name var paramName = $"(?<{_paramNameGroup}>{identifier})"; @@ -70,55 +80,58 @@ private static string BuildContentLineRegex() public IEnumerable Deserialize(TextReader reader) { + var lineNo = 0; var context = new SerializationContext(); var stack = new Stack(); var current = default(ICalendarComponent); foreach (var contentLineString in GetContentLines(reader)) { + ++lineNo; var contentLine = ParseContentLine(context, contentLineString); + if (string.Equals(contentLine.Name, "BEGIN", StringComparison.OrdinalIgnoreCase)) - { + { //start/push a new Sub-Component stack.Push(current); - current = _componentFactory.Build((string)contentLine.Value); + current = CalendarComponentFactory.Build((string)contentLine.Value); SerializationUtil.OnDeserializing(current); + continue; } - else + + if (string.Equals(contentLine.Name, "END", StringComparison.OrdinalIgnoreCase)) { - if (current == null) + if (!string.Equals((string)contentLine.Value, current.Name, StringComparison.OrdinalIgnoreCase)) { - throw new SerializationException($"Expected 'BEGIN', found '{contentLine.Name}'"); + throw new SerializationException($"Expected 'END:{current.Name}' in lineNo {lineNo}, found 'END:{contentLine.Value}'"); } - if (string.Equals(contentLine.Name, "END", StringComparison.OrdinalIgnoreCase)) + SerializationUtil.OnDeserialized(current); + var finished = current; + current = stack.Pop(); + if (current == null) { - if (!string.Equals((string)contentLine.Value, current.Name, StringComparison.OrdinalIgnoreCase)) - { - throw new SerializationException($"Expected 'END:{current.Name}', found 'END:{contentLine.Value}'"); - } - SerializationUtil.OnDeserialized(current); - var finished = current; - current = stack.Pop(); - if (current == null) - { - yield return finished; - } - else - { - current.Children.Add(finished); - } + yield return finished; } else { - current.Properties.Add(contentLine); + current.Children.Add(finished); } + continue; } + + if (current == null) + { + throw new SerializationException($"Expected 'BEGIN' in lineNo {lineNo}, found '{contentLine.Name}'"); + } + + current.Properties.Add(contentLine); } + if (current != null) { throw new SerializationException($"Unclosed component {current.Name}"); } } - private CalendarProperty ParseContentLine(SerializationContext context, string input) + CalendarProperty ParseContentLine(SerializationContext context, string input) { var match = _contentLineRegex.Match(input); if (!match.Success) @@ -138,7 +151,7 @@ private CalendarProperty ParseContentLine(SerializationContext context, string i return property; } - private static void SetPropertyParameters(CalendarProperty property, CaptureCollection paramNames, CaptureCollection paramValues) + static void SetPropertyParameters(CalendarProperty property, CaptureCollection paramNames, CaptureCollection paramValues) { var paramValueIndex = 0; for (var paramNameIndex = 0; paramNameIndex < paramNames.Count; paramNameIndex++) @@ -156,29 +169,26 @@ private static void SetPropertyParameters(CalendarProperty property, CaptureColl } } - private void SetPropertyValue(SerializationContext context, CalendarProperty property, string value) + void SetPropertyValue(SerializationContext context, CalendarProperty property, string value) { var type = _dataTypeMapper.GetPropertyMapping(property) ?? typeof(string); var serializer = (SerializerBase)_serializerFactory.Build(type, context); - using (var valueReader = new StringReader(value)) + using var valueReader = new StringReader(value); + var propertyValue = serializer.Deserialize(valueReader); + if (propertyValue is IEnumerable propertyValues) { - var propertyValue = serializer.Deserialize(valueReader); - var propertyValues = propertyValue as IEnumerable; - if (propertyValues != null) - { - foreach (var singlePropertyValue in propertyValues) - { - property.AddValue(singlePropertyValue); - } - } - else + foreach (var singlePropertyValue in propertyValues) { - property.AddValue(propertyValue); + property.AddValue(singlePropertyValue); } } + else + { + property.AddValue(propertyValue); + } } - private static IEnumerable GetContentLines(TextReader reader) + static IEnumerable GetContentLines(TextReader reader) { var currentLine = new StringBuilder(); while (true) diff --git a/src/Ical.Net/ServiceProvider.cs b/src/Ical.Net/ServiceProvider.cs index bd38a291d..86e14f3a1 100644 --- a/src/Ical.Net/ServiceProvider.cs +++ b/src/Ical.Net/ServiceProvider.cs @@ -1,94 +1,67 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; namespace Ical.Net { public class ServiceProvider { - private readonly IDictionary _mTypedServices = new Dictionary(); - private readonly IDictionary _mNamedServices = new Dictionary(); + readonly IDictionary TypedServices_ = new Dictionary(); + readonly IDictionary NamedServices_ = new Dictionary(); - public virtual object GetService(Type serviceType) - { - object service; - _mTypedServices.TryGetValue(serviceType, out service); - return service; - } + public object GetService(Type serviceType) => TypedServices_[serviceType]; + public object GetService(string name) => NamedServices_[name]; - public virtual object GetService(string name) - { - object service; - _mNamedServices.TryGetValue(name, out service); - return service; - } + public T GetService() => (T)GetService(typeof(T)); + public T GetService(string name) => (T)GetService(name); - public virtual T GetService() + public void SetService(string name, object? obj) { - var service = GetService(typeof (T)); - if (service is T) + if (!string.IsNullOrEmpty(name) && obj != null) { - return (T) service; + NamedServices_[name] = obj; } - return default(T); } - public virtual T GetService(string name) + public void SetService(object? obj) { - var service = GetService(name); - if (service is T) + if (obj == null) { - return (T) service; + return; } - return default(T); - } + var type = obj.GetType(); + TypedServices_[type] = obj; - public virtual void SetService(string name, object obj) - { - if (!string.IsNullOrEmpty(name) && obj != null) + // Get interfaces for the given type + foreach (var iFace in type.GetInterfaces()) { - _mNamedServices[name] = obj; + TypedServices_[iFace] = obj; } } - public virtual void SetService(object obj) + public void RemoveService(Type? type) { - if (obj != null) + if (type == null) { - var type = obj.GetType(); - _mTypedServices[type] = obj; - - // Get interfaces for the given type - foreach (var iface in type.GetInterfaces()) - { - _mTypedServices[iface] = obj; - } + return; } - } - - public virtual void RemoveService(Type type) - { - if (type != null) + if (TypedServices_.ContainsKey(type)) { - if (_mTypedServices.ContainsKey(type)) - { - _mTypedServices.Remove(type); - } + TypedServices_.Remove(type); + } - // Get interfaces for the given type - foreach (var iface in type.GetInterfaces().Where(iface => _mTypedServices.ContainsKey(iface))) - { - _mTypedServices.Remove(iface); - } + // Get interfaces for the given type + foreach (var iFace in type.GetInterfaces().Where(iface => TypedServices_.ContainsKey(iface))) + { + TypedServices_.Remove(iFace); } } - public virtual void RemoveService(string name) + public void RemoveService(string name) { - if (_mNamedServices.ContainsKey(name)) + if (NamedServices_.ContainsKey(name)) { - _mNamedServices.Remove(name); + NamedServices_.Remove(name); } } } diff --git a/src/Ical.Net/Utility/DateUtil.cs b/src/Ical.Net/Utility/DateUtil.cs index c85972041..d4d5d9b0d 100644 --- a/src/Ical.Net/Utility/DateUtil.cs +++ b/src/Ical.Net/Utility/DateUtil.cs @@ -5,21 +5,22 @@ using Ical.Net.DataTypes; using NodaTime; using NodaTime.TimeZones; +using NodaTime.Xml; namespace Ical.Net.Utility { internal static class DateUtil { - public static IDateTime StartOfDay(IDateTime dt) + public static IDateTime StartOfDay(this IDateTime dt) => dt.AddHours(-dt.Hour).AddMinutes(-dt.Minute).AddSeconds(-dt.Second); - public static IDateTime EndOfDay(IDateTime dt) + public static IDateTime EndOfDay(this IDateTime dt) => StartOfDay(dt).AddDays(1).AddTicks(-1); - public static DateTime GetSimpleDateTimeData(IDateTime dt) + public static DateTime GetSimpleDateTimeData(this IDateTime dt) => DateTime.SpecifyKind(dt.Value, dt.IsUtc ? DateTimeKind.Utc : DateTimeKind.Local); - public static DateTime SimpleDateTimeToMatch(IDateTime dt, IDateTime toMatch) + public static DateTime SimpleDateTimeToMatch(this IDateTime dt, IDateTime toMatch) { if (toMatch.IsUtc && dt.IsUtc) { @@ -36,7 +37,7 @@ public static DateTime SimpleDateTimeToMatch(IDateTime dt, IDateTime toMatch) return dt.Value; } - public static IDateTime MatchTimeZone(IDateTime dt1, IDateTime dt2) + public static IDateTime MatchTimeZone(this IDateTime dt1, IDateTime dt2) { // Associate the date/time with the first. var copy = dt2; @@ -57,7 +58,7 @@ public static IDateTime MatchTimeZone(IDateTime dt1, IDateTime dt2) : new CalDateTime(copy.AsSystemLocal); } - public static DateTime AddWeeks(DateTime dt, int interval, DayOfWeek firstDayOfWeek) + public static DateTime AddWeeks(this DateTime dt, int interval, DayOfWeek firstDayOfWeek) { // NOTE: fixes WeeklyUntilWkst2() eval. // NOTE: simplified the execution of this - fixes bug #3119920 - missing weekly occurences also @@ -70,7 +71,7 @@ public static DateTime AddWeeks(DateTime dt, int interval, DayOfWeek firstDayOfW return dt; } - public static DateTime FirstDayOfWeek(DateTime dt, DayOfWeek firstDayOfWeek, out int offset) + public static DateTime FirstDayOfWeek(this DateTime dt, DayOfWeek firstDayOfWeek, out int offset) { offset = 0; while (dt.DayOfWeek != firstDayOfWeek) @@ -81,22 +82,26 @@ public static DateTime FirstDayOfWeek(DateTime dt, DayOfWeek firstDayOfWeek, out return dt; } - private static readonly Lazy> _windowsMapping + static readonly Lazy> _windowsMapping = new Lazy>(InitializeWindowsMappings, LazyThreadSafetyMode.PublicationOnly); - private static Dictionary InitializeWindowsMappings() + static Dictionary InitializeWindowsMappings() => TzdbDateTimeZoneSource.Default.WindowsMapping.PrimaryMapping .ToDictionary(k => k.Key, v => v.Value, StringComparer.OrdinalIgnoreCase); public static readonly DateTimeZone LocalDateTimeZone = DateTimeZoneProviders.Tzdb.GetSystemDefault(); - /// - /// Use this method to turn a raw string into a NodaTime DateTimeZone. It searches all time zone providers (IANA, BCL, serialization, etc) to see if - /// the string matches. If it doesn't, it walks each provider, and checks to see if the time zone the provider knows about is contained within the - /// target time zone string. Some older icalendar programs would generate nonstandard time zone strings, and this secondary check works around - /// that. - /// - /// A BCL, IANA, or serialization time zone identifier + /// Use this method to turn a raw string into a NodaTime DateTimeZone. + /// + /// It searches all time zone providers (IANA, BCL, serialization, etc) to see if the string matches. + /// If it doesn't, it walks each provider, + /// and checks to see if the time zone the provider knows about + /// is contained within the target time zone string. + /// + /// Some older icalendar programs would generate nonstandard time zone strings, + /// and this secondary check works around that. + /// + /// A BCL, IANA, or serialization time zone identifier. /// If true, this method will return the system local time zone if tzId doesn't match a known time zone identifier. /// Otherwise, it will throw an exception. public static DateTimeZone GetZone(string tzId, bool useLocalIfNotFound = true) @@ -122,7 +127,7 @@ public static DateTimeZone GetZone(string tzId, bool useLocalIfNotFound = true) return DateTimeZoneProviders.Tzdb.GetZoneOrNull(ianaZone); } - zone = NodaTime.Xml.XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(tzId); + zone = XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(tzId); if (zone != null) { return zone; @@ -130,7 +135,7 @@ public static DateTimeZone GetZone(string tzId, bool useLocalIfNotFound = true) //US/Eastern is commonly represented as US-Eastern var newTzId = tzId.Replace("-", "/"); - zone = NodaTime.Xml.XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(newTzId); + zone = XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(newTzId); if (zone != null) { return zone; @@ -149,9 +154,9 @@ public static DateTimeZone GetZone(string tzId, bool useLocalIfNotFound = true) return DateTimeZoneProviders.Tzdb.GetZoneOrNull(ianaZone); } - foreach (var providerId in NodaTime.Xml.XmlSerializationSettings.DateTimeZoneProvider.Ids.Where(tzId.Contains)) + foreach (var providerId in XmlSerializationSettings.DateTimeZoneProvider.Ids.Where(tzId.Contains)) { - return NodaTime.Xml.XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(providerId); + return XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(providerId); } if (useLocalIfNotFound) @@ -162,7 +167,7 @@ public static DateTimeZone GetZone(string tzId, bool useLocalIfNotFound = true) throw new ArgumentException($"Unrecognized time zone id {tzId}"); } - public static ZonedDateTime AddYears(ZonedDateTime zonedDateTime, int years) + public static ZonedDateTime AddYears(this ZonedDateTime zonedDateTime, int years) { var futureDate = zonedDateTime.Date.PlusYears(years); var futureLocalDateTime = new LocalDateTime(futureDate.Year, futureDate.Month, futureDate.Day, zonedDateTime.Hour, zonedDateTime.Minute, @@ -171,7 +176,7 @@ public static ZonedDateTime AddYears(ZonedDateTime zonedDateTime, int years) return zonedFutureDate; } - public static ZonedDateTime AddMonths(ZonedDateTime zonedDateTime, int months) + public static ZonedDateTime AddMonths(this ZonedDateTime zonedDateTime, int months) { var futureDate = zonedDateTime.Date.PlusMonths(months); var futureLocalDateTime = new LocalDateTime(futureDate.Year, futureDate.Month, futureDate.Day, zonedDateTime.Hour, zonedDateTime.Minute, @@ -180,7 +185,7 @@ public static ZonedDateTime AddMonths(ZonedDateTime zonedDateTime, int months) return zonedFutureDate; } - public static ZonedDateTime ToZonedDateTimeLeniently(DateTime dateTime, string tzId) + public static ZonedDateTime ToZonedDateTimeLeniently(this DateTime dateTime, string tzId) { var zone = GetZone(tzId); var localDt = LocalDateTime.FromDateTime(dateTime); //19:00 UTC @@ -188,30 +193,29 @@ public static ZonedDateTime ToZonedDateTimeLeniently(DateTime dateTime, string t return lenientZonedDateTime; } - public static ZonedDateTime FromTimeZoneToTimeZone(DateTime dateTime, string fromZoneId, string toZoneId) + public static ZonedDateTime FromTimeZoneToTimeZone(this DateTime dateTime, string fromZoneId, string toZoneId) => FromTimeZoneToTimeZone(dateTime, GetZone(fromZoneId), GetZone(toZoneId)); - public static ZonedDateTime FromTimeZoneToTimeZone(DateTime dateTime, DateTimeZone fromZone, DateTimeZone toZone) + public static ZonedDateTime FromTimeZoneToTimeZone(this DateTime dateTime, DateTimeZone fromZone, DateTimeZone toZone) { var oldZone = LocalDateTime.FromDateTime(dateTime).InZoneLeniently(fromZone); var newZone = oldZone.WithZone(toZone); return newZone; } - public static bool IsSerializationTimeZone(DateTimeZone zone) => NodaTime.Xml.XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(zone.Id) != null; + public static bool IsSerializationTimeZone(this DateTimeZone zone) => XmlSerializationSettings.DateTimeZoneProvider.GetZoneOrNull(zone.Id) != null; - /// - /// Truncate to the specified TimeSpan's magnitude. For example, to truncate to the nearest second, use TimeSpan.FromSeconds(1) - /// - /// - /// - /// + /// Truncate the accuracy to the specified TimeSpan's magnitude/Period. + /// + /// For example, to truncate to the nearest second, use TimeSpan.FromSeconds(1) + /// + /// public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan) => timeSpan == TimeSpan.Zero ? dateTime : dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks)); - public static int WeekOfMonth(DateTime d) + public static int WeekOfMonth(this DateTime d) { var isExact = d.Day % 7 == 0; var offset = isExact diff --git a/src/Ical.Net/VTimeZoneInfo.cs b/src/Ical.Net/VTimeZoneInfo.cs index 2c3cd34b2..66ea30088 100644 --- a/src/Ical.Net/VTimeZoneInfo.cs +++ b/src/Ical.Net/VTimeZoneInfo.cs @@ -40,8 +40,7 @@ protected override void OnDeserializing(StreamingContext context) public override bool Equals(object obj) { - var tzi = obj as VTimeZoneInfo; - if (tzi != null) + if (obj is VTimeZoneInfo tzi) { return Equals(TimeZoneName, tzi.TimeZoneName) && Equals(OffsetFrom, tzi.OffsetFrom) && @@ -50,7 +49,7 @@ public override bool Equals(object obj) return base.Equals(obj); } - public virtual string TzId + public string TzId { get => !(Parent is VTimeZone tz) @@ -70,7 +69,7 @@ public virtual string TzId /// /// /// - public virtual string TimeZoneName + public string TimeZoneName { get => TimeZoneNames.Count > 0 ? TimeZoneNames[0] @@ -82,93 +81,90 @@ public virtual string TimeZoneName } } - public virtual UtcOffset TZOffsetFrom + public UtcOffset TZOffsetFrom { get => OffsetFrom; set => OffsetFrom = value; } - public virtual UtcOffset OffsetFrom + public UtcOffset OffsetFrom { get => Properties.Get("TZOFFSETFROM"); set => Properties.Set("TZOFFSETFROM", value); } - public virtual UtcOffset OffsetTo + public UtcOffset OffsetTo { get => Properties.Get("TZOFFSETTO"); set => Properties.Set("TZOFFSETTO", value); } - public virtual UtcOffset TZOffsetTo + public UtcOffset TZOffsetTo { get => OffsetTo; set => OffsetTo = value; } - public virtual IList TimeZoneNames + public IList TimeZoneNames { get => Properties.GetMany("TZNAME"); set => Properties.Set("TZNAME", value); } - public virtual IDateTime DtStart + public IDateTime DtStart { get => Start; set => Start = value; } - public virtual IDateTime Start + public IDateTime Start { get => Properties.Get("DTSTART"); set => Properties.Set("DTSTART", value); } - public virtual IList ExceptionDates + public IList ExceptionDates { get => Properties.GetMany("EXDATE"); set => Properties.Set("EXDATE", value); } - public virtual IList ExceptionRules + public IList ExceptionRules { get => Properties.GetMany("EXRULE"); set => Properties.Set("EXRULE", value); } - public virtual IList RecurrenceDates + public IList RecurrenceDates { get => Properties.GetMany("RDATE"); set => Properties.Set("RDATE", value); } - public virtual IList RecurrenceRules + public IList RecurrenceRules { get => Properties.GetMany("RRULE"); set => Properties.Set("RRULE", value); } - public virtual IDateTime RecurrenceId + public IDateTime RecurrenceId { get => Properties.Get("RECURRENCE-ID"); set => Properties.Set("RECURRENCE-ID", value); } - public virtual void ClearEvaluation() - { - RecurrenceUtil.ClearEvaluation(this); - } + public void ClearEvaluation() => RecurrenceUtil.ClearEvaluation(this); - public virtual HashSet GetOccurrences(IDateTime dt) - => RecurrenceUtil.GetOccurrences(this, dt, true); + public HashSet GetOccurrences(IDateTime dt) + => this.GetOccurrences(dt, true); - public virtual HashSet GetOccurrences(DateTime dt) - => RecurrenceUtil.GetOccurrences(this, new CalDateTime(dt), true); + public HashSet GetOccurrences(DateTime dt) + => this.GetOccurrences(new CalDateTime(dt), true); - public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) - => RecurrenceUtil.GetOccurrences(this, startTime, endTime, true); + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + => this.GetOccurrences(startTime, endTime, true); - public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) - => RecurrenceUtil.GetOccurrences(this, new CalDateTime(startTime), new CalDateTime(endTime), true); + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) + => this.GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime), true); } } \ No newline at end of file diff --git a/src/PerfTests/ApplicationWorkflows.cs b/src/PerfTests/ApplicationWorkflows.cs index abb082a79..ba4ea67d7 100644 --- a/src/PerfTests/ApplicationWorkflows.cs +++ b/src/PerfTests/ApplicationWorkflows.cs @@ -6,20 +6,22 @@ using BenchmarkDotNet.Attributes; using Ical.Net; using Ical.Net.DataTypes; +using NUnit.Framework; namespace PerfTests { public class ApplicationWorkflows { - private static readonly TimeSpan _oneYear = TimeSpan.FromDays(365); - private static readonly DateTime _searchStart = DateTime.Now.Subtract(_oneYear); - private static readonly DateTime _searchEnd = DateTime.Now.Add(_oneYear); - private static readonly List _manyCalendars = GetIcalStrings(); + const int DaysPerYear = 365; + static readonly TimeSpan _oneYear = TimeSpan.FromDays(DaysPerYear); + static readonly DateTime _searchStart = DateTime.Now.Subtract(_oneYear); + static readonly DateTime _searchEnd = DateTime.Now.Add(_oneYear); + static readonly List _manyCalendars = GetIcalStrings(); - private static List GetIcalStrings() + static List GetIcalStrings() { var currentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var topLevelIcsPath = Path.GetFullPath(Path.Combine(currentDirectory, @"..\..\..\..\", @"Ical.Net.CoreUnitTests\Calendars")); + var topLevelIcsPath = Path.GetFullPath(Path.Combine(currentDirectory!, @".\Calendars")); return Directory.EnumerateFiles(topLevelIcsPath, "*.ics", SearchOption.AllDirectories) .Select(File.ReadAllText) .Distinct(StringComparer.OrdinalIgnoreCase) @@ -30,47 +32,46 @@ private static List GetIcalStrings() } [Benchmark] - public List SingleThreaded() + public void SingleThreaded() => SingleThreaded_(); + [Test(ExpectedResult = 364)] + public static int SingleThreaded_() { - return _manyCalendars + var list = _manyCalendars .SelectMany(Calendar.Load) .SelectMany(c => c.Events) - .SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd)) - .ToList(); + .SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd)).ToList(); + return list.Count; } [Benchmark] - public List ParallelUponDeserialize() - { - return _manyCalendars + public int ParallelUponDeserialize() => ParallelUponDeserialize_(); + [Test(ExpectedResult = 364)] + public static int ParallelUponDeserialize_() => _manyCalendars .AsParallel() .SelectMany(Calendar.Load) .SelectMany(c => c.Events) .SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd)) - .ToList(); - } + .Count(); [Benchmark] - public List ParallelUponGetOccurrences() - { - return _manyCalendars + public List ParallelUponGetOccurrences() => ParallelUponGetOccurrences_(); + public static List ParallelUponGetOccurrences_() => _manyCalendars .SelectMany(Calendar.Load) .SelectMany(c => c.Events) .AsParallel() .SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd)) .ToList(); - } [Benchmark] - public List ParallelDeserializeSequentialGatherEventsParallelGetOccurrences() - { - return _manyCalendars + public int ParallelDeserializeSequentialGatherEventsParallelGetOccurrences() + => ParallelDeserializeSequentialGatherEventsParallelGetOccurrences_(); + [Test(ExpectedResult = 364)] + public static int ParallelDeserializeSequentialGatherEventsParallelGetOccurrences_() => _manyCalendars .AsParallel() .SelectMany(Calendar.Load) .AsSequential() .SelectMany(c => c.Events) .SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd)) - .ToList(); - } + .Count(); } } \ No newline at end of file diff --git a/src/PerfTests/CalDateTimePerfTests.cs b/src/PerfTests/CalDateTimePerfTests.cs index 842ae8af9..e22c95191 100644 --- a/src/PerfTests/CalDateTimePerfTests.cs +++ b/src/PerfTests/CalDateTimePerfTests.cs @@ -6,8 +6,8 @@ namespace PerfTests { public class CalDateTimePerfTests { - private const string _aTzid = "Australia/Sydney"; - private const string _bTzid = "America/New_York"; + const string _aTzid = "Australia/Sydney"; + const string _bTzid = "America/New_York"; [Benchmark] public IDateTime EmptyTzid() => new CalDateTime(DateTime.Now); diff --git a/src/PerfTests/OccurencePerfTests.cs b/src/PerfTests/OccurencePerfTests.cs index 1fb1f5bfe..90b57f286 100644 --- a/src/PerfTests/OccurencePerfTests.cs +++ b/src/PerfTests/OccurencePerfTests.cs @@ -5,44 +5,52 @@ using Ical.Net; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; +using NUnit.Framework; namespace PerfTests { - public class OccurencePerfTests + public class OccurrencePerfTests { [Benchmark] public void MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar() + => MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar_(); + [Test(ExpectedResult = 40)] + public static int MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar_() { var calendar = GetFourCalendarEventsWithUntilRule(); var searchStart = calendar.Events.First().DtStart.AddYears(-1); var searchEnd = calendar.Events.Last().DtStart.AddYears(1); - var occurences = calendar.GetOccurrences(searchStart, searchEnd); + return calendar.GetOccurrences(searchStart, searchEnd).Count; } [Benchmark] - public void MultipleEventsWithUntilOccurrences() + public void MultipleEventsWithUntilOccurrences() => MultipleEventsWithUntilOccurrences_(); + [Test(ExpectedResult = 40)] + public static int MultipleEventsWithUntilOccurrences_() { var calendar = GetFourCalendarEventsWithUntilRule(); var searchStart = calendar.Events.First().DtStart.AddYears(-1); var searchEnd = calendar.Events.Last().DtStart.AddYears(1); - var eventOccurrences = calendar.Events + return calendar.Events .SelectMany(e => e.GetOccurrences(searchStart, searchEnd)) - .ToList(); + .Count(); } [Benchmark] - public void MultipleEventsWithUntilOccurrencesEventsAsParallel() + public void MultipleEventsWithUntilOccurrencesEventsAsParallel() => MultipleEventsWithUntilOccurrencesEventsAsParallel_(); + [Test(ExpectedResult = 40)] + public static int MultipleEventsWithUntilOccurrencesEventsAsParallel_() { var calendar = GetFourCalendarEventsWithUntilRule(); var searchStart = calendar.Events.First().DtStart.AddYears(-1); var searchEnd = calendar.Events.Last().DtStart.AddYears(1).AddDays(10); - var eventOccurrences = calendar.Events + return calendar.Events .AsParallel() .SelectMany(e => e.GetOccurrences(searchStart, searchEnd)) - .ToList(); + .Count(); } - private Calendar GetFourCalendarEventsWithUntilRule() + static Calendar GetFourCalendarEventsWithUntilRule() { const string tzid = "America/New_York"; const int limit = 4; @@ -76,37 +84,45 @@ private Calendar GetFourCalendarEventsWithUntilRule() [Benchmark] public void MultipleEventsWithCountOccurrencesSearchingByWholeCalendar() + => MultipleEventsWithCountOccurrencesSearchingByWholeCalendar_(); + [Test(ExpectedResult = 400)] + public static int MultipleEventsWithCountOccurrencesSearchingByWholeCalendar_() { var calendar = GetFourCalendarEventsWithCountRule(); var searchStart = calendar.Events.First().DtStart.AddYears(-1); var searchEnd = calendar.Events.Last().DtStart.AddYears(1); - var occurences = calendar.GetOccurrences(searchStart, searchEnd); + return calendar.GetOccurrences(searchStart, searchEnd).Count; } [Benchmark] - public void MultipleEventsWithCountOccurrences() + public static int MultipleEventsWithCountOccurrences() => MultipleEventsWithCountOccurrences_(); + [Test(ExpectedResult = 400)] + public static int MultipleEventsWithCountOccurrences_() { var calendar = GetFourCalendarEventsWithCountRule(); var searchStart = calendar.Events.First().DtStart.AddYears(-1); var searchEnd = calendar.Events.Last().DtStart.AddYears(1); - var eventOccurrences = calendar.Events + return calendar.Events .SelectMany(e => e.GetOccurrences(searchStart, searchEnd)) - .ToList(); + .Count(); } [Benchmark] public void MultipleEventsWithCountOccurrencesEventsAsParallel() + => MultipleEventsWithCountOccurrencesEventsAsParallel_(); + [Test(ExpectedResult = 400)] + public static int MultipleEventsWithCountOccurrencesEventsAsParallel_() { var calendar = GetFourCalendarEventsWithCountRule(); var searchStart = calendar.Events.First().DtStart.AddYears(-1); var searchEnd = calendar.Events.Last().DtStart.AddYears(1).AddDays(10); - var eventOccurrences = calendar.Events + return calendar.Events .AsParallel() .SelectMany(e => e.GetOccurrences(searchStart, searchEnd)) - .ToList(); + .Count(); } - private Calendar GetFourCalendarEventsWithCountRule() + static Calendar GetFourCalendarEventsWithCountRule() { const string tzid = "America/New_York"; const int limit = 4; diff --git a/src/PerfTests/PerfTests.csproj b/src/PerfTests/PerfTests.csproj index 0f31f9dc1..621f7658f 100644 --- a/src/PerfTests/PerfTests.csproj +++ b/src/PerfTests/PerfTests.csproj @@ -3,6 +3,7 @@ Exe netcoreapp3.1;net50 + enable @@ -10,6 +11,7 @@ + diff --git a/src/PerfTests/ReadMe.md b/src/PerfTests/ReadMe.md new file mode 100644 index 000000000..d1252f224 --- /dev/null +++ b/src/PerfTests/ReadMe.md @@ -0,0 +1,44 @@ +# Expected Test Results + +BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19044 +Intel Core i7-10610U CPU 1.80GHz, 1 CPU, 8 logical and 4 physical cores +.NET Core SDK=7.0.100 + [Host] : .NET Core 3.1.31 (CoreCLR 4.700.22.51102, CoreFX 4.700.22.51303), X64 RyuJIT [AttachedDebugger] + DefaultJob : .NET Core 3.1.31 (CoreCLR 4.700.22.51102, CoreFX 4.700.22.51303), X64 RyuJIT + + +| Method | Mean | Error | StdDev | Median | +|---------------------------------------------------------------- |---------:|---------:|---------:|---------:| +| SingleThreaded | 70.48 ms | 1.472 ms | 3.800 ms | 69.72 ms | +| ParallelUponDeserialize | 67.22 ms | 1.543 ms | 2.310 ms | 66.75 ms | +| ParallelUponGetOccurrences | 68.30 ms | 2.128 ms | 6.003 ms | 66.59 ms | +| ParallelDeserializeSequentialGatherEventsParallelGetOccurrences | 68.87 ms | 1.511 ms | 4.186 ms | 68.19 ms | + +| Method | Mean | Error | StdDev | Median | +|----------------------------- |-----------:|----------:|----------:|-----------:| +| EmptyTzid | 599.1 ns | 10.11 ns | 8.44 ns | 597.1 ns | +| SpecifiedTzid | 1,235.1 ns | 24.34 ns | 29.89 ns | 1,228.5 ns | +| UtcDateTime | 958.9 ns | 18.83 ns | 25.13 ns | 963.7 ns | +| EmptyTzidToTzid | 2,479.6 ns | 43.99 ns | 39.00 ns | 2,489.7 ns | +| SpecifiedTzidToDifferentTzid | 2,822.4 ns | 89.15 ns | 249.98 ns | 2,749.9 ns | +| UtcToDifferentTzid | 2,726.5 ns | 110.13 ns | 312.42 ns | 2,678.9 ns | + +| Method | Mean | Error | StdDev | Median | +|----------------------------------------------------------- |-----------:|----------:|-----------:|-----------:| +| MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar | 1,006.8 us | 60.48 us | 174.5 us | 956.8 us | +| MultipleEventsWithUntilOccurrences | 861.4 us | 59.69 us | 174.1 us | 809.8 us | +| MultipleEventsWithUntilOccurrencesEventsAsParallel | NA | NA | NA | NA | +| MultipleEventsWithCountOccurrencesSearchingByWholeCalendar | 9,631.7 us | 626.00 us | 1,806.2 us | 9,082.5 us | +| MultipleEventsWithCountOccurrences | 5,736.6 us | 290.15 us | 837.1 us | 5,513.1 us | +| MultipleEventsWithCountOccurrencesEventsAsParallel | NA | NA | NA | NA | + + +| Method | Mean | Error | StdDev | Median | +|------------------ |----------:|----------:|----------:|-----------:| +| Deserialize | 299.60 us | 13.559 us | 38.245 us | 290.744 us | +| SerializeCalendar | 10.41 us | 0.604 us | 1.770 us | 9.623 us | + +| Method | Mean | Error | StdDev | +|-------------------------------------- |---------:|---------:|---------:| +| DeserializeAndComputeUntilOccurrences | 12.30 ms | 0.421 ms | 1.193 ms | +| DeserializeAndComputeCountOccurrences | 11.92 ms | 0.471 ms | 1.353 ms | diff --git a/src/PerfTests/Runner.cs b/src/PerfTests/Runner.cs index 358fe9bfd..e7b73c31f 100644 --- a/src/PerfTests/Runner.cs +++ b/src/PerfTests/Runner.cs @@ -1,23 +1,10 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; +using System.Reflection; using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains.InProcess; namespace PerfTests { public class Runner { - static void Main(string[] args) - { - BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(ApplicationWorkflows))); - - - //BenchmarkRunnerCore.Run(BenchmarkConverter.TypeToBenchmarks(typeof(OccurencePerfTests)), t => InProcessToolchain.Instance); - //BenchmarkRunnerCore.Run(BenchmarkConverter.TypeToBenchmarks(typeof(CalDateTimePerfTests)), t => InProcessToolchain.Instance); - //BenchmarkRunnerCore.Run(BenchmarkConverter.TypeToBenchmarks(typeof(SerializationPerfTests)), t => InProcessToolchain.Instance); - //BenchmarkRunnerCore.Run(BenchmarkConverter.TypeToBenchmarks(typeof(ThroughputTests)), t => InProcessToolchain.Instance); - } + static void Main(string[] args) => BenchmarkRunner.Run(Assembly.GetExecutingAssembly());//BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(ApplicationWorkflows)));//BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(OccurrencePerfTests)));//, t => InProcessToolchain.Instance);//BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(CalDateTimePerfTests)));//, t => InProcessToolchain.Instance);//BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(SerializationPerfTests)));//, t => InProcessToolchain.Instance);//BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(ThroughputTests)));//, t => InProcessToolchain.Instance); } } diff --git a/src/PerfTests/SerializationPerfTests.cs b/src/PerfTests/SerializationPerfTests.cs index 5b65ec176..1a0eb2baf 100644 --- a/src/PerfTests/SerializationPerfTests.cs +++ b/src/PerfTests/SerializationPerfTests.cs @@ -11,7 +11,7 @@ namespace PerfTests { public class SerializationPerfTests { - private const string _sampleEvent = @"BEGIN:VCALENDAR + const string _sampleEvent = @"BEGIN:VCALENDAR PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN VERSION:2.0 METHOD:PUBLISH @@ -69,13 +69,14 @@ rsion 08.00.0681.000"">\n\n\n\n