From 1feaa17ad5340ac13f2dba873276465bbe097525 Mon Sep 17 00:00:00 2001 From: axunonb Date: Tue, 15 Apr 2025 00:30:18 +0200 Subject: [PATCH 1/2] Enable NRT for `Serialization` namespace --- Ical.Net/DataTypes/RequestStatus.cs | 2 +- Ical.Net/DataTypes/StatusCode.cs | 2 +- .../Serialization/CalendarComponentFactory.cs | 3 +- Ical.Net/Serialization/CalendarSerializer.cs | 9 +- Ical.Net/Serialization/ComponentSerializer.cs | 19 ++-- Ical.Net/Serialization/DataMapSerializer.cs | 17 +-- Ical.Net/Serialization/DataTypeMapper.cs | 33 +++--- .../DataTypeSerializerFactory.cs | 105 ++++++------------ .../DataTypes/AttachmentSerializer.cs | 24 ++-- .../DataTypes/AttendeeSerializer.cs | 17 ++- .../DataTypes/DataTypeSerializer.cs | 5 +- .../DataTypes/DateTimeSerializer.cs | 4 +- .../DataTypes/DurationSerializer.cs | 2 +- .../DataTypes/EncodableDataTypeSerializer.cs | 25 +++-- .../Serialization/DataTypes/EnumSerializer.cs | 27 +++-- .../DataTypes/FreeBusyEntrySerializer.cs | 15 ++- .../DataTypes/GeographicLocationSerializer.cs | 38 +++---- .../DataTypes/IntegerSerializer.cs | 27 ++--- .../DataTypes/OrganizerSerializer.cs | 30 +++-- .../DataTypes/PeriodListSerializer.cs | 10 +- .../DataTypes/PeriodSerializer.cs | 9 +- .../DataTypes/RecurrencePatternSerializer.cs | 16 ++- .../DataTypes/RequestStatusSerializer.cs | 30 +++-- .../DataTypes/StatusCodeSerializer.cs | 25 +++-- .../DataTypes/StringSerializer.cs | 61 +++++----- .../DataTypes/TriggerSerializer.cs | 26 +++-- .../Serialization/DataTypes/UriSerializer.cs | 21 ++-- .../DataTypes/UtcOffsetSerializer.cs | 47 ++++---- .../DataTypes/WeekDaySerializer.cs | 22 +++- Ical.Net/Serialization/EncodingStack.cs | 7 +- Ical.Net/Serialization/EventSerializer.cs | 12 +- .../Serialization/GenericListSerializer.cs | 6 +- Ical.Net/Serialization/IEncodingProvider.cs | 3 +- Ical.Net/Serialization/IParameterProvider.cs | 1 + Ical.Net/Serialization/ISerializer.cs | 7 +- Ical.Net/Serialization/ISerializerFactory.cs | 5 +- Ical.Net/Serialization/IStringSerializer.cs | 7 +- Ical.Net/Serialization/ParameterSerializer.cs | 9 +- Ical.Net/Serialization/PropertySerializer.cs | 7 +- .../Serialization/SerializationContext.cs | 36 ++---- Ical.Net/Serialization/SerializationUtil.cs | 9 +- Ical.Net/Serialization/SerializerBase.cs | 91 ++++++--------- Ical.Net/Serialization/SerializerFactory.cs | 5 +- Ical.Net/Serialization/SimpleDeserializer.cs | 7 +- 44 files changed, 442 insertions(+), 441 deletions(-) diff --git a/Ical.Net/DataTypes/RequestStatus.cs b/Ical.Net/DataTypes/RequestStatus.cs index 30b1e200..c5996940 100644 --- a/Ical.Net/DataTypes/RequestStatus.cs +++ b/Ical.Net/DataTypes/RequestStatus.cs @@ -61,7 +61,7 @@ public override void CopyFrom(ICopyable obj) ExtraData = rs.ExtraData; } - public override string ToString() + public override string? ToString() { var serializer = new RequestStatusSerializer(); return serializer.SerializeToString(this); diff --git a/Ical.Net/DataTypes/StatusCode.cs b/Ical.Net/DataTypes/StatusCode.cs index 8ec36117..b8b0ca28 100644 --- a/Ical.Net/DataTypes/StatusCode.cs +++ b/Ical.Net/DataTypes/StatusCode.cs @@ -68,7 +68,7 @@ public override void CopyFrom(ICopyable obj) statusCode.Parts.CopyTo(Parts, 0); } - public override string ToString() => new StatusCodeSerializer().SerializeToString(this); + public override string? ToString() => new StatusCodeSerializer().SerializeToString(this); protected bool Equals(StatusCode other) => Parts.SequenceEqual(other.Parts); diff --git a/Ical.Net/Serialization/CalendarComponentFactory.cs b/Ical.Net/Serialization/CalendarComponentFactory.cs index a399a90f..0be82510 100644 --- a/Ical.Net/Serialization/CalendarComponentFactory.cs +++ b/Ical.Net/Serialization/CalendarComponentFactory.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using Ical.Net.CalendarComponents; namespace Ical.Net.Serialization; @@ -48,4 +49,4 @@ public virtual ICalendarComponent Build(string objectName) c.Name = name; return c; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/CalendarSerializer.cs b/Ical.Net/Serialization/CalendarSerializer.cs index ff3194d3..192eb96a 100644 --- a/Ical.Net/Serialization/CalendarSerializer.cs +++ b/Ical.Net/Serialization/CalendarSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using System.IO; @@ -11,7 +12,7 @@ namespace Ical.Net.Serialization; public class CalendarSerializer : ComponentSerializer { - private readonly Calendar _calendar; + private readonly Calendar? _calendar; public CalendarSerializer() : this(new SerializationContext()) { } @@ -23,16 +24,16 @@ public CalendarSerializer(Calendar cal) public CalendarSerializer(SerializationContext ctx) : base(ctx) { } - public virtual string SerializeToString() => SerializeToString(_calendar); + public virtual string? SerializeToString() => SerializeToString(_calendar); protected override IComparer PropertySorter => new CalendarPropertySorter(); - public override object Deserialize(TextReader tr) => null; + public override object? Deserialize(TextReader tr) => null; private class CalendarPropertySorter : IComparer { - public int Compare(ICalendarProperty x, ICalendarProperty y) + public int Compare(ICalendarProperty? x, ICalendarProperty? y) { if (x == y) { diff --git a/Ical.Net/Serialization/ComponentSerializer.cs b/Ical.Net/Serialization/ComponentSerializer.cs index 90f9b4c7..d101c70e 100644 --- a/Ical.Net/Serialization/ComponentSerializer.cs +++ b/Ical.Net/Serialization/ComponentSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using System.IO; @@ -23,9 +24,9 @@ public ComponentSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(CalendarComponent); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - if (!(obj is ICalendarComponent c)) + if (obj is not ICalendarComponent c || SerializationContext == null) { return null; } @@ -44,27 +45,29 @@ public override string SerializeToString(object obj) foreach (var p in properties) { // Get a serializer for each property. - var serializer = sf.Build(p.GetType(), SerializationContext) as IStringSerializer; - sb.Append(serializer.SerializeToString(p)); + var serializer = sf?.Build(p.GetType(), SerializationContext) as IStringSerializer; + var val = serializer?.SerializeToString(p); + if (val != null) sb.Append(val); } // Serialize child objects foreach (var child in c.Children) { // Get a serializer for each child object. - var serializer = sf.Build(child.GetType(), SerializationContext) as IStringSerializer; - sb.Append(serializer.SerializeToString(child)); + var serializer = sf?.Build(child.GetType(), SerializationContext) as IStringSerializer; + var val = serializer?.SerializeToString(child); + if (val != null) sb.Append(val); } sb.FoldLines($"END:{upperName}"); 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/Ical.Net/Serialization/DataMapSerializer.cs b/Ical.Net/Serialization/DataMapSerializer.cs index 05824ea8..c8a3ac4f 100644 --- a/Ical.Net/Serialization/DataMapSerializer.cs +++ b/Ical.Net/Serialization/DataMapSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.Serialization.DataTypes; @@ -15,11 +16,11 @@ public DataMapSerializer() { } public DataMapSerializer(SerializationContext ctx) : base(ctx) { } - protected IStringSerializer GetMappedSerializer() + protected IStringSerializer? GetMappedSerializer() { var sf = GetService(); var mapper = GetService(); - if (sf == null || mapper == null) + if (sf == null || mapper == null || SerializationContext == null) { return null; } @@ -27,7 +28,7 @@ protected IStringSerializer GetMappedSerializer() var obj = SerializationContext.Peek(); // Get the data type for this object - var type = mapper.GetPropertyMapping(obj); + var type = obj != null ? mapper.GetPropertyMapping(obj) : null; return type == null ? new StringSerializer(SerializationContext) @@ -38,18 +39,18 @@ public override Type TargetType { get { - ISerializer serializer = GetMappedSerializer(); - return serializer?.TargetType; + ISerializer serializer = GetMappedSerializer() ?? throw new InvalidOperationException("Cannot detect the mapped serializer"); + return serializer.TargetType; } } - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var serializer = GetMappedSerializer(); return serializer?.SerializeToString(obj); } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { var serializer = GetMappedSerializer(); if (serializer == null) @@ -67,4 +68,4 @@ public override object Deserialize(TextReader tr) // as try/catch is much slower than other means. return returnValue ?? value; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypeMapper.cs b/Ical.Net/Serialization/DataTypeMapper.cs index 87c6d5c1..5dd9cb24 100644 --- a/Ical.Net/Serialization/DataTypeMapper.cs +++ b/Ical.Net/Serialization/DataTypeMapper.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using Ical.Net.CalendarComponents; @@ -16,8 +17,8 @@ internal class DataTypeMapper { private class PropertyMapping { - public Type ObjectType { get; set; } - public TypeResolverDelegate Resolver { get; set; } + public Type? ObjectType { get; set; } + public TypeResolverDelegate? Resolver { get; set; } public bool AllowsMultipleValuesPerProperty { get; set; } } @@ -66,25 +67,21 @@ public DataTypeMapper() protected Type ResolveStatusProperty(object context) { - if (!(context is ICalendarObject obj)) + if (context is not ICalendarObject obj) { - return null; + return typeof(object); // Return a default type to match the delegate signature } - 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), + _ => typeof(object) // Return a default type to match the delegate signature + }; } - public void AddPropertyMapping(string name, Type objectType, bool allowsMultipleValues) + public void AddPropertyMapping(string? name, Type? objectType, bool allowsMultipleValues) { if (name == null || objectType == null) { @@ -100,7 +97,7 @@ public void AddPropertyMapping(string name, Type objectType, bool allowsMultiple _propertyMap[name] = m; } - public void AddPropertyMapping(string name, TypeResolverDelegate resolver, bool allowsMultipleValues) + public void AddPropertyMapping(string? name, TypeResolverDelegate? resolver, bool allowsMultipleValues) { if (name == null || resolver == null) { @@ -116,7 +113,7 @@ public void AddPropertyMapping(string name, TypeResolverDelegate resolver, bool _propertyMap[name] = m; } - public void RemovePropertyMapping(string name) + public void RemovePropertyMapping(string? name) { if (name != null && _propertyMap.ContainsKey(name)) { @@ -132,7 +129,7 @@ public virtual bool GetPropertyAllowsMultipleValues(object obj) && m.AllowsMultipleValuesPerProperty; } - public virtual Type GetPropertyMapping(object obj) + public virtual Type? GetPropertyMapping(object obj) { var p = obj as ICalendarProperty; if (p?.Name == null) diff --git a/Ical.Net/Serialization/DataTypeSerializerFactory.cs b/Ical.Net/Serialization/DataTypeSerializerFactory.cs index ef7c6c4b..034f364c 100644 --- a/Ical.Net/Serialization/DataTypeSerializerFactory.cs +++ b/Ical.Net/Serialization/DataTypeSerializerFactory.cs @@ -3,7 +3,10 @@ // Licensed under the MIT license. // +#nullable enable using System; +using System.Collections.Generic; +using System.Linq; using Ical.Net.DataTypes; using Ical.Net.Serialization.DataTypes; @@ -11,86 +14,42 @@ namespace Ical.Net.Serialization; public class DataTypeSerializerFactory : ISerializerFactory { + private static readonly Dictionary> _serializerMap = + new() + { + { typeof(Attachment), ctx => new AttachmentSerializer(ctx) }, + { typeof(Attendee), ctx => new AttendeeSerializer(ctx) }, + { typeof(CalDateTime), ctx => new DateTimeSerializer(ctx) }, + { typeof(FreeBusyEntry), ctx => new FreeBusyEntrySerializer(ctx) }, + { typeof(GeographicLocation), ctx => new GeographicLocationSerializer(ctx) }, + { typeof(Organizer), ctx => new OrganizerSerializer(ctx) }, + { typeof(Period), ctx => new PeriodSerializer(ctx) }, + { typeof(PeriodList), ctx => new PeriodListSerializer(ctx) }, + { typeof(RecurrencePattern), ctx => new RecurrencePatternSerializer(ctx) }, + { typeof(RequestStatus), ctx => new RequestStatusSerializer(ctx) }, + { typeof(StatusCode), ctx => new StatusCodeSerializer(ctx) }, + { typeof(Trigger), ctx => new TriggerSerializer(ctx) }, + { typeof(UtcOffset), ctx => new UtcOffsetSerializer(ctx) }, + { typeof(WeekDay), ctx => new WeekDaySerializer(ctx) } + }; + /// /// Returns a serializer that can be used to serialize and object /// of type . - /// - /// TODO: Add support for caching. - /// /// /// The type of object to be serialized. /// The serialization context. - public virtual ISerializer Build(Type objectType, SerializationContext ctx) + public virtual ISerializer? Build(Type? objectType, SerializationContext ctx) { - if (objectType != null) - { - ISerializer s; + if (objectType == null) return null; - if (typeof(Attachment).IsAssignableFrom(objectType)) - { - s = new AttachmentSerializer(ctx); - } - else if (typeof(Attendee).IsAssignableFrom(objectType)) - { - s = new AttendeeSerializer(ctx); - } - else if (typeof(CalDateTime).IsAssignableFrom(objectType)) - { - s = new DateTimeSerializer(ctx); - } - else if (typeof(FreeBusyEntry).IsAssignableFrom(objectType)) - { - s = new FreeBusyEntrySerializer(ctx); - } - else if (typeof(GeographicLocation).IsAssignableFrom(objectType)) - { - s = new GeographicLocationSerializer(ctx); - } - else if (typeof(Organizer).IsAssignableFrom(objectType)) - { - s = new OrganizerSerializer(ctx); - } - else if (typeof(Period).IsAssignableFrom(objectType)) - { - s = new PeriodSerializer(ctx); - } - else if (typeof(PeriodList).IsAssignableFrom(objectType)) - { - s = new PeriodListSerializer(ctx); - } - else if (typeof(RecurrencePattern).IsAssignableFrom(objectType)) - { - s = new RecurrencePatternSerializer(ctx); - } - else if (typeof(RequestStatus).IsAssignableFrom(objectType)) - { - s = new RequestStatusSerializer(ctx); - } - else if (typeof(StatusCode).IsAssignableFrom(objectType)) - { - s = new StatusCodeSerializer(ctx); - } - else if (typeof(Trigger).IsAssignableFrom(objectType)) - { - s = new TriggerSerializer(ctx); - } - else if (typeof(UtcOffset).IsAssignableFrom(objectType)) - { - s = new UtcOffsetSerializer(ctx); - } - else if (typeof(WeekDay).IsAssignableFrom(objectType)) - { - s = new WeekDaySerializer(ctx); - } - // Default to a string serializer, which simply calls - // ToString() on the value to serialize it. - else - { - s = new StringSerializer(ctx); - } + // Check if the type exists in the map + var serializer = _serializerMap + .Where(entry => entry.Key.IsAssignableFrom(objectType)) + .Select(entry => entry.Value(ctx)) + .FirstOrDefault(); - return s; - } - return null; + // Return the found serializer or default to a string serializer + return serializer ?? new StringSerializer(ctx); } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs b/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs index 49c5d2e5..ad54d5e4 100644 --- a/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,7 +18,7 @@ public AttachmentSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(Attachment); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var a = obj as Attachment; if (a == null) @@ -49,17 +50,22 @@ public override string SerializeToString(object obj) return Encode(a, a.Data); } - public Attachment Deserialize(string attachment) + public Attachment? Deserialize(string attachment) { try { var a = CreateAndAssociate() as Attachment; + if (a == null) return null; + // Decode the value, if necessary var data = DecodeData(a, attachment); // Get the currently-used encoding off the encoding stack. var encodingStack = GetService(); - a.ValueEncoding = encodingStack.Current; + if (encodingStack != null) + { + a.ValueEncoding = encodingStack.Current; + } // Get the format of the attachment var valueType = a.GetValueType(); @@ -74,11 +80,13 @@ public Attachment Deserialize(string attachment) }; } - // The default VALUE type for attachments is URI. So, let's - // grab the URI by default. + // The default VALUE type for attachments is URI. + // So, let's grab the URI by default. var uriValue = Decode(a, attachment); - a.Uri = new Uri(uriValue, UriKind.RelativeOrAbsolute); + if (string.IsNullOrEmpty(uriValue)) return null; + + a.Uri = new Uri(uriValue, UriKind.RelativeOrAbsolute); return a; } @@ -90,5 +98,5 @@ public Attachment Deserialize(string attachment) return null; } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); -} \ No newline at end of file + public override object? Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); +} diff --git a/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs b/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs index 780d13c9..e9fc266c 100644 --- a/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,7 +18,7 @@ public AttendeeSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(Attendee); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var a = obj as Attendee; return a?.Value == null @@ -25,13 +26,16 @@ public override string SerializeToString(object obj) : Encode(a, a.Value.OriginalString); } - public Attendee Deserialize(string attendee) + public Attendee? Deserialize(string attendee) { try { - var a = CreateAndAssociate() as Attendee; + if (CreateAndAssociate() is not Attendee a) return null; + var uriString = Unescape(Decode(a, attendee)); + if (uriString == null) return a; + // Prepend "mailto:" if necessary if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) { @@ -39,15 +43,16 @@ public Attendee Deserialize(string attendee) } a.Value = new Uri(uriString); + return a; } catch { - // ignored + // Return null instead of throwing an exception } return null; } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); -} \ No newline at end of file + public override object? Deserialize(TextReader? tr) => tr != null ? Deserialize(tr.ReadToEnd()) : null; +} diff --git a/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs b/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs index 6bc28e43..35830a1e 100644 --- a/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using Ical.Net.DataTypes; @@ -14,7 +15,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, true) is not ICalendarDataType dt) @@ -22,7 +23,7 @@ protected virtual ICalendarDataType CreateAndAssociate() return null; } - if (SerializationContext.Peek() is ICalendarObject associatedObject) + if (SerializationContext?.Peek() is ICalendarObject associatedObject) { dt.AssociatedObject = associatedObject; } diff --git a/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs b/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs index d83def04..dd82b655 100644 --- a/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs @@ -31,7 +31,7 @@ public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(CalDateTime); - public override string? SerializeToString(object obj) + public override string? SerializeToString(object? obj) { if (obj is not CalDateTime dt) { @@ -68,7 +68,7 @@ public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } var value = tr.ReadToEnd(); // CalDateTime is defined as the Target type - var parent = SerializationContext.Peek(); + var parent = SerializationContext?.Peek(); // The associated object is an ICalendarObject of type CalendarProperty // that contains any timezone ("TZID" property) deserialized in a prior step diff --git a/Ical.Net/Serialization/DataTypes/DurationSerializer.cs b/Ical.Net/Serialization/DataTypes/DurationSerializer.cs index 9abd16f5..a695d808 100644 --- a/Ical.Net/Serialization/DataTypes/DurationSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DurationSerializer.cs @@ -20,7 +20,7 @@ public DurationSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(Duration); - public override string? SerializeToString(object obj) + public override string? SerializeToString(object? obj) => (obj is not Duration duration) ? null : SerializeToString(duration); private static string SerializeToString(Duration ts) diff --git a/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs b/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs index 4edae16d..7480fbf7 100644 --- a/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using Ical.Net.DataTypes; namespace Ical.Net.Serialization.DataTypes; @@ -13,42 +14,44 @@ 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) { return null; } - if (dt?.Encoding == null) + if (dt.Encoding == null) { return value; } // Return the value in the current encoding var encodingStack = GetService(); - return Encode(dt, encodingStack.Current.GetBytes(value)); + return encodingStack?.Current == null + ? value + : Encode(dt, encodingStack.Current.GetBytes(value)); } - protected string Encode(IEncodableDataType dt, byte[] data) + protected string? Encode(IEncodableDataType dt, byte[]? data) { if (data == null) { return null; } - if (dt?.Encoding == null) + if (dt.Encoding == null) { // Default to the current encoding var encodingStack = GetService(); - return encodingStack.Current.GetString(data); + return encodingStack?.Current?.GetString(data); } var encodingProvider = GetService(); return encodingProvider?.Encode(dt.Encoding, data); } - protected string Decode(IEncodableDataType dt, string value) + protected string? Decode(IEncodableDataType dt, string value) { if (dt?.Encoding == null) { @@ -63,10 +66,10 @@ protected string Decode(IEncodableDataType dt, string value) // Default to the current encoding var encodingStack = GetService(); - return encodingStack.Current.GetString(data); + return encodingStack?.Current?.GetString(data); } - protected byte[] DecodeData(IEncodableDataType dt, string value) + protected byte[]? DecodeData(IEncodableDataType dt, string? value) { if (value == null) { @@ -77,10 +80,10 @@ protected byte[] DecodeData(IEncodableDataType dt, string value) { // Default to the current encoding var encodingStack = GetService(); - return encodingStack.Current.GetBytes(value); + return encodingStack?.Current?.GetBytes(value); } var encodingProvider = GetService(); return encodingProvider?.DecodeData(dt.Encoding, value); } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/EnumSerializer.cs b/Ical.Net/Serialization/DataTypes/EnumSerializer.cs index d4ee2080..5507adf6 100644 --- a/Ical.Net/Serialization/DataTypes/EnumSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/EnumSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -25,21 +26,20 @@ public EnumSerializer(Type enumType, SerializationContext ctx) : base(ctx) public override Type TargetType => _mEnumType; - public override string SerializeToString(object enumValue) + public override string? SerializeToString(object? obj) { try { - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + if (SerializationContext?.Peek() is ICalendarObject calObject) { // Encode the value as needed. var dt = new EncodableDataType { - AssociatedObject = obj + AssociatedObject = calObject }; - return Encode(dt, enumValue.ToString()); + return Encode(dt, obj?.ToString()); } - return enumValue.ToString(); + return obj?.ToString(); } catch { @@ -47,14 +47,13 @@ public override string SerializeToString(object enumValue) } } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { var value = tr.ReadToEnd(); 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 @@ -65,10 +64,14 @@ public override object Deserialize(TextReader tr) } // Remove "-" characters while parsing Enum values. - return Enum.Parse(_mEnumType, value.Replace("-", ""), true); + if (value != null) + return Enum.Parse(_mEnumType, value.Replace("-", ""), true); + } + catch + { + // Return null instead of throwing an exception } - catch { } return value; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs b/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs index 4bf6fea6..e6dfe96f 100644 --- a/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs +++ b/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,10 +18,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 not FreeBusyEntry entry) { return base.SerializeToString(obj); } @@ -44,12 +44,11 @@ public override string SerializeToString(object obj) return base.SerializeToString(obj); } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { - var entry = base.Deserialize(tr) as FreeBusyEntry; - if (entry == null) + if (base.Deserialize(tr) is not FreeBusyEntry entry) { - return entry; + return null; } if (!entry.Parameters.ContainsKey("FBTYPE")) @@ -81,4 +80,4 @@ public override object Deserialize(TextReader tr) return entry; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs b/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs index f2f36b66..63ea31b3 100644 --- a/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Globalization; using System.IO; @@ -18,50 +19,47 @@ public GeographicLocationSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(GeographicLocation); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - var g = obj as GeographicLocation; - if (g == null) + if (obj is not GeographicLocation location) { return null; } - var value = g.Latitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat) + ";" - + g.Longitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat); - return Encode(g, value); + var value = location.Latitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat) + ";" + + location.Longitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat); + return Encode(location, value); } - public GeographicLocation Deserialize(string value) + public GeographicLocation? Deserialize(string value) { if (string.IsNullOrWhiteSpace(value)) { return null; } - var g = CreateAndAssociate() as GeographicLocation; - if (g == null) + if (CreateAndAssociate() is not GeographicLocation location) { return null; } // Decode the value, if necessary! - value = Decode(g, value); + var decoded = Decode(location, value); + if (decoded == null) return null; - var values = value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + var values = decoded.Split([';'], StringSplitOptions.RemoveEmptyEntries); if (values.Length != 2) { return null; } - double lat; - double lon; - double.TryParse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture, out lat); - double.TryParse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture, out lon); - g.Latitude = lat; - g.Longitude = lon; + double.TryParse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var lat); + double.TryParse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var lon); + location.Latitude = lat; + location.Longitude = lon; - return g; + return location; } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); -} \ No newline at end of file + public override object? Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); +} diff --git a/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs b/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs index a63aafc0..0bf78575 100644 --- a/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,19 +18,18 @@ public IntegerSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(int); - public override string SerializeToString(object integer) + public override string? SerializeToString(object? obj) { try { - var i = Convert.ToInt32(integer); + var i = Convert.ToInt32(obj); - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + if (SerializationContext?.Peek() is ICalendarObject calObject) { // Encode the value as needed. var dt = new EncodableDataType { - AssociatedObject = obj + AssociatedObject = calObject }; return Encode(dt, i.ToString()); } @@ -41,14 +41,13 @@ public override string SerializeToString(object integer) } } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { var value = tr.ReadToEnd(); 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 @@ -58,14 +57,16 @@ public override object Deserialize(TextReader tr) value = Decode(dt, value); } - int i; - if (Int32.TryParse(value, out i)) + if (int.TryParse(value, out var i)) { return i; } } - catch { } + catch + { + // Return null instead of throwing an exception + } - return value; + return null; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs b/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs index a41f8bc7..4e106fea 100644 --- a/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,7 +18,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 { @@ -32,17 +33,23 @@ public override string SerializeToString(object obj) } } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader? tr) { + if (tr == null) return null; + var value = tr.ReadToEnd(); - Organizer o = null; + Organizer? organizer = null; try { - o = CreateAndAssociate() as Organizer; - if (o != null) + organizer = CreateAndAssociate() as Organizer; + if (organizer != null) { - var uriString = Unescape(Decode(o, value)); + var uriString = Unescape(Decode(organizer, value)); + if (uriString == null) + { + return null; + } // Prepend "mailto:" if necessary if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) @@ -50,11 +57,14 @@ public override object Deserialize(TextReader tr) uriString = "mailto:" + uriString; } - o.Value = new Uri(uriString); + organizer.Value = new Uri(uriString); } } - catch { } + catch + { + // Return null instead of throwing an exception + } - return o; + return organizer; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs b/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs index 9d20af07..df976082 100644 --- a/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs @@ -20,10 +20,10 @@ public PeriodListSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(PeriodList); - public override string? SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var factory = GetService(); - if (obj is not PeriodList periodList || factory == null) + if (obj is not PeriodList periodList || factory == null || SerializationContext == null) { return null; } @@ -35,7 +35,7 @@ public PeriodListSerializer(SerializationContext ctx) : base(ctx) { } return null; } - var parts = new List(periodList.Count); + var parts = new List(periodList.Count); var firstPeriod = periodList.FirstOrDefault(); @@ -72,7 +72,7 @@ public PeriodListSerializer(SerializationContext ctx) : base(ctx) { } // Create the day specifier and associate it with a calendar object var rdt = CreateAndAssociate() as PeriodList; var factory = GetService(); - if (rdt == null || factory == null) + if (rdt == null || factory == null || SerializationContext == null) { return null; } @@ -82,7 +82,7 @@ public PeriodListSerializer(SerializationContext ctx) : base(ctx) { } var dtSerializer = factory.Build(typeof(CalDateTime), SerializationContext) as IStringSerializer; var periodSerializer = factory.Build(typeof(Period), SerializationContext) as IStringSerializer; - if (dtSerializer == null || periodSerializer == null) + if (value == null || dtSerializer == null || periodSerializer == null) { return null; } diff --git a/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs b/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs index 3d0271f0..b2f19dce 100644 --- a/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs @@ -19,7 +19,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 factory = GetService(); @@ -29,7 +29,7 @@ public PeriodSerializer(SerializationContext ctx) : base(ctx) { } } // Push the period onto the serialization context stack - SerializationContext.Push(p); + SerializationContext?.Push(p); try { @@ -71,7 +71,7 @@ public PeriodSerializer(SerializationContext ctx) : base(ctx) { } finally { // Pop the period off the serialization context stack - SerializationContext.Pop(); + SerializationContext?.Pop(); } } @@ -95,11 +95,12 @@ public PeriodSerializer(SerializationContext ctx) : base(ctx) { } // Decode the value as necessary value = Decode(p, value); + if (value == null) return null; var values = value.Split('/'); if (values.Length != 2) { - return false; + return null; } var start = dtSerializer.Deserialize(new StringReader(values[0])) as CalDateTime; diff --git a/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs b/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs index 7aa02703..b1887d85 100644 --- a/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs @@ -103,10 +103,10 @@ private static void SerializeByValue(List aggregate, IList byValue, public override Type TargetType => typeof(RecurrencePattern); - public override string? SerializeToString(object obj) + public override string? SerializeToString(object? obj) { var factory = GetService(); - if (obj is not RecurrencePattern recur || factory == null) + if (obj is not RecurrencePattern recur || factory == null || SerializationContext == null) { return null; } @@ -154,7 +154,11 @@ private static void SerializeByValue(List aggregate, IList byValue, if (factory.Build(typeof(WeekDay), SerializationContext) is IStringSerializer serializer) { - bydayValues.AddRange(recur.ByDay.Select(byday => serializer.SerializeToString(byday))); + bydayValues.AddRange(recur.ByDay + .Select(byday => serializer.SerializeToString(byday)) + .Where(serialized => serialized != null) + // tell the compiler that the filtered values are not null after the Where clause + .Select(serialized => serialized!)); } values.Add($"BYDAY={string.Join(",", bydayValues)}"); @@ -203,6 +207,7 @@ private static void SerializeByValue(List aggregate, IList byValue, // Decode the value, if necessary value = Decode(r, value); + if (value == null) return null; DeserializePattern(value, r, factory); return r; @@ -250,6 +255,11 @@ private void DeserializePattern(string value, RecurrencePattern r, ISerializerFa private void ProcessKeyValuePair(string key, string value, RecurrencePattern r, ISerializerFactory factory) { + if (SerializationContext == null) + { + throw new InvalidOperationException("SerializationContext is not set."); + } + switch (key) { case "freq": diff --git a/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs b/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs index 429131bf..067deca1 100644 --- a/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using System.Text; @@ -19,12 +20,16 @@ public RequestStatusSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(RequestStatus); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { + if (SerializationContext == null) + { + return null; + } + try { - var rs = obj as RequestStatus; - if (rs == null) + if (obj is not RequestStatus rs) { return null; } @@ -55,7 +60,7 @@ public override string SerializeToString(object obj) finally { // Pop the object off the serialization stack - SerializationContext.Pop(); + SerializationContext?.Pop(); } } catch @@ -67,18 +72,20 @@ public override string SerializeToString(object obj) internal static readonly Regex NarrowRequestMatch = new Regex(@"(.*?[^\\]);(.*?[^\\]);(.+)", RegexOptions.Compiled, RegexDefaults.Timeout); internal static readonly Regex BroadRequestMatch = new Regex(@"(.*?[^\\]);(.+)", RegexOptions.Compiled, RegexDefaults.Timeout); - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader? tr) { + if (tr == null || SerializationContext == null) return null; + var value = tr.ReadToEnd(); - var rs = CreateAndAssociate() as RequestStatus; - if (rs == null) + if (CreateAndAssociate() is not RequestStatus rs) { return null; } // Decode the value as needed value = Decode(rs, value); + if (value == null) return null; // Push the object onto the serialization stack SerializationContext.Push(rs); @@ -105,7 +112,10 @@ public override object Deserialize(TextReader tr) return null; } - rs.StatusCode = serializer.Deserialize(new StringReader(Unescape(match.Groups[1].Value))) as StatusCode; + var unescaped = Unescape(match.Groups[1].Value); + if (unescaped != null) + rs.StatusCode = serializer.Deserialize(new StringReader(unescaped)) as StatusCode; + rs.Description = Unescape(match.Groups[2].Value); if (match.Groups.Count == 4) { @@ -118,8 +128,8 @@ public override object Deserialize(TextReader tr) finally { // Pop the object off the serialization stack - SerializationContext.Pop(); + SerializationContext?.Pop(); } return null; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs b/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs index 634e5c9f..3366f74b 100644 --- a/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using System.Text.RegularExpressions; @@ -18,10 +19,9 @@ public StatusCodeSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(StatusCode); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - var sc = obj as StatusCode; - if (sc == null) + if (obj is not StatusCode sc) { return null; } @@ -36,18 +36,20 @@ public override string SerializeToString(object obj) internal static readonly Regex StatusCode = new Regex(@"\d(\.\d+)*", RegexOptions.Compiled | RegexOptions.CultureInvariant, RegexDefaults.Timeout); - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader? tr) { + if (tr == null) return null; + var value = tr.ReadToEnd(); - var sc = CreateAndAssociate() as StatusCode; - if (sc == null) + if (CreateAndAssociate() is not StatusCode sc) { return null; } // Decode the value as needed value = Decode(sc, value); + if (value == null) return null; var match = StatusCode.Match(value); if (!match.Success) @@ -56,17 +58,16 @@ public override object Deserialize(TextReader tr) } var parts = match.Value.Split('.'); - var iparts = new int[parts.Length]; + var intParts = new int[parts.Length]; for (var i = 0; i < parts.Length; i++) { - int num; - if (!int.TryParse(parts[i], out num)) + if (!int.TryParse(parts[i], out var num)) { return false; } - iparts[i] = num; + intParts[i] = num; } - return new StatusCode(iparts); + return new StatusCode(intParts); } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/StringSerializer.cs b/Ical.Net/Serialization/DataTypes/StringSerializer.cs index 6b9f6334..d74b322d 100644 --- a/Ical.Net/Serialization/DataTypes/StringSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/StringSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections; using System.Collections.Generic; @@ -21,7 +22,7 @@ public StringSerializer(SerializationContext ctx) : base(ctx) { } internal static readonly Regex SingleBackslashMatch = new Regex(@"(? typeof(string); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { if (obj == null) { @@ -71,17 +72,16 @@ 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 @@ -90,7 +90,11 @@ public override string SerializeToString(object obj) }; for (var i = 0; i < values.Count; i++) { - values[i] = Encode(dt, Escape(values[i])); + var value = Encode(dt, Escape(values[i])); + if (value != null) + { + values[i] = value; + } } return string.Join(",", values); @@ -98,13 +102,17 @@ public override string SerializeToString(object obj) for (var i = 0; i < values.Count; i++) { - values[i] = Escape(values[i]); + var escaped = Escape(values[i]); + if (escaped != null) + { + values[i] = escaped; + } } return string.Join(",", values); } internal static readonly Regex UnescapedCommas = new Regex(@"(? or simply a string, - // depending on the input text. Anything that uses this serializer should - // be prepared to receive either a string, or an IList. - var serializeAsList = false; - // Determine if we can serialize this property - // with multiple values per line. - var co = SerializationContext.Peek() as ICalendarObject; - if (co is ICalendarProperty) + // Ensure SerializationContext is not null before accessing Peek() + var context = SerializationContext; + if (context?.Peek() is ICalendarProperty cp) { - serializeAsList = GetService().GetPropertyAllowsMultipleValues(co); + var dataTypeMapper = GetService(); + if (dataTypeMapper != null) + { + serializeAsList = dataTypeMapper.GetPropertyAllowsMultipleValues(cp); + } } - // Try to decode the string - EncodableDataType dt = null; - if (co != null) + var dt = new EncodableDataType { - dt = new EncodableDataType - { - AssociatedObject = co - }; - } + AssociatedObject = context?.Peek() as ICalendarObject + }; var encodedValues = serializeAsList ? UnescapedCommas.Split(value) : new[] { value }; var escapedValues = encodedValues.Select(v => Decode(dt, v)).ToList(); var values = escapedValues.Select(Unescape).ToList(); - // Return either a single value, or the entire list. if (values.Count == 1) { return values[0]; diff --git a/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs b/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs index 8d6c49e8..39c29269 100644 --- a/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/TriggerSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,17 +18,17 @@ 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)) + if (obj is not Trigger t) { return null; } // Push the trigger onto the serialization stack - SerializationContext.Push(t); + SerializationContext?.Push(t); try { var factory = GetService(); @@ -44,13 +45,14 @@ public override string SerializeToString(object obj) var value = valueType == typeof(CalDateTime) ? t.DateTime - : (object) t.Duration; + : (object?) t.Duration; + return serializer.SerializeToString(value); } finally { // Pop the trigger off the serialization stack - SerializationContext.Pop(); + SerializationContext?.Pop(); } } catch @@ -59,8 +61,10 @@ public override string SerializeToString(object obj) } } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader? tr) { + if (tr == null) return null; + var value = tr.ReadToEnd(); if (!(CreateAndAssociate() is Trigger t)) @@ -69,12 +73,14 @@ public override object Deserialize(TextReader tr) } // Push the trigger onto the serialization stack - SerializationContext.Push(t); + SerializationContext?.Push(t); try { // Decode the value as needed value = Decode(t, value); + if (value == null) return null; + // Set the trigger relation if (t.Parameters.ContainsKey("RELATED") && t.Parameters.Get("RELATED").Equals("END")) { @@ -94,8 +100,8 @@ public override object Deserialize(TextReader tr) { case null: return null; - case CalDateTime _: - t.DateTime = (CalDateTime) obj; + case CalDateTime dt: + t.DateTime = dt; break; default: t.Duration = (Duration) obj; @@ -107,7 +113,7 @@ public override object Deserialize(TextReader tr) finally { // Pop the trigger off the serialization stack - SerializationContext.Pop(); + SerializationContext?.Pop(); } } } diff --git a/Ical.Net/Serialization/DataTypes/UriSerializer.cs b/Ical.Net/Serialization/DataTypes/UriSerializer.cs index c3d63903..104b3fdc 100644 --- a/Ical.Net/Serialization/DataTypes/UriSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/UriSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using Ical.Net.DataTypes; @@ -17,16 +18,14 @@ public UriSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(string); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - if (!(obj is Uri)) + if (obj is not Uri uri) { return null; } - var uri = (Uri) obj; - - if (SerializationContext.Peek() is ICalendarObject co) + if (SerializationContext?.Peek() is ICalendarObject co) { var dt = new EncodableDataType { @@ -37,7 +36,7 @@ public override string SerializeToString(object obj) return uri.OriginalString; } - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader? tr) { if (tr == null) { @@ -46,13 +45,14 @@ public override object Deserialize(TextReader tr) var value = tr.ReadToEnd(); - if (SerializationContext.Peek() is ICalendarObject co) + if (SerializationContext?.Peek() is ICalendarObject co) { var dt = new EncodableDataType { AssociatedObject = co }; value = Decode(dt, value); + if (value == null) return null; } try @@ -60,7 +60,10 @@ public override object Deserialize(TextReader tr) var uri = new Uri(value, UriKind.RelativeOrAbsolute); return uri; } - catch { } + catch + { + // Return null instead of throwing an exception + } return null; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs b/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs index 0bad992f..9c8fa40d 100644 --- a/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Globalization; using System.IO; -using System.Text.RegularExpressions; using Ical.Net.DataTypes; namespace Ical.Net.Serialization.DataTypes; @@ -19,7 +19,7 @@ public UtcOffsetSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(UtcOffset); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { if (obj is not UtcOffset offset) return null; @@ -30,43 +30,34 @@ public override string SerializeToString(object obj) return Encode(offset, value); } - internal static readonly Regex DecodeOffset = new Regex(@"(\+|-)(\d{2})(\d{2})(\d{2})?", RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexDefaults.Timeout); - - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { var offsetString = tr.ReadToEnd(); - var offset = new UtcOffset(offsetString); - return offset; - } - - public static TimeSpan GetOffset(string rawOffset) - { - if (rawOffset.EndsWith("00")) + try { - rawOffset = rawOffset.Substring(0, rawOffset.Length - 2); + var offset = new UtcOffset(offsetString); + return offset; } - - DateTimeOffset temp; - if (DateTimeOffset.TryParse("2016-01-01 00:00:00 " + rawOffset, out temp)) + catch { - return temp.Offset; + return null; } + } - TimeSpan ts; + public static TimeSpan GetOffset(string rawOffset) + { + // Determine if the offset is negative var isNegative = rawOffset.StartsWith("-"); + rawOffset = rawOffset.TrimStart('+', '-'); - if (isNegative || rawOffset.StartsWith("+")) - { - rawOffset = rawOffset.Substring(1, rawOffset.Length - 1); - } + // Supported formats + var formats = new[] { "hhmmss", "hhmm", "hh" }; - if (!TimeSpan.TryParseExact(rawOffset, "hhmmss", CultureInfo.InvariantCulture, out ts)) + if (TimeSpan.TryParseExact(rawOffset, formats, CultureInfo.InvariantCulture, out var ts)) { - throw new FormatException($"{rawOffset} is not a valid UTC offset"); + return isNegative ? -ts : ts; } - return isNegative - ? -ts - : ts; + throw new FormatException($"{rawOffset} is not a valid UTC offset."); } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs b/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs index 9308d670..83a67197 100644 --- a/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs +++ b/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using System.Text.RegularExpressions; @@ -18,9 +19,9 @@ public WeekDaySerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(WeekDay); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - if (!(obj is WeekDay ds)) + if (obj is not WeekDay ds) { return null; } @@ -30,22 +31,33 @@ public override string SerializeToString(object obj) { value += ds.Offset; } - value += Enum.GetName(typeof(DayOfWeek), ds.DayOfWeek).ToUpper().Substring(0, 2); + + try + { + var name = Enum.GetName(typeof(DayOfWeek), ds.DayOfWeek); + value += name!.ToUpper().Substring(0, 2); + } + catch + { + return null; + } return Encode(ds, value); } private static readonly Regex _dayOfWeek = new Regex(@"(\+|-)?(\d{1,2})?(\w{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexDefaults.Timeout); - public override object Deserialize(TextReader tr) + public override object? Deserialize(TextReader tr) { var value = tr.ReadToEnd(); // Create the day specifier and associate it with a calendar object - var ds = CreateAndAssociate() as WeekDay; + if (CreateAndAssociate() is not WeekDay ds) + return null; // Decode the value, if necessary value = Decode(ds, value); + if (value == null) return null; var match = _dayOfWeek.Match(value); if (!match.Success) diff --git a/Ical.Net/Serialization/EncodingStack.cs b/Ical.Net/Serialization/EncodingStack.cs index fa04b5a0..eed211e8 100644 --- a/Ical.Net/Serialization/EncodingStack.cs +++ b/Ical.Net/Serialization/EncodingStack.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Collections.Generic; using System.Text; @@ -22,7 +23,7 @@ public EncodingStack() ? _mStack.Peek() : Encoding.UTF8; - public void Push(Encoding encoding) + public void Push(Encoding? encoding) { if (encoding != null) { @@ -30,7 +31,7 @@ public void Push(Encoding encoding) } } - public Encoding Pop() => _mStack.Count > 0 + public Encoding? Pop() => _mStack.Count > 0 ? _mStack.Pop() : null; -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/EventSerializer.cs b/Ical.Net/Serialization/EventSerializer.cs index 62facad9..6618e605 100644 --- a/Ical.Net/Serialization/EventSerializer.cs +++ b/Ical.Net/Serialization/EventSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using Ical.Net.CalendarComponents; @@ -16,14 +17,15 @@ public EventSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(CalendarEvent); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - var evt = obj as CalendarEvent; + if (obj is not CalendarEvent evt) + return null; - CalendarEvent actualEvent; + CalendarEvent? actualEvent; if (evt.Properties.ContainsKey("DURATION") && evt.Properties.ContainsKey("DTEND")) { - actualEvent = evt.Copy(); + actualEvent = evt.Copy()!; actualEvent.Properties.Remove("DURATION"); } else @@ -32,4 +34,4 @@ public override string SerializeToString(object obj) } return base.SerializeToString(actualEvent); } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/GenericListSerializer.cs b/Ical.Net/Serialization/GenericListSerializer.cs index 4a784490..ae9899f8 100644 --- a/Ical.Net/Serialization/GenericListSerializer.cs +++ b/Ical.Net/Serialization/GenericListSerializer.cs @@ -27,12 +27,12 @@ public GenericListSerializer(Type objectType) public override Type TargetType => _objectType; - public override string SerializeToString(object obj) => throw new NotImplementedException(); + public override string SerializeToString(object? obj) => throw new NotImplementedException(); private MethodInfo? _addMethodInfo; public override object? Deserialize(TextReader tr) { - var p = SerializationContext.Peek() as ICalendarProperty; + var p = SerializationContext?.Peek() as ICalendarProperty; if (p == null) { return null; @@ -47,7 +47,7 @@ public GenericListSerializer(Type objectType) // Get a serializer for the inner type var sf = GetService(); - var stringSerializer = sf.Build(_innerType, SerializationContext) as IStringSerializer; + var stringSerializer = sf?.Build(_innerType, SerializationContext) as IStringSerializer; if (stringSerializer == null) { return null; diff --git a/Ical.Net/Serialization/IEncodingProvider.cs b/Ical.Net/Serialization/IEncodingProvider.cs index fbd74b82..2e353d08 100644 --- a/Ical.Net/Serialization/IEncodingProvider.cs +++ b/Ical.Net/Serialization/IEncodingProvider.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable namespace Ical.Net.Serialization; internal interface IEncodingProvider @@ -10,4 +11,4 @@ internal interface IEncodingProvider string Encode(string encoding, byte[] data); string DecodeString(string encoding, string value); byte[] DecodeData(string encoding, string value); -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/IParameterProvider.cs b/Ical.Net/Serialization/IParameterProvider.cs index fdffe810..1a7a1515 100644 --- a/Ical.Net/Serialization/IParameterProvider.cs +++ b/Ical.Net/Serialization/IParameterProvider.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Collections.Generic; namespace Ical.Net.Serialization; diff --git a/Ical.Net/Serialization/ISerializer.cs b/Ical.Net/Serialization/ISerializer.cs index 087f26e0..f72c4e7b 100644 --- a/Ical.Net/Serialization/ISerializer.cs +++ b/Ical.Net/Serialization/ISerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using System.Text; @@ -11,9 +12,9 @@ namespace Ical.Net.Serialization; public interface ISerializer : IServiceProvider { - SerializationContext SerializationContext { get; set; } + SerializationContext? SerializationContext { get; set; } Type TargetType { get; } void Serialize(object obj, Stream stream, Encoding encoding); - object Deserialize(Stream stream, Encoding encoding); -} \ No newline at end of file + object? Deserialize(Stream stream, Encoding encoding); +} diff --git a/Ical.Net/Serialization/ISerializerFactory.cs b/Ical.Net/Serialization/ISerializerFactory.cs index 0f735e1a..34c7552d 100644 --- a/Ical.Net/Serialization/ISerializerFactory.cs +++ b/Ical.Net/Serialization/ISerializerFactory.cs @@ -3,11 +3,12 @@ // Licensed under the MIT license. // +#nullable enable using System; namespace Ical.Net.Serialization; public interface ISerializerFactory { - ISerializer Build(Type objectType, SerializationContext ctx); -} \ No newline at end of file + ISerializer? Build(Type objectType, SerializationContext ctx); +} diff --git a/Ical.Net/Serialization/IStringSerializer.cs b/Ical.Net/Serialization/IStringSerializer.cs index f3e4ea00..0e1510d6 100644 --- a/Ical.Net/Serialization/IStringSerializer.cs +++ b/Ical.Net/Serialization/IStringSerializer.cs @@ -3,12 +3,13 @@ // Licensed under the MIT license. // +#nullable enable using System.IO; namespace Ical.Net.Serialization; public interface IStringSerializer : ISerializer { - string SerializeToString(object obj); - object Deserialize(TextReader tr); -} \ No newline at end of file + string? SerializeToString(object? obj); + object? Deserialize(TextReader tr); +} diff --git a/Ical.Net/Serialization/ParameterSerializer.cs b/Ical.Net/Serialization/ParameterSerializer.cs index c4207a4d..06a54be0 100644 --- a/Ical.Net/Serialization/ParameterSerializer.cs +++ b/Ical.Net/Serialization/ParameterSerializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using System.Text; @@ -17,9 +18,9 @@ public ParameterSerializer(SerializationContext ctx) : base(ctx) { } public override Type TargetType => typeof(CalendarParameter); - public override string SerializeToString(object obj) + public override string? SerializeToString(object? obj) { - if (!(obj is CalendarParameter p)) + if (obj is not CalendarParameter p) { return null; } @@ -41,5 +42,5 @@ public override string SerializeToString(object obj) return builder.ToString(); } - public override object Deserialize(TextReader tr) => null; -} \ No newline at end of file + public override object? Deserialize(TextReader tr) => null; +} diff --git a/Ical.Net/Serialization/PropertySerializer.cs b/Ical.Net/Serialization/PropertySerializer.cs index d426f6ab..6e280c83 100644 --- a/Ical.Net/Serialization/PropertySerializer.cs +++ b/Ical.Net/Serialization/PropertySerializer.cs @@ -22,7 +22,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,11 +31,12 @@ public PropertySerializer(SerializationContext ctx) : base(ctx) { } } // Push this object on the serialization context. - SerializationContext.Push(prop); + SerializationContext?.Push(prop); // Get a serializer factory that we can use to serialize // the property and parameter values var sf = GetService(); + if (sf == null) return null; var result = new StringBuilder(); foreach (var v in prop.Values.Where(value => value != null)) @@ -44,7 +45,7 @@ public PropertySerializer(SerializationContext ctx) : base(ctx) { } } // Pop the object off the serialization context. - SerializationContext.Pop(); + SerializationContext?.Pop(); return result.ToString(); } diff --git a/Ical.Net/Serialization/SerializationContext.cs b/Ical.Net/Serialization/SerializationContext.cs index bdbed498..b7f9aec4 100644 --- a/Ical.Net/Serialization/SerializationContext.cs +++ b/Ical.Net/Serialization/SerializationContext.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; @@ -10,7 +11,7 @@ namespace Ical.Net.Serialization; public class SerializationContext { - private static SerializationContext _default; + private static SerializationContext? _default; /// /// Gets the Singleton instance of the SerializationContext class. @@ -19,10 +20,7 @@ public static SerializationContext Default { get { - if (_default == null) - { - _default = new SerializationContext(); - } + _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 @@ -51,7 +49,7 @@ public SerializationContext() SetService(new EncodingProvider(this)); } - public virtual void Push(object item) + public virtual void Push(object? item) { if (item != null) { @@ -59,7 +57,7 @@ public virtual void Push(object item) } } - public virtual object Pop() + public virtual object? Pop() { if (_mStack.Count > 0) { @@ -72,7 +70,7 @@ public virtual object Pop() return null; } - public virtual object Peek() + public virtual object? Peek() { if (_mStack.Count > 0) { @@ -93,23 +91,11 @@ public virtual object Peek() public virtual T GetService(string name) => _mServiceProvider.GetService(name); - public virtual void SetService(string name, object obj) - { - _mServiceProvider.SetService(name, obj); - } + public virtual void SetService(string name, object obj) => _mServiceProvider.SetService(name, obj); - public virtual void SetService(object obj) - { - _mServiceProvider.SetService(obj); - } + public virtual void SetService(object obj) => _mServiceProvider.SetService(obj); - public virtual void RemoveService(Type type) - { - _mServiceProvider.RemoveService(type); - } + public virtual void RemoveService(Type type) => _mServiceProvider.RemoveService(type); - public virtual void RemoveService(string name) - { - _mServiceProvider.RemoveService(name); - } -} \ No newline at end of file + public virtual void RemoveService(string name) => _mServiceProvider.RemoveService(name); +} diff --git a/Ical.Net/Serialization/SerializationUtil.cs b/Ical.Net/Serialization/SerializationUtil.cs index b8e783ca..17d026da 100644 --- a/Ical.Net/Serialization/SerializationUtil.cs +++ b/Ical.Net/Serialization/SerializationUtil.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -53,14 +54,14 @@ private static List GetDeserializingMethods(Type targetType) } private static ConcurrentDictionary> _onDeserializedMethods = new ConcurrentDictionary>(); - private static List GetDeserializedMethods(Type targetType) + private static List GetDeserializedMethods(Type? targetType) { if (targetType == null) { return new List(); } - List methodInfos; - if (_onDeserializedMethods.TryGetValue(targetType, out methodInfos)) + + if (_onDeserializedMethods.TryGetValue(targetType, out var methodInfos)) { return methodInfos; } @@ -77,4 +78,4 @@ private static List GetDeserializedMethods(Type targetType) _onDeserializedMethods.AddOrUpdate(targetType, methodInfos, (type, list) => methodInfos); return methodInfos; } -} \ No newline at end of file +} diff --git a/Ical.Net/Serialization/SerializerBase.cs b/Ical.Net/Serialization/SerializerBase.cs index 53c38eb5..d238b7be 100644 --- a/Ical.Net/Serialization/SerializerBase.cs +++ b/Ical.Net/Serialization/SerializerBase.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.IO; using System.Text; @@ -11,7 +12,7 @@ namespace Ical.Net.Serialization; public abstract class SerializerBase : IStringSerializer { - private SerializationContext _mSerializationContext; + private SerializationContext? _mSerializationContext; protected SerializerBase() { @@ -23,26 +24,23 @@ protected SerializerBase(SerializationContext ctx) _mSerializationContext = ctx; } - public virtual SerializationContext SerializationContext + public virtual SerializationContext? SerializationContext // NOSONAR: auto-property { get => _mSerializationContext; set => _mSerializationContext = value; } public abstract Type TargetType { get; } - public abstract string SerializeToString(object obj); - public abstract object Deserialize(TextReader tr); + public abstract string? SerializeToString(object? obj); + 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; } @@ -53,64 +51,43 @@ 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 encoding on the stack - var encodingStack = GetService(); - encodingStack.Push(encoding); + // Push the current object onto the serialization stack + SerializationContext?.Push(obj); - sw.Write(SerializeToString(obj)); + // Push the current encoding on the stack + var encodingStack = GetService(); + encodingStack?.Push(encoding); - // Pop the current encoding off the serialization stack - encodingStack.Pop(); + sw.Write(SerializeToString(obj)); - // Pop the current object off the serialization stack - SerializationContext.Pop(); - } + // Pop the current encoding off the serialization stack + encodingStack?.Pop(); + + // Pop the current object off the serialization stack + SerializationContext?.Pop(); } - public virtual object GetService(Type serviceType) => SerializationContext?.GetService(serviceType); + public virtual object? GetService(Type serviceType) => SerializationContext?.GetService(serviceType); - public virtual object GetService(string name) => SerializationContext?.GetService(name); + public virtual object? GetService(string name) => SerializationContext?.GetService(name); - public virtual T GetService() - { - if (SerializationContext != null) - { - return SerializationContext.GetService(); - } - return default(T); - } + public virtual T? GetService() + => SerializationContext != null ? SerializationContext.GetService() : default; - public virtual T GetService(string name) - { - if (SerializationContext != null) - { - return SerializationContext.GetService(name); - } - return default(T); - } + public virtual T? GetService(string name) => + SerializationContext != null ? SerializationContext.GetService(name) : default; public void SetService(string name, object obj) - { - SerializationContext?.SetService(name, obj); - } + => SerializationContext?.SetService(name, obj); public void SetService(object obj) - { - SerializationContext?.SetService(obj); - } + => SerializationContext?.SetService(obj); public void RemoveService(Type type) - { - SerializationContext?.RemoveService(type); - } + => SerializationContext?.RemoveService(type); public void RemoveService(string name) - { - SerializationContext?.RemoveService(name); - } -} \ No newline at end of file + => SerializationContext?.RemoveService(name); +} diff --git a/Ical.Net/Serialization/SerializerFactory.cs b/Ical.Net/Serialization/SerializerFactory.cs index 7770177d..93b35e8a 100644 --- a/Ical.Net/Serialization/SerializerFactory.cs +++ b/Ical.Net/Serialization/SerializerFactory.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Reflection; using Ical.Net.CalendarComponents; @@ -29,7 +30,7 @@ public SerializerFactory() /// /// The type of object to be serialized. /// The serialization context. - public virtual ISerializer Build(Type objectType, SerializationContext ctx) + public virtual ISerializer? Build(Type? objectType, SerializationContext ctx) { if (objectType == null) { @@ -81,7 +82,7 @@ public virtual ISerializer Build(Type objectType, SerializationContext ctx) } else if (typeof(ICalendarDataType).IsAssignableFrom(objectType)) { - s = _mDataTypeSerializerFactory.Build(objectType, ctx); + s = _mDataTypeSerializerFactory.Build(objectType, ctx)!; } // Default to a string serializer, which simply calls // ToString() on the value to serialize it. diff --git a/Ical.Net/Serialization/SimpleDeserializer.cs b/Ical.Net/Serialization/SimpleDeserializer.cs index ce6b9ff0..17e4683f 100644 --- a/Ical.Net/Serialization/SimpleDeserializer.cs +++ b/Ical.Net/Serialization/SimpleDeserializer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using System.IO; @@ -83,7 +84,7 @@ public IEnumerable Deserialize(TextReader reader) var contentLine = ParseContentLine(context, contentLineString); if (string.Equals(contentLine.Name, "BEGIN", StringComparison.OrdinalIgnoreCase)) { - stack.Push(current); + stack.Push(current!); // Must push to stack! current = _componentFactory.Build((string) contentLine.Value); SerializationUtil.OnDeserializing(current); } @@ -164,10 +165,10 @@ private static void SetPropertyParameters(CalendarProperty property, CaptureColl private void SetPropertyValue(SerializationContext context, CalendarProperty property, string value) { var type = _dataTypeMapper.GetPropertyMapping(property) ?? typeof(string); - var serializer = (SerializerBase) _serializerFactory.Build(type, context); + var serializer = (SerializerBase?) _serializerFactory.Build(type, context); using var valueReader = new StringReader(value); - var propertyValue = serializer.Deserialize(valueReader); + var propertyValue = serializer?.Deserialize(valueReader); if (propertyValue is IEnumerable propertyValues) { From c5dbc5b37893ada8073385a7063cfb6680449aca Mon Sep 17 00:00:00 2001 From: axunonb Date: Sun, 27 Apr 2025 14:20:00 +0200 Subject: [PATCH 2/2] Implemented changes from review --- .../Serialization/DataTypes/AttachmentSerializer.cs | 5 ++--- Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs | 5 ++--- .../DataTypes/EncodableDataTypeSerializer.cs | 10 +++++----- .../Serialization/DataTypes/UtcOffsetSerializer.cs | 7 +++---- Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs | 4 +++- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs b/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs index ad54d5e4..978b5944 100644 --- a/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs @@ -82,9 +82,8 @@ public AttachmentSerializer(SerializationContext ctx) : base(ctx) { } // The default VALUE type for attachments is URI. // So, let's grab the URI by default. - var uriValue = Decode(a, attachment); - - if (string.IsNullOrEmpty(uriValue)) return null; + // Note: Returned value can't be null here when args are not null. + var uriValue = Decode(a, attachment)!; a.Uri = new Uri(uriValue, UriKind.RelativeOrAbsolute); diff --git a/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs b/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs index e9fc266c..891c87b0 100644 --- a/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs @@ -32,9 +32,8 @@ public AttendeeSerializer(SerializationContext ctx) : base(ctx) { } { if (CreateAndAssociate() is not Attendee a) return null; - var uriString = Unescape(Decode(a, attendee)); - - if (uriString == null) return a; + // Note: Returned value can't be null here when args are not null. + var uriString = Unescape(Decode(a, attendee))!; // Prepend "mailto:" if necessary if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) diff --git a/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs b/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs index 7480fbf7..5915e97a 100644 --- a/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs @@ -44,7 +44,7 @@ protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) { } { // Default to the current encoding var encodingStack = GetService(); - return encodingStack?.Current?.GetString(data); + return encodingStack?.Current.GetString(data); } var encodingProvider = GetService(); @@ -53,7 +53,7 @@ protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) { } protected string? Decode(IEncodableDataType dt, string value) { - if (dt?.Encoding == null) + if (dt.Encoding == null) { return value; } @@ -66,7 +66,7 @@ protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) { } // Default to the current encoding var encodingStack = GetService(); - return encodingStack?.Current?.GetString(data); + return encodingStack?.Current.GetString(data); } protected byte[]? DecodeData(IEncodableDataType dt, string? value) @@ -76,11 +76,11 @@ protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) { } return null; } - if (dt?.Encoding == null) + if (dt.Encoding == null) { // Default to the current encoding var encodingStack = GetService(); - return encodingStack?.Current?.GetBytes(value); + return encodingStack?.Current.GetBytes(value); } var encodingProvider = GetService(); diff --git a/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs b/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs index 9c8fa40d..c32a8217 100644 --- a/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs @@ -44,16 +44,15 @@ public UtcOffsetSerializer(SerializationContext ctx) : base(ctx) { } } } + private static readonly string[] _supportedFormats = ["hhmmss", "hhmm", "hh"]; + public static TimeSpan GetOffset(string rawOffset) { // Determine if the offset is negative var isNegative = rawOffset.StartsWith("-"); rawOffset = rawOffset.TrimStart('+', '-'); - // Supported formats - var formats = new[] { "hhmmss", "hhmm", "hh" }; - - if (TimeSpan.TryParseExact(rawOffset, formats, CultureInfo.InvariantCulture, out var ts)) + if (TimeSpan.TryParseExact(rawOffset, _supportedFormats, CultureInfo.InvariantCulture, out var ts)) { return isNegative ? -ts : ts; } diff --git a/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs b/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs index 83a67197..345eb18d 100644 --- a/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs +++ b/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs @@ -35,7 +35,9 @@ public WeekDaySerializer(SerializationContext ctx) : base(ctx) { } try { var name = Enum.GetName(typeof(DayOfWeek), ds.DayOfWeek); - value += name!.ToUpper().Substring(0, 2); + if (name == null) return null; + + value += name.ToUpper().Substring(0, 2); } catch {