diff --git a/VisualCard.Calendar/CalendarTools.cs b/VisualCard.Calendar/CalendarTools.cs index 01caefe..afd3ce5 100644 --- a/VisualCard.Calendar/CalendarTools.cs +++ b/VisualCard.Calendar/CalendarTools.cs @@ -79,11 +79,13 @@ public static Parts.Calendar[] GetCalendars(StreamReader stream) // Parse the lines of the calendar file string CalendarLine; - StringBuilder CalendarContent = new(); + List<(int, string)> lines = []; Version CalendarVersion = new(); + int lineNumber = 0; while (!stream.EndOfStream) { bool append = false; + lineNumber++; // Skip empty lines CalendarLine = stream.ReadLine(); @@ -97,7 +99,7 @@ public static Parts.Calendar[] GetCalendars(StreamReader stream) !CalendarLine.EqualsNoCase(VCalendarConstants._endText)) append = true; if (append) - CalendarContent.Append(CalendarLine); + lines.Add((lineNumber, CalendarLine)); // All vCalendars must begin with BEGIN:VCALENDAR if (!CalendarLine.EqualsNoCase(VCalendarConstants._beginText) && !BeginSpotted) @@ -130,17 +132,13 @@ public static Parts.Calendar[] GetCalendars(StreamReader stream) EndSpotted = true; // Make a new parser instance - string content = CalendarContent.ToString(); - string[] contentLines = content.SplitNewLines(); - VCalendarParser CalendarParser = new(contentLines, CalendarVersion); + VCalendarParser CalendarParser = new([.. lines], CalendarVersion); FinalParsers.Add(CalendarParser); // Clear the content in case we want to make a second contact - CalendarContent.Clear(); + lines.Clear(); BeginSpotted = false; } - else if (append) - CalendarContent.AppendLine(); } // Close the stream to avoid stuck file handle diff --git a/VisualCard.Calendar/Parsers/VCalendarParser.cs b/VisualCard.Calendar/Parsers/VCalendarParser.cs index fa0f1f1..92cb8e3 100644 --- a/VisualCard.Calendar/Parsers/VCalendarParser.cs +++ b/VisualCard.Calendar/Parsers/VCalendarParser.cs @@ -40,12 +40,12 @@ namespace VisualCard.Calendar.Parsers internal class VCalendarParser { private readonly Version calendarVersion = new(); - private readonly string[] calendarContent = []; + private readonly (int, string)[] calendarContent = []; /// /// vCalendar calendar content /// - public string[] CalendarContent => + public (int, string)[] CalendarContent => calendarContent; /// /// vCalendar calendar version @@ -73,8 +73,9 @@ public Parts.Calendar Parse() for (int i = 0; i < CalendarContent.Length; i++) { // Get line - string _value = CalendarContent[i]; - int lineNumber = i + 1; + var content = CalendarContent[i]; + string _value = content.Item2; + int lineNumber = content.Item1; if (string.IsNullOrEmpty(_value)) continue; @@ -84,7 +85,7 @@ public Parts.Calendar Parse() subPart = begins[begins.Count - 1].Item2; // First, check to see if we need to construct blocks - string secondLine = i + 1 < CalendarContent.Length ? CalendarContent[i + 1] : ""; + string secondLine = i + 1 < CalendarContent.Length ? CalendarContent[i + 1].Item2 : ""; bool firstConstructedLine = !_value.StartsWith(VcardConstants._spaceBreak) && !_value.StartsWith(VcardConstants._tabBreak); constructing = secondLine.StartsWithAnyOf([VcardConstants._spaceBreak, VcardConstants._tabBreak]); secondLine = secondLine.Length > 1 ? secondLine.Substring(1) : ""; @@ -540,7 +541,7 @@ private void SaveLastSubPart(Parts.Calendar? subpart, ref Parts.Calendar part) throw new ArgumentException($"Can't place {subpart.GetType().Name} inside {part.GetType().Name}"); } - internal VCalendarParser(string[] calendarContent, Version calendarVersion) + internal VCalendarParser((int, string)[] calendarContent, Version calendarVersion) { this.calendarContent = calendarContent; this.calendarVersion = calendarVersion; diff --git a/VisualCard/CardTools.cs b/VisualCard/CardTools.cs index 2117df9..b2a7d36 100644 --- a/VisualCard/CardTools.cs +++ b/VisualCard/CardTools.cs @@ -79,14 +79,16 @@ public static Card[] GetCards(StreamReader stream) // Parse the lines of the card file string CardLine; - StringBuilder CardContent = new(); Version CardVersion = new(); List nestedCards = []; + List<(int, string)> lines = []; bool nested = false; bool versionDirect = false; + int lineNumber = 0; while (!stream.EndOfStream) { bool append = false; + lineNumber++; // Skip empty lines CardLine = stream.ReadLine(); @@ -125,7 +127,7 @@ public static Card[] GetCards(StreamReader stream) continue; } if (append) - CardContent.Append(CardLine); + lines.Add((lineNumber, CardLine)); // All VCards must begin with BEGIN:VCARD if (!CardLine.EqualsNoCase(VcardConstants._beginText) && !BeginSpotted) @@ -170,22 +172,18 @@ public static Card[] GetCards(StreamReader stream) nested = false; // Make a new parser instance - string content = CardContent.ToString(); - string[] contentLines = content.SplitNewLines(); - VcardParser CardParser = new(contentLines, CardVersion) + VcardParser CardParser = new([.. lines], CardVersion) { nestedCards = [.. nestedCards] }; FinalParsers.Add(CardParser); // Clear the content in case we want to make a second contact - CardContent.Clear(); + lines.Clear(); nestedCards.Clear(); BeginSpotted = false; versionDirect = false; } - else if (append) - CardContent.AppendLine(); } // Close the stream to avoid stuck file handle diff --git a/VisualCard/Parsers/VcardParser.cs b/VisualCard/Parsers/VcardParser.cs index 6e70a26..15fb85b 100644 --- a/VisualCard/Parsers/VcardParser.cs +++ b/VisualCard/Parsers/VcardParser.cs @@ -40,12 +40,12 @@ internal class VcardParser { internal Card[] nestedCards = []; private readonly Version cardVersion = new(); - private string[] cardContent = []; + private (int, string)[] cardContent = []; /// /// VCard card content /// - public string[] CardContent => + public (int, string)[] CardContent => cardContent; /// /// VCard card version @@ -69,9 +69,9 @@ public Card Parse() // Move kind to the top if (CardVersion.Major >= 4) { - string kindLine = CardContent.SingleOrDefault((line) => line.ToUpper().StartsWith(VcardConstants._kindSpecifier)); - if (!string.IsNullOrEmpty(kindLine)) - cardContent = [kindLine, .. cardContent.Where((line) => line != kindLine).ToArray()]; + var kindLine = CardContent.SingleOrDefault((line) => line.Item2.ToUpper().StartsWith(VcardConstants._kindSpecifier)); + if (!string.IsNullOrEmpty(kindLine.Item2)) + cardContent = [kindLine, .. cardContent.Where((line) => line.Item2 != kindLine.Item2).ToArray()]; } // Iterate through all the lines @@ -82,13 +82,14 @@ public Card Parse() for (int i = 0; i < CardContent.Length; i++) { // Get line - string _value = CardContent[i]; - int lineNumber = i + 1; + var content = CardContent[i]; + string _value = content.Item2; + int lineNumber = content.Item1; if (string.IsNullOrEmpty(_value)) continue; // First, check to see if we need to construct blocks - string secondLine = i + 1 < CardContent.Length ? CardContent[i + 1] : ""; + string secondLine = i + 1 < CardContent.Length ? CardContent[i + 1].Item2 : ""; bool firstConstructedLine = !_value.StartsWith(VcardConstants._spaceBreak) && !_value.StartsWith(VcardConstants._tabBreak); constructing = secondLine.StartsWithAnyOf([VcardConstants._spaceBreak, VcardConstants._tabBreak]); secondLine = secondLine.Length > 1 ? secondLine.Substring(1) : ""; @@ -321,7 +322,7 @@ private bool ValidateComponent(ref string[] expectedFields, out stri return actualFields.SequenceEqual(expectedFields); } - internal VcardParser(string[] cardContent, Version cardVersion) + internal VcardParser((int, string)[] cardContent, Version cardVersion) { this.cardContent = cardContent; this.cardVersion = cardVersion;