From c3dae4c147dba304b10ef0a69b59a69f0b5c359d Mon Sep 17 00:00:00 2001 From: Aptivi CEO Date: Sat, 12 Aug 2023 11:55:27 +0300 Subject: [PATCH] imp - Consolidated type-based info parsers We've consolidated type-based info parsers --- We've made a substantial change to type-based info parsers by determining the types from the value and parsing them more efficiently. We've cut down the constants so that there are no repeat types with a difference in the delimiter. --- Type: imp Breaking: False Doc Required: False Part: 1/1 --- VisualCard.Tests/ContactData.cs | 13 +- VisualCard/Converters/MeCard.cs | 38 ---- VisualCard/Parsers/Four/VcardFour.cs | 281 ++++++++++++++----------- VisualCard/Parsers/Three/VcardThree.cs | 216 ++++++++++++------- VisualCard/Parsers/Two/VcardTwo.cs | 190 +++++++++++------ VisualCard/Parsers/VcardConstants.cs | 67 +++--- VisualCard/Parsers/VcardParserTools.cs | 22 ++ VisualCard/Parts/AddressInfo.cs | 84 +++++++- VisualCard/Parts/EmailInfo.cs | 84 +++++++- VisualCard/Parts/GeoInfo.cs | 18 +- VisualCard/Parts/ImppInfo.cs | 29 +-- VisualCard/Parts/LogoInfo.cs | 18 +- VisualCard/Parts/NameInfo.cs | 18 +- VisualCard/Parts/NicknameInfo.cs | 12 +- VisualCard/Parts/OrganizationInfo.cs | 18 +- VisualCard/Parts/PhotoInfo.cs | 18 +- VisualCard/Parts/RoleInfo.cs | 14 +- VisualCard/Parts/SoundInfo.cs | 18 +- VisualCard/Parts/TelephoneInfo.cs | 18 +- VisualCard/Parts/TimeZoneInfo.cs | 18 +- VisualCard/Parts/TitleInfo.cs | 14 +- 21 files changed, 741 insertions(+), 467 deletions(-) diff --git a/VisualCard.Tests/ContactData.cs b/VisualCard.Tests/ContactData.cs index 31fd2b0..446f6b0 100644 --- a/VisualCard.Tests/ContactData.cs +++ b/VisualCard.Tests/ContactData.cs @@ -42,7 +42,7 @@ public static class ContactData BEGIN:VCARD VERSION:3.0 FN:Rick Hood - N:Hood;Rick;;;; + N:Hood;Rick;;; END:VCARD """ @@ -147,7 +147,7 @@ public static class ContactData BEGIN:VCARD VERSION:3.0 FN:John Sanders - N:Sanders;John;;;; + N:Sanders;John;;; TEL;TYPE=CELL:495-522-3560 ADR;TYPE=HOME:;;Los Angeles;;;;USA EMAIL;TYPE=HOME:john.s@acme.co @@ -720,6 +720,7 @@ public static class ContactData TEL;TYPE=cell:589-210-1059 TITLE:Chief Executive Officer URL:https://sso.org/ + BDAY:19890222 X-SIP-SIP:sip test END:VCARD @@ -789,7 +790,7 @@ public static class ContactData new ImppInfo(0, Array.Empty(), "aim:IM", new string[] { "HOME" }), new ImppInfo(0, Array.Empty(), "msn:Windows LIVE", new string[] { "HOME" }), new ImppInfo(0, Array.Empty(), "ymsgr:Yahoo", new string[] { "HOME" }) - } + }, }; private static readonly Card multipleVcardFourContactsInstanceThree = new ( @@ -827,7 +828,8 @@ public static class ContactData ContactXNames = new XNameInfo[] { new XNameInfo(0, Array.Empty(), "SIP-SIP", new string[] { "sip test" }, Array.Empty()), - } + }, + ContactBirthdate = new DateTime(1989, 2, 22), }; private static readonly Card multipleVcardFourContactsInstanceFour = singleVcardFourContactInstance; #endregion @@ -891,7 +893,8 @@ public static class ContactData ContactMails = new EmailInfo[] { new EmailInfo(0, Array.Empty(), new string[] { "INTERNET" }, "deriks@Microsoft.com") - } + }, + ContactBirthdate = new DateTime(1963, 9, 21), }; private static readonly Card vcardThreeOldSampleInstanceTwo = new ( diff --git a/VisualCard/Converters/MeCard.cs b/VisualCard/Converters/MeCard.cs index 8a9a5ba..6381a85 100644 --- a/VisualCard/Converters/MeCard.cs +++ b/VisualCard/Converters/MeCard.cs @@ -83,31 +83,6 @@ public static List GetContactsFromMeCardString(string meCardStr var nameSplits = value.Substring(2).Split(','); fullName = $"{nameSplits[1]} {nameSplits[0]}"; } - - // As VisualCard doesn't support ADR: yet, why don't we just make it ADR; until we improve type detecion? - if (value.StartsWith("ADR:")) - values[i] = $"ADR;TYPE=HOME{values[i].Substring(3)}"; - - // As VisualCard doesn't support EMAIL: yet, why don't we just make it EMAIL; until we improve type detecion? - if (value.StartsWith("EMAIL:")) - values[i] = $"EMAIL;TYPE=HOME{values[i].Substring(5)}"; - - // MeCard stores birthday date in this format: - // 19700310 - // Digit 1-4: Year - // Digit 5-6: Month - // Digit 7-8: Day - // However, .NET's DateTime doesn't support it, so we verify the number, split it, and make an appropriate string for this. - if (value.StartsWith("BDAY:")) - { - int birthNum = int.Parse(value.Substring(5)); - var birthDigits = GetDigits(birthNum).ToList(); - int birthYear = (birthDigits[0] * 1000) + (birthDigits[1] * 100) + (birthDigits[2] * 10) + birthDigits[3]; - int birthMonth = (birthDigits[4] * 10) + birthDigits[5]; - int birthDay = (birthDigits[6] * 10) + birthDigits[7]; - var birthDate = new DateTime(birthYear, birthMonth, birthDay); - values[i] = $"BDAY:{birthDate:g}"; - } } // Install the values! @@ -132,18 +107,5 @@ public static List GetContactsFromMeCardString(string meCardStr return cardParsers; } - private static IEnumerable GetDigits(int num) - { - int individualFactor = 0; - int tennerFactor = Convert.ToInt32(Math.Pow(10, num.ToString().Length)); - while (tennerFactor > 1) - { - num -= tennerFactor * individualFactor; - tennerFactor /= 10; - individualFactor = num / tennerFactor; - yield return individualFactor; - } - } - } } diff --git a/VisualCard/Parsers/Four/VcardFour.cs b/VisualCard/Parsers/Four/VcardFour.cs index 69baabd..1560440 100644 --- a/VisualCard/Parsers/Four/VcardFour.cs +++ b/VisualCard/Parsers/Four/VcardFour.cs @@ -104,6 +104,13 @@ public override Card Parse() string _value = CardContentReader.ReadLine(); lineNumber += 1; + // Check for type + bool isWithType = false; + var valueSplit = VcardParserTools.SplitToKeyAndValueFromString(_value); + if (valueSplit[0].Contains(";")) + isWithType = true; + var delimiter = isWithType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter; + try { // Variables @@ -139,44 +146,36 @@ public override Card Parse() // Card type (KIND:individual, KIND:group, KIND:org, KIND:location, ...) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._kindSpecifier)) + if (_value.StartsWith(VcardConstants._kindSpecifier + delimiter)) { // Get the value - string kindValue = _value.Substring(VcardConstants._kindSpecifier.Length); + string kindValue = _value.Substring(VcardConstants._kindSpecifier.Length + 1); // Populate field if (!string.IsNullOrEmpty(kindValue)) _kind = Regex.Unescape(kindValue); } - // The name (N:Sanders;John;;;) + // The name (N:Sanders;John;;; or N;ALTID=1;LANGUAGE=en:Sanders;John;;;) // ALTID is supported. - if (_value.StartsWith(VcardConstants._nameSpecifier)) + if (_value.StartsWith(VcardConstants._nameSpecifier + delimiter)) { // Get the name - _names.Add(NameInfo.FromStringVcardFour(splitValues, idReservedForName)); + if (isWithType) + _names.Add(NameInfo.FromStringVcardFourWithType(_value, splitArgs, finalArgs, altId, _names, idReservedForName)); + else + _names.Add(NameInfo.FromStringVcardFour(splitValues, idReservedForName)); // Since we've reserved id 0, set the flag idReservedForName = true; } - // The name (N;ALTID=1;LANGUAGE=en:Sanders;John;;;) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._nameSpecifierWithType)) - { - // Get the name - _names.Add(NameInfo.FromStringVcardFourWithType(_value, splitArgs, finalArgs, altId, _names, idReservedForName)); - - // Since we've reserved a specific id, set the flag - idReservedForName = true; - } - // Full name (FN:John Sanders) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._fullNameSpecifier)) + if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) { // Get the value - string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length); + string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); // Populate field _fullName = Regex.Unescape(fullNameValue); @@ -185,52 +184,61 @@ public override Card Parse() fullNameSpecifierSpotted = true; } - // Telephone (TEL;TYPE=cell,home:495-522-3560) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._telephoneSpecifierWithType)) - _telephones.Add(TelephoneInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); - - // Telephone (TEL:495-522-3560) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._telephoneSpecifier)) - _telephones.Add(TelephoneInfo.FromStringVcardFour(_value, altId)); + // Telephone (TEL;CELL;TYPE=HOME:495-522-3560 or TEL;TYPE=cell,home:495-522-3560 or TEL:495-522-3560) + // Type is supported + if (_value.StartsWith(VcardConstants._telephoneSpecifier + delimiter)) + { + if (isWithType) + _telephones.Add(TelephoneInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _telephones.Add(TelephoneInfo.FromStringVcardFour(_value, altId)); + } - // Address (ADR;TYPE=HOME:;;Los Angeles, USA;;;;) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._addressSpecifierWithType)) - _addresses.Add(AddressInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + // Address (ADR;TYPE=HOME:;;Los Angeles, USA;;;; or ADR:;;Los Angeles, USA;;;;) + if (_value.StartsWith(VcardConstants._addressSpecifier + delimiter)) + { + if (isWithType) + _addresses.Add(AddressInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _addresses.Add(AddressInfo.FromStringVcardFour(_value, altId)); + } // Email (EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) // ALTID is supported. - if (_value.StartsWith(VcardConstants._emailSpecifier)) - _emails.Add(EmailInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + if (_value.StartsWith(VcardConstants._emailSpecifier + delimiter)) + { + if (isWithType) + _emails.Add(EmailInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _emails.Add(EmailInfo.FromStringVcardFour(_value, altId)); + } // Organization (ORG:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) // ALTID is supported. - if (_value.StartsWith(VcardConstants._orgSpecifier)) - _orgs.Add(OrganizationInfo.FromStringVcardFour(_value, altId)); - - // Organization (ORG;TYPE=WORK:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._orgSpecifierWithType)) - _orgs.Add(OrganizationInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + if (_value.StartsWith(VcardConstants._orgSpecifier + delimiter)) + { + if (isWithType) + _orgs.Add(OrganizationInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _orgs.Add(OrganizationInfo.FromStringVcardFour(_value, altId)); + } // Title (TITLE:Product Manager) // ALTID is supported. - if (_value.StartsWith(VcardConstants._titleSpecifier)) - _titles.Add(TitleInfo.FromStringVcardFour(_value, altId)); - - // Title (TITLE;ALTID=1;LANGUAGE=fr:Patron or TITLE;LANGUAGE=fr:Patron) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._titleSpecifierWithArguments)) - _titles.Add(TitleInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + if (_value.StartsWith(VcardConstants._titleSpecifier + delimiter)) + { + if (isWithType) + _titles.Add(TitleInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _titles.Add(TitleInfo.FromStringVcardFour(_value, altId)); + } // Website link (URL:https://sso.org/) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._urlSpecifier)) + if (_value.StartsWith(VcardConstants._urlSpecifier + delimiter)) { // Get the value - string urlValue = _value.Substring(VcardConstants._urlSpecifier.Length); + string urlValue = _value.Substring(VcardConstants._urlSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(urlValue, UriKind.Absolute, out Uri uri)) @@ -242,10 +250,10 @@ public override Card Parse() // Note (NOTE:Product Manager) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._noteSpecifier)) + if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) { // Get the value - string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length); + string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length + 1); // Populate field _note = Regex.Unescape(noteValue); @@ -253,25 +261,40 @@ public override Card Parse() // Photo (PHOTO;ENCODING=BASE64;JPEG:... or PHOTO;VALUE=URL:file:///jqpublic.gif or PHOTO;ENCODING=BASE64;TYPE=GIF:...) // ALTID is supported. - if (_value.StartsWith(VcardConstants._photoSpecifierWithType)) - _photos.Add(PhotoInfo.FromStringVcardFourWithType(_value, finalArgs, altId, CardContentReader)); + if (_value.StartsWith(VcardConstants._photoSpecifier + delimiter)) + { + if (isWithType) + _photos.Add(PhotoInfo.FromStringVcardFourWithType(_value, finalArgs, altId, CardContentReader)); + else + throw new InvalidDataException("Photo field must not have empty type."); + } // Logo (LOGO;ENCODING=BASE64;JPEG:... or LOGO;VALUE=URL:file:///jqpublic.gif or LOGO;ENCODING=BASE64;TYPE=GIF:...) // ALTID is supported. - if (_value.StartsWith(VcardConstants._logoSpecifierWithType)) - _logos.Add(LogoInfo.FromStringVcardFourWithType(_value, finalArgs, altId, CardContentReader)); + if (_value.StartsWith(VcardConstants._logoSpecifier + delimiter)) + { + if (isWithType) + _logos.Add(LogoInfo.FromStringVcardFourWithType(_value, finalArgs, altId, CardContentReader)); + else + throw new InvalidDataException("Photo field must not have empty type."); + } // Sound (SOUND;VALUE=URL:file///multimed/audio/jqpublic.wav or SOUND;WAVE;BASE64:... or SOUND;TYPE=WAVE;ENCODING=BASE64:...) // ALTID is supported. - if (_value.StartsWith(VcardConstants._soundSpecifierWithType)) - _sounds.Add(SoundInfo.FromStringVcardFourWithType(_value, finalArgs, altId, CardContentReader)); + if (_value.StartsWith(VcardConstants._soundSpecifier + delimiter)) + { + if (isWithType) + _sounds.Add(SoundInfo.FromStringVcardFourWithType(_value, finalArgs, altId, CardContentReader)); + else + throw new InvalidDataException("Photo field must not have empty type."); + } // Revision (REV:1995-10-31T22:27:10Z or REV:19951031T222710) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._revSpecifier)) + if (_value.StartsWith(VcardConstants._revSpecifier + delimiter)) { // Get the value - string revValue = _value.Substring(VcardConstants._revSpecifier.Length); + string revValue = _value.Substring(VcardConstants._revSpecifier.Length + 1); // Populate field _rev = DateTime.Parse(revValue); @@ -279,41 +302,55 @@ public override Card Parse() // Nickname (NICKNAME;TYPE=cell,home:495-522-3560) // ALTID is supported. - if (_value.StartsWith(VcardConstants._nicknameSpecifierWithType)) - _nicks.Add(NicknameInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); - - // Nickname (NICKNAME:495-522-3560) - // ALTID is supported. See above. - if (_value.StartsWith(VcardConstants._nicknameSpecifier)) - _nicks.Add(NicknameInfo.FromStringVcardFour(_value, altId)); + if (_value.StartsWith(VcardConstants._nicknameSpecifier + delimiter)) + { + if (isWithType) + _nicks.Add(NicknameInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _nicks.Add(NicknameInfo.FromStringVcardFour(_value, altId)); + } // Birthdate (BDAY:19950415 or BDAY:1953-10-15T23:10:00Z) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._birthSpecifier)) + if (_value.StartsWith(VcardConstants._birthSpecifier + delimiter)) { // Get the value - string bdayValue = _value.Substring(VcardConstants._birthSpecifier.Length); + string bdayValue = ""; + if (isWithType) + bdayValue = _value.Substring(_value.IndexOf(VcardConstants._argumentDelimiter) + 1); + else + bdayValue = _value.Substring(VcardConstants._birthSpecifier.Length + 1); // Populate field - _bday = DateTime.Parse(bdayValue); + if (int.TryParse(bdayValue, out int bdayDigits) && bdayValue.Length == 8) + { + int birthNum = int.Parse(bdayValue); + var birthDigits = VcardParserTools.GetDigits(birthNum).ToList(); + int birthYear = (birthDigits[0] * 1000) + (birthDigits[1] * 100) + (birthDigits[2] * 10) + birthDigits[3]; + int birthMonth = (birthDigits[4] * 10) + birthDigits[5]; + int birthDay = (birthDigits[6] * 10) + birthDigits[7]; + _bday = new DateTime(birthYear, birthMonth, birthDay); + } + else + _bday = DateTime.Parse(bdayValue); } // Role (ROLE:Programmer) // ALTID is supported. - if (_value.StartsWith(VcardConstants._roleSpecifier)) - _roles.Add(RoleInfo.FromStringVcardFour(_value, altId)); - - // Role (ROLE;ALTID=1;LANGUAGE=en:Programmer) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._roleSpecifierWithType)) - _roles.Add(RoleInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + if (_value.StartsWith(VcardConstants._roleSpecifier + delimiter)) + { + if (isWithType) + _roles.Add(RoleInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _roles.Add(RoleInfo.FromStringVcardFour(_value, altId)); + } // Categories (CATEGORIES:INTERNET or CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._categoriesSpecifier)) + if (_value.StartsWith(VcardConstants._categoriesSpecifier + delimiter)) { // Get the value - string categoriesValue = _value.Substring(VcardConstants._categoriesSpecifier.Length); + string categoriesValue = _value.Substring(VcardConstants._categoriesSpecifier.Length + 1); // Populate field _categories.AddRange(Regex.Unescape(categoriesValue).Split(',')); @@ -321,10 +358,10 @@ public override Card Parse() // Product ID (PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._productIdSpecifier)) + if (_value.StartsWith(VcardConstants._productIdSpecifier + delimiter)) { // Get the value - string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length); + string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length + 1); // Populate field _prodId = Regex.Unescape(prodIdValue); @@ -332,10 +369,10 @@ public override Card Parse() // Sort string (SORT-STRING:Harten) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._sortStringSpecifier)) + if (_value.StartsWith(VcardConstants._sortStringSpecifier + delimiter)) { // Get the value - string sortStringValue = _value.Substring(VcardConstants._sortStringSpecifier.Length); + string sortStringValue = _value.Substring(VcardConstants._sortStringSpecifier.Length + 1); // Populate field _sortString = Regex.Unescape(sortStringValue); @@ -343,40 +380,40 @@ public override Card Parse() // Time Zone (TZ;VALUE=text:-05:00; EST; Raleigh/North America) // ALTID is supported. - if (_value.StartsWith(VcardConstants._timeZoneSpecifierWithType)) - _timezones.Add(TimeZoneInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); - - // Time Zone (TZ:-05:00) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._timeZoneSpecifier)) - _timezones.Add(TimeZoneInfo.FromStringVcardFour(_value, altId)); + if (_value.StartsWith(VcardConstants._timeZoneSpecifier + delimiter)) + { + if (isWithType) + _timezones.Add(TimeZoneInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _timezones.Add(TimeZoneInfo.FromStringVcardFour(_value, altId)); + } // Geo (GEO;VALUE=uri:https://...) // ALTID is supported. - if (_value.StartsWith(VcardConstants._geoSpecifierWithType)) - _geos.Add(GeoInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); - - // Geo (GEO:geo:37.386013,-122.082932) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._geoSpecifier)) - _geos.Add(GeoInfo.FromStringVcardFour(_value, altId)); + if (_value.StartsWith(VcardConstants._geoSpecifier + delimiter)) + { + if (isWithType) + _geos.Add(GeoInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _geos.Add(GeoInfo.FromStringVcardFour(_value, altId)); + } // IMPP information (IMPP;TYPE=home:sip:test) // ALTID is supported. - if (_value.StartsWith(VcardConstants._imppSpecifierWithType)) - _impps.Add(ImppInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); - - // IMPP information (IMPP:sip:test) - // ALTID is supported. See above. - if (_value.StartsWith(VcardConstants._imppSpecifier)) - _impps.Add(ImppInfo.FromStringVcardFour(_value, altId)); + if (_value.StartsWith(VcardConstants._imppSpecifier + delimiter)) + { + if (isWithType) + _impps.Add(ImppInfo.FromStringVcardFourWithType(_value, finalArgs, altId)); + else + _impps.Add(ImppInfo.FromStringVcardFour(_value, altId)); + } // Source (SOURCE:http://johndoe.com/vcard.vcf) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._sourceSpecifier)) + if (_value.StartsWith(VcardConstants._sourceSpecifier + delimiter)) { // Get the value - string sourceStringValue = _value.Substring(VcardConstants._sourceSpecifier.Length); + string sourceStringValue = _value.Substring(VcardConstants._sourceSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(sourceStringValue, UriKind.Absolute, out Uri uri)) @@ -388,10 +425,10 @@ public override Card Parse() // XML code (XML:Not an xCard XML element) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._xmlSpecifier)) + if (_value.StartsWith(VcardConstants._xmlSpecifier + delimiter)) { // Get the value - string xmlStringValue = _value.Substring(VcardConstants._xmlSpecifier.Length); + string xmlStringValue = _value.Substring(VcardConstants._xmlSpecifier.Length + 1); // Populate field _xml = Regex.Unescape(xmlStringValue); @@ -399,10 +436,10 @@ public override Card Parse() // Free/busy URL (FBURL:http://example.com/fb/jdoe) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._fbUrlSpecifier)) + if (_value.StartsWith(VcardConstants._fbUrlSpecifier + delimiter)) { // Get the value - string fbUrlStringValue = _value.Substring(VcardConstants._fbUrlSpecifier.Length); + string fbUrlStringValue = _value.Substring(VcardConstants._fbUrlSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(fbUrlStringValue, UriKind.Absolute, out Uri uri)) @@ -414,10 +451,10 @@ public override Card Parse() // Calendar URL (CALURI:http://example.com/calendar/jdoe) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._calUriSpecifier)) + if (_value.StartsWith(VcardConstants._calUriSpecifier + delimiter)) { // Get the value - string calUriStringValue = _value.Substring(VcardConstants._calUriSpecifier.Length); + string calUriStringValue = _value.Substring(VcardConstants._calUriSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(calUriStringValue, UriKind.Absolute, out Uri uri)) @@ -429,10 +466,10 @@ public override Card Parse() // Calendar Request URL (CALADRURI:http://example.com/calendar/jdoe) // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._caladrUriSpecifier)) + if (_value.StartsWith(VcardConstants._caladrUriSpecifier + delimiter)) { // Get the value - string caladrUriStringValue = _value.Substring(VcardConstants._caladrUriSpecifier.Length); + string caladrUriStringValue = _value.Substring(VcardConstants._caladrUriSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(caladrUriStringValue, UriKind.Absolute, out Uri uri)) @@ -508,11 +545,11 @@ internal override string SaveToString(Card card) // First, write the header cardBuilder.AppendLine("BEGIN:VCARD"); cardBuilder.AppendLine($"VERSION:{CardVersion}"); - cardBuilder.AppendLine($"{VcardConstants._kindSpecifier}{card.CardKind}"); + cardBuilder.AppendLine($"{VcardConstants._kindSpecifier}:{card.CardKind}"); // Then, write the full name and the name if (!string.IsNullOrWhiteSpace(card.ContactFullName)) - cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}{card.ContactFullName}"); + cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}:{card.ContactFullName}"); foreach (NameInfo name in card.ContactNames) cardBuilder.AppendLine(name.ToStringVcardFour()); @@ -528,9 +565,9 @@ internal override string SaveToString(Card card) foreach (TitleInfo title in card.ContactTitles) cardBuilder.AppendLine(title.ToStringVcardFour()); if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}{card.ContactURL}"); + cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}{card.ContactNotes}"); + cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); foreach (PhotoInfo photo in card.ContactPhotos) cardBuilder.AppendLine(photo.ToStringVcardFour()); foreach (LogoInfo logo in card.ContactLogos) @@ -538,19 +575,19 @@ internal override string SaveToString(Card card) foreach (SoundInfo sound in card.ContactSounds) cardBuilder.AppendLine(sound.ToStringVcardFour()); if (card.CardRevision is not null && card.CardRevision != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._revSpecifier}{card.CardRevision:dd-MM-yyyy_HH-mm-ss}"); + cardBuilder.AppendLine($"{VcardConstants._revSpecifier}:{card.CardRevision:yyyy-MM-dd HH:mm:ss}"); foreach (NicknameInfo nickname in card.ContactNicknames) cardBuilder.AppendLine(nickname.ToStringVcardFour()); if (card.ContactBirthdate is not null && card.ContactBirthdate != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}{card.ContactBirthdate:dd-MM-yyyy}"); + cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}:{card.ContactBirthdate:yyyy-MM-dd}"); foreach (RoleInfo role in card.ContactRoles) cardBuilder.AppendLine(role.ToStringVcardFour()); if (card.ContactCategories is not null && card.ContactCategories.Length > 0) - cardBuilder.AppendLine($"{VcardConstants._categoriesSpecifier}{string.Join(",", card.ContactCategories)}"); + cardBuilder.AppendLine($"{VcardConstants._categoriesSpecifier}:{string.Join(",", card.ContactCategories)}"); if (!string.IsNullOrWhiteSpace(card.ContactProdId)) - cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}{card.ContactProdId}"); + cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}:{card.ContactProdId}"); if (!string.IsNullOrWhiteSpace(card.ContactSortString)) - cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}{card.ContactSortString}"); + cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}:{card.ContactSortString}"); foreach (TimeZoneInfo timeZone in card.ContactTimeZone) cardBuilder.AppendLine(timeZone.ToStringVcardFour()); foreach (GeoInfo geo in card.ContactGeo) diff --git a/VisualCard/Parsers/Three/VcardThree.cs b/VisualCard/Parsers/Three/VcardThree.cs index c818faa..de3ec78 100644 --- a/VisualCard/Parsers/Three/VcardThree.cs +++ b/VisualCard/Parsers/Three/VcardThree.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Mail; using System.Text; using System.Text.RegularExpressions; @@ -97,10 +98,17 @@ public override Card Parse() string _value = CardContentReader.ReadLine(); lineNumber += 1; + // Check for type + bool isWithType = false; + var valueSplit = VcardParserTools.SplitToKeyAndValueFromString(_value); + if (valueSplit[0].Contains(";")) + isWithType = true; + var delimiter = isWithType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter; + try { // The name (N:Sanders;John;;;) - if (_value.StartsWith(VcardConstants._nameSpecifier)) + if (_value.StartsWith(VcardConstants._nameSpecifier + delimiter)) { // Get the name _names.Add(NameInfo.FromStringVcardThree(_value)); @@ -110,10 +118,10 @@ public override Card Parse() } // Full name (FN:John Sanders) - if (_value.StartsWith(VcardConstants._fullNameSpecifier)) + if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) { // Get the value - string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length); + string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); // Populate field _fullName = Regex.Unescape(fullNameValue); @@ -122,39 +130,52 @@ public override Card Parse() fullNameSpecifierSpotted = true; } - // Telephone (TEL;TYPE=cell,home:495-522-3560) - if (_value.StartsWith(VcardConstants._telephoneSpecifierWithType)) - _telephones.Add(TelephoneInfo.FromStringVcardThreeWithType(_value)); - - // Telephone (TEL:495-522-3560) - if (_value.StartsWith(VcardConstants._telephoneSpecifier)) - _telephones.Add(TelephoneInfo.FromStringVcardThree(_value)); + // Telephone (TEL;TYPE=CELL;HOME:495-522-3560 or TEL;TYPE=cell,home:495-522-3560 or TEL:495-522-3560) + // Type is supported + if (_value.StartsWith(VcardConstants._telephoneSpecifier + delimiter)) + { + if (isWithType) + _telephones.Add(TelephoneInfo.FromStringVcardThreeWithType(_value)); + else + _telephones.Add(TelephoneInfo.FromStringVcardThree(_value)); + } - // Address (ADR;TYPE=HOME:;;Los Angeles, USA;;;;) - if (_value.StartsWith(VcardConstants._addressSpecifierWithType)) - _addresses.Add(AddressInfo.FromStringVcardThreeWithType(_value)); + // Address (ADR;TYPE=HOME:;;Los Angeles, USA;;;; or ADR:;;Los Angeles, USA;;;;) + if (_value.StartsWith(VcardConstants._addressSpecifier + delimiter)) + { + if (isWithType) + _addresses.Add(AddressInfo.FromStringVcardThreeWithType(_value)); + else + _addresses.Add(AddressInfo.FromStringVcardThree(_value)); + } // Email (EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) - if (_value.StartsWith(VcardConstants._emailSpecifier)) - _emails.Add(EmailInfo.FromStringVcardThreeWithType(_value)); + if (_value.StartsWith(VcardConstants._emailSpecifier + delimiter)) + { + if (isWithType) + _emails.Add(EmailInfo.FromStringVcardThreeWithType(_value)); + else + _emails.Add(EmailInfo.FromStringVcardThree(_value)); + } // Organization (ORG:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) - if (_value.StartsWith(VcardConstants._orgSpecifier)) - _orgs.Add(OrganizationInfo.FromStringVcardThree(_value)); - - // Organization (ORG;TYPE=WORK:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) - if (_value.StartsWith(VcardConstants._orgSpecifierWithType)) - _orgs.Add(OrganizationInfo.FromStringVcardThreeWithType(_value)); + if (_value.StartsWith(VcardConstants._orgSpecifier + delimiter)) + { + if (isWithType) + _orgs.Add(OrganizationInfo.FromStringVcardThreeWithType(_value)); + else + _orgs.Add(OrganizationInfo.FromStringVcardThree(_value)); + } // Title (TITLE:Product Manager) - if (_value.StartsWith(VcardConstants._titleSpecifier)) + if (_value.StartsWith(VcardConstants._titleSpecifier + delimiter)) _titles.Add(TitleInfo.FromStringVcardThree(_value)); // Website link (URL:https://sso.org/) - if (_value.StartsWith(VcardConstants._urlSpecifier)) + if (_value.StartsWith(VcardConstants._urlSpecifier + delimiter)) { // Get the value - string urlValue = _value.Substring(VcardConstants._urlSpecifier.Length); + string urlValue = _value.Substring(VcardConstants._urlSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(urlValue, UriKind.Absolute, out Uri uri)) @@ -165,128 +186,161 @@ public override Card Parse() } // Note (NOTE:Product Manager) - if (_value.StartsWith(VcardConstants._noteSpecifier)) + if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) { // Get the value - string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length); + string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length + 1); // Populate field _note = Regex.Unescape(noteValue); } // Photo (PHOTO;ENCODING=BASE64;JPEG:... or PHOTO;VALUE=URL:file:///jqpublic.gif or PHOTO;ENCODING=BASE64;TYPE=GIF:...) - if (_value.StartsWith(VcardConstants._photoSpecifierWithType)) - _photos.Add(PhotoInfo.FromStringVcardThreeWithType(_value, CardContentReader)); + if (_value.StartsWith(VcardConstants._photoSpecifier + delimiter)) + { + if (isWithType) + _photos.Add(PhotoInfo.FromStringVcardThreeWithType(_value, CardContentReader)); + else + throw new InvalidDataException("Photo field must not have empty type."); + } // Logo (LOGO;ENCODING=BASE64;JPEG:... or LOGO;VALUE=URL:file:///jqpublic.gif or LOGO;ENCODING=BASE64;TYPE=GIF:...) - if (_value.StartsWith(VcardConstants._logoSpecifierWithType)) - _logos.Add(LogoInfo.FromStringVcardThreeWithType(_value, CardContentReader)); + if (_value.StartsWith(VcardConstants._logoSpecifier + delimiter)) + { + if (isWithType) + _logos.Add(LogoInfo.FromStringVcardThreeWithType(_value, CardContentReader)); + else + throw new InvalidDataException("Logo field must not have empty type."); + } // Sound (SOUND;VALUE=URL:file///multimed/audio/jqpublic.wav or SOUND;WAVE;BASE64:... or SOUND;TYPE=WAVE;ENCODING=BASE64:...) - if (_value.StartsWith(VcardConstants._soundSpecifierWithType)) - _sounds.Add(SoundInfo.FromStringVcardThreeWithType(_value, CardContentReader)); + if (_value.StartsWith(VcardConstants._soundSpecifier + delimiter)) + { + if (isWithType) + _sounds.Add(SoundInfo.FromStringVcardThreeWithType(_value, CardContentReader)); + else + throw new InvalidDataException("Sound field must not have empty type."); + } // Revision (REV:1995-10-31T22:27:10Z or REV:19951031T222710) - if (_value.StartsWith(VcardConstants._revSpecifier)) + if (_value.StartsWith(VcardConstants._revSpecifier + delimiter)) { // Get the value - string revValue = _value.Substring(VcardConstants._revSpecifier.Length); + string revValue = _value.Substring(VcardConstants._revSpecifier.Length + 1); // Populate field _rev = DateTime.Parse(revValue); } // Nickname (NICKNAME;TYPE=work:Boss) - if (_value.StartsWith(VcardConstants._nicknameSpecifierWithType)) - _nicks.Add(NicknameInfo.FromStringVcardThreeWithType(_value)); - - // Nickname (NICKNAME:Jim) - if (_value.StartsWith(VcardConstants._nicknameSpecifier)) - _nicks.Add(NicknameInfo.FromStringVcardThree(_value)); + if (_value.StartsWith(VcardConstants._nicknameSpecifier + delimiter)) + { + if (isWithType) + _nicks.Add(NicknameInfo.FromStringVcardThreeWithType(_value)); + else + _nicks.Add(NicknameInfo.FromStringVcardThree(_value)); + } // Birthdate (BDAY:19950415 or BDAY:1953-10-15T23:10:00Z) - if (_value.StartsWith(VcardConstants._birthSpecifier)) + if (_value.StartsWith(VcardConstants._birthSpecifier + delimiter)) { // Get the value - string bdayValue = _value.Substring(VcardConstants._birthSpecifier.Length); + string bdayValue = ""; + if (isWithType) + bdayValue = _value.Substring(_value.IndexOf(VcardConstants._argumentDelimiter) + 1); + else + bdayValue = _value.Substring(VcardConstants._birthSpecifier.Length + 1); // Populate field - _bday = DateTime.Parse(bdayValue); + if (int.TryParse(bdayValue, out int bdayDigits) && bdayValue.Length == 8) + { + int birthNum = int.Parse(bdayValue); + var birthDigits = VcardParserTools.GetDigits(birthNum).ToList(); + int birthYear = (birthDigits[0] * 1000) + (birthDigits[1] * 100) + (birthDigits[2] * 10) + birthDigits[3]; + int birthMonth = (birthDigits[4] * 10) + birthDigits[5]; + int birthDay = (birthDigits[6] * 10) + birthDigits[7]; + _bday = new DateTime(birthYear, birthMonth, birthDay); + } + else + _bday = DateTime.Parse(bdayValue); } // Mailer (MAILER:ccMail 2.2 or MAILER:PigeonMail 2.1) - if (_value.StartsWith(VcardConstants._mailerSpecifier)) + if (_value.StartsWith(VcardConstants._mailerSpecifier + delimiter)) { // Get the value - string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length); + string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length + 1); // Populate field _mailer = Regex.Unescape(mailerValue); } // Role (ROLE:Programmer) - if (_value.StartsWith(VcardConstants._roleSpecifier)) + if (_value.StartsWith(VcardConstants._roleSpecifier + delimiter)) _roles.Add(RoleInfo.FromStringVcardThree(_value)); // Categories (CATEGORIES:INTERNET or CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY) - if (_value.StartsWith(VcardConstants._categoriesSpecifier)) + if (_value.StartsWith(VcardConstants._categoriesSpecifier + delimiter)) { // Get the value - string categoriesValue = _value.Substring(VcardConstants._categoriesSpecifier.Length); + string categoriesValue = _value.Substring(VcardConstants._categoriesSpecifier.Length + 1); // Populate field _categories.AddRange(Regex.Unescape(categoriesValue).Split(',')); } // Product ID (PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN) - if (_value.StartsWith(VcardConstants._productIdSpecifier)) + if (_value.StartsWith(VcardConstants._productIdSpecifier + delimiter)) { // Get the value - string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length); + string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length + 1); // Populate field _prodId = Regex.Unescape(prodIdValue); } // Sort string (SORT-STRING:Harten) - if (_value.StartsWith(VcardConstants._sortStringSpecifier)) + if (_value.StartsWith(VcardConstants._sortStringSpecifier + delimiter)) { // Get the value - string sortStringValue = _value.Substring(VcardConstants._sortStringSpecifier.Length); + string sortStringValue = _value.Substring(VcardConstants._sortStringSpecifier.Length + 1); // Populate field _sortString = Regex.Unescape(sortStringValue); } // Time Zone (TZ;VALUE=text:-05:00; EST; Raleigh/North America) - if (_value.StartsWith(VcardConstants._timeZoneSpecifierWithType)) - _timezones.Add(TimeZoneInfo.FromStringVcardThreeWithType(_value)); - - // Time Zone (TZ:-05:00) - if (_value.StartsWith(VcardConstants._timeZoneSpecifier)) - _timezones.Add(TimeZoneInfo.FromStringVcardThree(_value)); + if (_value.StartsWith(VcardConstants._timeZoneSpecifier + delimiter)) + { + if (isWithType) + _timezones.Add(TimeZoneInfo.FromStringVcardThreeWithType(_value)); + else + _timezones.Add(TimeZoneInfo.FromStringVcardThree(_value)); + } // Geo (GEO;VALUE=uri:https://...) - if (_value.StartsWith(VcardConstants._geoSpecifierWithType)) - _geos.Add(GeoInfo.FromStringVcardThreeWithType(_value)); - - // Geo (GEO:geo:37.386013,-122.082932) - if (_value.StartsWith(VcardConstants._geoSpecifier)) - _geos.Add(GeoInfo.FromStringVcardThree(_value)); + if (_value.StartsWith(VcardConstants._geoSpecifier + delimiter)) + { + if (isWithType) + _geos.Add(GeoInfo.FromStringVcardThreeWithType(_value)); + else + _geos.Add(GeoInfo.FromStringVcardThree(_value)); + } // IMPP information (IMPP;TYPE=home:sip:test) - if (_value.StartsWith(VcardConstants._imppSpecifierWithType)) - _impps.Add(ImppInfo.FromStringVcardThreeWithType(_value)); - - // IMPP information (IMPP:sip:test) - if (_value.StartsWith(VcardConstants._imppSpecifier)) - _impps.Add(ImppInfo.FromStringVcardThree(_value)); + if (_value.StartsWith(VcardConstants._imppSpecifier + delimiter)) + { + if (isWithType) + _impps.Add(ImppInfo.FromStringVcardThreeWithType(_value)); + else + _impps.Add(ImppInfo.FromStringVcardThree(_value)); + } // Source (SOURCE:http://johndoe.com/vcard.vcf) - if (_value.StartsWith(VcardConstants._sourceSpecifier)) + if (_value.StartsWith(VcardConstants._sourceSpecifier + delimiter)) { // Get the value - string sourceStringValue = _value.Substring(VcardConstants._sourceSpecifier.Length); + string sourceStringValue = _value.Substring(VcardConstants._sourceSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(sourceStringValue, UriKind.Absolute, out Uri uri)) @@ -366,7 +420,7 @@ internal override string SaveToString(Card card) // Then, write the full name and the name if (!string.IsNullOrWhiteSpace(card.ContactFullName)) - cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}{card.ContactFullName}"); + cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}:{card.ContactFullName}"); foreach (NameInfo name in card.ContactNames) cardBuilder.AppendLine(name.ToStringVcardThree()); @@ -382,9 +436,9 @@ internal override string SaveToString(Card card) foreach (TitleInfo title in card.ContactTitles) cardBuilder.AppendLine(title.ToStringVcardThree()); if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}{card.ContactURL}"); + cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}{card.ContactNotes}"); + cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); foreach (PhotoInfo photo in card.ContactPhotos) cardBuilder.AppendLine(photo.ToStringVcardThree()); foreach (LogoInfo logo in card.ContactLogos) @@ -392,21 +446,21 @@ internal override string SaveToString(Card card) foreach (SoundInfo sound in card.ContactSounds) cardBuilder.AppendLine(sound.ToStringVcardThree()); if (card.CardRevision is not null && card.CardRevision != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._revSpecifier}{card.CardRevision:dd-MM-yyyy_HH-mm-ss}"); + cardBuilder.AppendLine($"{VcardConstants._revSpecifier}:{card.CardRevision:yyyy-MM-dd HH:mm:ss}"); foreach (NicknameInfo nickname in card.ContactNicknames) cardBuilder.AppendLine(nickname.ToStringVcardThree()); if (card.ContactBirthdate is not null && card.ContactBirthdate != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}{card.ContactBirthdate:dd-MM-yyyy}"); + cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}:{card.ContactBirthdate:yyyy-MM-dd}"); if (!string.IsNullOrWhiteSpace(card.ContactMailer)) - cardBuilder.AppendLine($"{VcardConstants._mailerSpecifier}{card.ContactMailer}"); + cardBuilder.AppendLine($"{VcardConstants._mailerSpecifier}:{card.ContactMailer}"); foreach (RoleInfo role in card.ContactRoles) cardBuilder.AppendLine(role.ToStringVcardThree()); if (card.ContactCategories is not null && card.ContactCategories.Length > 0) - cardBuilder.AppendLine($"{VcardConstants._categoriesSpecifier}{string.Join(",", card.ContactCategories)}"); + cardBuilder.AppendLine($"{VcardConstants._categoriesSpecifier}:{string.Join(",", card.ContactCategories)}"); if (!string.IsNullOrWhiteSpace(card.ContactProdId)) - cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}{card.ContactProdId}"); + cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}:{card.ContactProdId}"); if (!string.IsNullOrWhiteSpace(card.ContactSortString)) - cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}{card.ContactSortString}"); + cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}:{card.ContactSortString}"); foreach (TimeZoneInfo timeZone in card.ContactTimeZone) cardBuilder.AppendLine(timeZone.ToStringVcardThree()); foreach (GeoInfo geo in card.ContactGeo) diff --git a/VisualCard/Parsers/Two/VcardTwo.cs b/VisualCard/Parsers/Two/VcardTwo.cs index 0f5381f..77b5362 100644 --- a/VisualCard/Parsers/Two/VcardTwo.cs +++ b/VisualCard/Parsers/Two/VcardTwo.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Mail; using System.Text; using System.Text.RegularExpressions; @@ -92,10 +93,18 @@ public override Card Parse() string _value = CardContentReader.ReadLine(); lineNumber += 1; + // Check for type + bool isWithType = false; + var valueSplit = VcardParserTools.SplitToKeyAndValueFromString(_value); + if (valueSplit[0].Contains(";")) + isWithType = true; + var delimiter = isWithType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter; + try { // The name (N:Sanders;John;;;) - if (_value.StartsWith(VcardConstants._nameSpecifier)) + // Type is not supported + if (_value.StartsWith(VcardConstants._nameSpecifier + delimiter)) { // Get the name _names.Add(NameInfo.FromStringVcardTwo(_value)); @@ -105,48 +114,62 @@ public override Card Parse() } // Full name (FN:John Sanders) - if (_value.StartsWith(VcardConstants._fullNameSpecifier)) + // Type is not supported + if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) { // Get the value - string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length); + string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); // Populate field _fullName = Regex.Unescape(fullNameValue); } - // Telephone (TEL;CELL;HOME:495-522-3560 or TEL;TYPE=cell,home:495-522-3560) - if (_value.StartsWith(VcardConstants._telephoneSpecifierWithType)) - _telephones.Add(TelephoneInfo.FromStringVcardTwoWithType(_value)); - - // Telephone (TEL:495-522-3560) - if (_value.StartsWith(VcardConstants._telephoneSpecifier)) - _telephones.Add(TelephoneInfo.FromStringVcardTwo(_value)); + // Telephone (TEL;CELL;HOME:495-522-3560 or TEL;TYPE=cell,home:495-522-3560 or TEL:495-522-3560) + // Type is supported + if (_value.StartsWith(VcardConstants._telephoneSpecifier + delimiter)) + { + if (isWithType) + _telephones.Add(TelephoneInfo.FromStringVcardTwoWithType(_value)); + else + _telephones.Add(TelephoneInfo.FromStringVcardTwo(_value)); + } - // Address (ADR;HOME:;;Los Angeles, USA;;;;) - if (_value.StartsWith(VcardConstants._addressSpecifierWithType)) - _addresses.Add(AddressInfo.FromStringVcardTwoWithType(_value)); + // Address (ADR;HOME:;;Los Angeles, USA;;;; or ADR:;;Los Angeles, USA;;;;) + if (_value.StartsWith(VcardConstants._addressSpecifier + delimiter)) + { + if (isWithType) + _addresses.Add(AddressInfo.FromStringVcardTwoWithType(_value)); + else + _addresses.Add(AddressInfo.FromStringVcardTwo(_value)); + } // Email (EMAIL;HOME;INTERNET:john.s@acme.co or EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) - if (_value.StartsWith(VcardConstants._emailSpecifier)) - _emails.Add(EmailInfo.FromStringVcardTwoWithType(_value)); + if (_value.StartsWith(VcardConstants._emailSpecifier + delimiter)) + { + if (isWithType) + _emails.Add(EmailInfo.FromStringVcardTwoWithType(_value)); + else + _emails.Add(EmailInfo.FromStringVcardTwo(_value)); + } // Organization (ORG:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) - if (_value.StartsWith(VcardConstants._orgSpecifier)) - _orgs.Add(OrganizationInfo.FromStringVcardTwo(_value)); - - // Organization (ORG;TYPE=WORK:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) - if (_value.StartsWith(VcardConstants._orgSpecifierWithType)) - _orgs.Add(OrganizationInfo.FromStringVcardTwoWithType(_value)); + if (_value.StartsWith(VcardConstants._orgSpecifier + delimiter)) + { + if (isWithType) + _orgs.Add(OrganizationInfo.FromStringVcardTwoWithType(_value)); + else + _orgs.Add(OrganizationInfo.FromStringVcardTwo(_value)); + } // Title (TITLE:Product Manager) - if (_value.StartsWith(VcardConstants._titleSpecifier)) + if (_value.StartsWith(VcardConstants._titleSpecifier + delimiter)) _titles.Add(TitleInfo.FromStringVcardTwo(_value)); // Website link (URL:https://sso.org/) - if (_value.StartsWith(VcardConstants._urlSpecifier)) + if (_value.StartsWith(VcardConstants._urlSpecifier + delimiter)) { // Get the value - string urlValue = _value.Substring(VcardConstants._urlSpecifier.Length); + string urlValue = _value.Substring(VcardConstants._urlSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(urlValue, UriKind.Absolute, out Uri uri)) @@ -157,81 +180,116 @@ public override Card Parse() } // Note (NOTE:Product Manager) - if (_value.StartsWith(VcardConstants._noteSpecifier)) + if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) { // Get the value - string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length); + string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length + 1); // Populate field _note = Regex.Unescape(noteValue); } // Photo (PHOTO;ENCODING=BASE64;JPEG:... or PHOTO;VALUE=URL:file:///jqpublic.gif or PHOTO;ENCODING=BASE64;TYPE=GIF:...) - if (_value.StartsWith(VcardConstants._photoSpecifierWithType)) - _photos.Add(PhotoInfo.FromStringVcardTwoWithType(_value, CardContentReader)); + if (_value.StartsWith(VcardConstants._photoSpecifier + delimiter)) + { + if (isWithType) + _photos.Add(PhotoInfo.FromStringVcardTwoWithType(_value, CardContentReader)); + else + throw new InvalidDataException("Photo field must not have empty type."); + } // Logo (LOGO;ENCODING=BASE64;JPEG:... or LOGO;VALUE=URL:file:///jqpublic.gif or LOGO;ENCODING=BASE64;TYPE=GIF:...) - if (_value.StartsWith(VcardConstants._logoSpecifierWithType)) - _logos.Add(LogoInfo.FromStringVcardTwoWithType(_value, CardContentReader)); + if (_value.StartsWith(VcardConstants._logoSpecifier + delimiter)) + { + if (isWithType) + _logos.Add(LogoInfo.FromStringVcardTwoWithType(_value, CardContentReader)); + else + throw new InvalidDataException("Logo field must not have empty type."); + } // Sound (SOUND;VALUE=URL:file///multimed/audio/jqpublic.wav or SOUND;WAVE;BASE64:... or SOUND;TYPE=WAVE;ENCODING=BASE64:...) - if (_value.StartsWith(VcardConstants._soundSpecifierWithType)) - _sounds.Add(SoundInfo.FromStringVcardTwoWithType(_value, CardContentReader)); + if (_value.StartsWith(VcardConstants._soundSpecifier + delimiter)) + { + if (isWithType) + _sounds.Add(SoundInfo.FromStringVcardTwoWithType(_value, CardContentReader)); + else + throw new InvalidDataException("Sound field must not have empty type."); + } // Revision (REV:1995-10-31T22:27:10Z or REV:19951031T222710) - if (_value.StartsWith(VcardConstants._revSpecifier)) + if (_value.StartsWith(VcardConstants._revSpecifier + delimiter)) { - string revValue = _value.Substring(VcardConstants._revSpecifier.Length); + string revValue = _value.Substring(VcardConstants._revSpecifier.Length + 1); _rev = DateTime.Parse(revValue); } // Birthdate (BDAY:19950415 or BDAY:1953-10-15T23:10:00Z) - if (_value.StartsWith(VcardConstants._birthSpecifier)) + if (_value.StartsWith(VcardConstants._birthSpecifier + delimiter)) { - string bdayValue = _value.Substring(VcardConstants._birthSpecifier.Length); - _bday = DateTime.Parse(bdayValue); + // Get the value + string bdayValue = ""; + if (isWithType) + bdayValue = _value.Substring(_value.IndexOf(VcardConstants._argumentDelimiter) + 1); + else + bdayValue = _value.Substring(VcardConstants._birthSpecifier.Length + 1); + + // Populate field + if (int.TryParse(bdayValue, out int bdayDigits) && bdayValue.Length == 8) + { + int birthNum = int.Parse(bdayValue); + var birthDigits = VcardParserTools.GetDigits(birthNum).ToList(); + int birthYear = (birthDigits[0] * 1000) + (birthDigits[1] * 100) + (birthDigits[2] * 10) + birthDigits[3]; + int birthMonth = (birthDigits[4] * 10) + birthDigits[5]; + int birthDay = (birthDigits[6] * 10) + birthDigits[7]; + _bday = new DateTime(birthYear, birthMonth, birthDay); + } + else + _bday = DateTime.Parse(bdayValue); } // Mailer (MAILER:ccMail 2.2 or MAILER:PigeonMail 2.1) - if (_value.StartsWith(VcardConstants._mailerSpecifier)) + if (_value.StartsWith(VcardConstants._mailerSpecifier + delimiter)) { - string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length); + string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length + 1); _mailer = Regex.Unescape(mailerValue); } // Role (ROLE:Programmer) - if (_value.StartsWith(VcardConstants._roleSpecifier)) + if (_value.StartsWith(VcardConstants._roleSpecifier + delimiter)) _roles.Add(RoleInfo.FromStringVcardTwo(_value)); // Time Zone (TZ;VALUE=text:-05:00; EST; Raleigh/North America) - if (_value.StartsWith(VcardConstants._timeZoneSpecifierWithType)) - _timezones.Add(TimeZoneInfo.FromStringVcardTwoWithType(_value)); - - // Time Zone (TZ:-05:00) - if (_value.StartsWith(VcardConstants._timeZoneSpecifier)) - _timezones.Add(TimeZoneInfo.FromStringVcardTwo(_value)); + if (_value.StartsWith(VcardConstants._timeZoneSpecifier + delimiter)) + { + if (isWithType) + _timezones.Add(TimeZoneInfo.FromStringVcardTwoWithType(_value)); + else + _timezones.Add(TimeZoneInfo.FromStringVcardTwo(_value)); + } // Geo (GEO;VALUE=uri:https://...) - if (_value.StartsWith(VcardConstants._geoSpecifierWithType)) - _geos.Add(GeoInfo.FromStringVcardTwoWithType(_value)); - - // Geo (GEO:geo:37.386013,-122.082932) - if (_value.StartsWith(VcardConstants._geoSpecifier)) - _geos.Add(GeoInfo.FromStringVcardTwo(_value)); + if (_value.StartsWith(VcardConstants._geoSpecifier + delimiter)) + { + if (isWithType) + _geos.Add(GeoInfo.FromStringVcardTwoWithType(_value)); + else + _geos.Add(GeoInfo.FromStringVcardTwo(_value)); + } // IMPP information (IMPP;TYPE=home:sip:test) - if (_value.StartsWith(VcardConstants._imppSpecifierWithType)) - _impps.Add(ImppInfo.FromStringVcardTwoWithType(_value)); - - // IMPP information (IMPP:sip:test) - if (_value.StartsWith(VcardConstants._imppSpecifier)) - _impps.Add(ImppInfo.FromStringVcardTwo(_value)); + if (_value.StartsWith(VcardConstants._imppSpecifier + delimiter)) + { + if (isWithType) + _impps.Add(ImppInfo.FromStringVcardTwoWithType(_value)); + else + _impps.Add(ImppInfo.FromStringVcardTwo(_value)); + } // Source (SOURCE:http://johndoe.com/vcard.vcf) - if (_value.StartsWith(VcardConstants._sourceSpecifier)) + if (_value.StartsWith(VcardConstants._sourceSpecifier + delimiter)) { // Get the value - string sourceStringValue = _value.Substring(VcardConstants._sourceSpecifier.Length); + string sourceStringValue = _value.Substring(VcardConstants._sourceSpecifier.Length + 1); // Try to parse the URL to ensure that it conforms to IETF RFC 1738: Uniform Resource Locators if (!Uri.TryCreate(sourceStringValue, UriKind.Absolute, out Uri uri)) @@ -309,7 +367,7 @@ internal override string SaveToString(Card card) // Then, write the full name and the name if (!string.IsNullOrWhiteSpace(card.ContactFullName)) - cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}{card.ContactFullName}"); + cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}:{card.ContactFullName}"); foreach (NameInfo name in card.ContactNames) cardBuilder.AppendLine(name.ToStringVcardTwo()); @@ -325,9 +383,9 @@ internal override string SaveToString(Card card) foreach (TitleInfo title in card.ContactTitles) cardBuilder.AppendLine(title.ToStringVcardTwo()); if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}{card.ContactURL}"); + cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}{card.ContactNotes}"); + cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); foreach (PhotoInfo photo in card.ContactPhotos) cardBuilder.AppendLine(photo.ToStringVcardTwo()); foreach (LogoInfo logo in card.ContactLogos) @@ -335,11 +393,11 @@ internal override string SaveToString(Card card) foreach (SoundInfo sound in card.ContactSounds) cardBuilder.AppendLine(sound.ToStringVcardTwo()); if (card.CardRevision is not null && card.CardRevision != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._revSpecifier}{card.CardRevision:dd-MM-yyyy_HH-mm-ss}"); + cardBuilder.AppendLine($"{VcardConstants._revSpecifier}:{card.CardRevision:yyyy-MM-dd HH:mm:ss}"); if (card.ContactBirthdate is not null && card.ContactBirthdate != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}{card.ContactBirthdate:dd-MM-yyyy}"); + cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}:{card.ContactBirthdate:yyyy-MM-dd}"); if (!string.IsNullOrWhiteSpace(card.ContactMailer)) - cardBuilder.AppendLine($"{VcardConstants._mailerSpecifier}{card.ContactMailer}"); + cardBuilder.AppendLine($"{VcardConstants._mailerSpecifier}:{card.ContactMailer}"); foreach (RoleInfo role in card.ContactRoles) cardBuilder.AppendLine(role.ToStringVcardTwo()); foreach (TimeZoneInfo timeZone in card.ContactTimeZone) diff --git a/VisualCard/Parsers/VcardConstants.cs b/VisualCard/Parsers/VcardConstants.cs index 1179e23..c9c01b2 100644 --- a/VisualCard/Parsers/VcardConstants.cs +++ b/VisualCard/Parsers/VcardConstants.cs @@ -31,52 +31,43 @@ internal static class VcardConstants internal const char _fieldDelimiter = ';'; internal const char _valueDelimiter = ','; internal const char _argumentDelimiter = ':'; - internal const string _nameSpecifier = "N:"; - internal const string _fullNameSpecifier = "FN:"; - internal const string _telephoneSpecifierWithType = "TEL;"; - internal const string _telephoneSpecifier = "TEL:"; - internal const string _addressSpecifierWithType = "ADR;"; - internal const string _emailSpecifier = "EMAIL;"; - internal const string _orgSpecifier = "ORG:"; - internal const string _orgSpecifierWithType = "ORG;"; - internal const string _titleSpecifier = "TITLE:"; - internal const string _urlSpecifier = "URL:"; - internal const string _noteSpecifier = "NOTE:"; - internal const string _photoSpecifierWithType = "PHOTO;"; - internal const string _logoSpecifierWithType = "LOGO;"; - internal const string _soundSpecifierWithType = "SOUND;"; - internal const string _revSpecifier = "REV:"; - internal const string _birthSpecifier = "BDAY:"; - internal const string _mailerSpecifier = "MAILER:"; - internal const string _roleSpecifier = "ROLE:"; - internal const string _timeZoneSpecifier = "TZ:"; - internal const string _geoSpecifier = "GEO:"; - internal const string _timeZoneSpecifierWithType = "TZ;"; - internal const string _geoSpecifierWithType = "GEO;"; - internal const string _imppSpecifier = "IMPP:"; - internal const string _imppSpecifierWithType = "IMPP;"; - internal const string _sourceSpecifier = "SOURCE:"; - internal const string _xmlSpecifier = "XML:"; - internal const string _fbUrlSpecifier = "FBURL:"; - internal const string _calUriSpecifier = "CALURI:"; - internal const string _caladrUriSpecifier = "CALADRURI:"; + internal const string _nameSpecifier = "N"; + internal const string _fullNameSpecifier = "FN"; + internal const string _telephoneSpecifier = "TEL"; + internal const string _addressSpecifier = "ADR"; + internal const string _emailSpecifier = "EMAIL"; + internal const string _orgSpecifier = "ORG"; + internal const string _titleSpecifier = "TITLE"; + internal const string _urlSpecifier = "URL"; + internal const string _noteSpecifier = "NOTE"; + internal const string _photoSpecifier = "PHOTO"; + internal const string _logoSpecifier = "LOGO"; + internal const string _soundSpecifier = "SOUND"; + internal const string _revSpecifier = "REV"; + internal const string _birthSpecifier = "BDAY"; + internal const string _mailerSpecifier = "MAILER"; + internal const string _roleSpecifier = "ROLE"; + internal const string _timeZoneSpecifier = "TZ"; + internal const string _geoSpecifier = "GEO"; + internal const string _imppSpecifier = "IMPP"; + internal const string _sourceSpecifier = "SOURCE"; + internal const string _xmlSpecifier = "XML"; + internal const string _fbUrlSpecifier = "FBURL"; + internal const string _calUriSpecifier = "CALURI"; + internal const string _caladrUriSpecifier = "CALADRURI"; internal const string _xSpecifier = "X-"; internal const string _typeArgumentSpecifier = "TYPE="; internal const string _valueArgumentSpecifier = "VALUE="; internal const string _encodingArgumentSpecifier = "ENCODING="; // Available in vCard 3.0 and vCard 4.0 - internal const string _nicknameSpecifier = "NICKNAME:"; - internal const string _nicknameSpecifierWithType = "NICKNAME;"; - internal const string _categoriesSpecifier = "CATEGORIES:"; - internal const string _productIdSpecifier = "PRODID:"; - internal const string _sortStringSpecifier = "SORT-STRING:"; + internal const string _nicknameSpecifier = "NICKNAME"; + internal const string _categoriesSpecifier = "CATEGORIES"; + internal const string _productIdSpecifier = "PRODID"; + internal const string _sortStringSpecifier = "SORT-STRING"; // Available in vCard 4.0 - internal const string _kindSpecifier = "KIND:"; - internal const string _nameSpecifierWithType = "N;"; - internal const string _titleSpecifierWithArguments = "TITLE;"; - internal const string _roleSpecifierWithType = "ROLE;"; + internal const string _kindSpecifier = "KIND"; internal const string _altIdArgumentSpecifier = "ALTID="; } } diff --git a/VisualCard/Parsers/VcardParserTools.cs b/VisualCard/Parsers/VcardParserTools.cs index 9254ede..05bc6f2 100644 --- a/VisualCard/Parsers/VcardParserTools.cs +++ b/VisualCard/Parsers/VcardParserTools.cs @@ -23,6 +23,8 @@ * */ +using System.Collections.Generic; +using System; using System.IO; using System.Linq; @@ -86,5 +88,25 @@ internal static string GetValuesString(string[] args, string @default, string ar internal static string[] GetValues(string[] args, string @default, string argSpecifier) => GetValuesString(args, @default, argSpecifier).Split(VcardConstants._valueDelimiter); + + internal static string[] SplitToKeyAndValueFromString(string line) + { + string key = line.Substring(0, line.IndexOf(':')); + string value = line.Substring(line.IndexOf(':')); + return new[] { key, value }; + } + + internal static IEnumerable GetDigits(int num) + { + int individualFactor = 0; + int tennerFactor = Convert.ToInt32(Math.Pow(10, num.ToString().Length)); + while (tennerFactor > 1) + { + num -= tennerFactor * individualFactor; + tennerFactor /= 10; + individualFactor = num / tennerFactor; + yield return individualFactor; + } + } } } diff --git a/VisualCard/Parts/AddressInfo.cs b/VisualCard/Parts/AddressInfo.cs index b1a5e89..73d5a6c 100644 --- a/VisualCard/Parts/AddressInfo.cs +++ b/VisualCard/Parts/AddressInfo.cs @@ -132,7 +132,7 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._addressSpecifierWithType}" + + $"{VcardConstants._addressSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + @@ -146,7 +146,7 @@ internal string ToStringVcardTwo() internal string ToStringVcardThree() { return - $"{VcardConstants._addressSpecifierWithType}" + + $"{VcardConstants._addressSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + @@ -161,7 +161,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{VcardConstants._addressSpecifierWithType}" + + $"{VcardConstants._addressSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + @@ -173,10 +173,34 @@ internal string ToStringVcardFour() $"{Country}"; } + internal static AddressInfo FromStringVcardTwo(string value) + { + // Get the value + string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1); + string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); + + // Check the provided address + string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); + if (splitAddressValues.Length < 7) + throw new InvalidDataException("Address information must specify exactly seven values (P.O. Box, extended address, street address, locality, region, postal code, and country)"); + + // Populate the fields + string[] _addressTypes = new string[] { "HOME" }; + string _addressPOBox = Regex.Unescape(splitAddressValues[0]); + string _addressExtended = Regex.Unescape(splitAddressValues[1]); + string _addressStreet = Regex.Unescape(splitAddressValues[2]); + string _addressLocality = Regex.Unescape(splitAddressValues[3]); + string _addressRegion = Regex.Unescape(splitAddressValues[4]); + string _addressPostalCode = Regex.Unescape(splitAddressValues[5]); + string _addressCountry = Regex.Unescape(splitAddressValues[6]); + AddressInfo _address = new(0, Array.Empty(), _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); + return _address; + } + internal static AddressInfo FromStringVcardTwoWithType(string value) { // Get the value - string adrValue = value.Substring(VcardConstants._addressSpecifierWithType.Length); + string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1); string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); if (splitAdr.Length < 2) throw new InvalidDataException("Address field must specify exactly two values (Type (optionally prepended with TYPE=), and address information)"); @@ -199,10 +223,34 @@ internal static AddressInfo FromStringVcardTwoWithType(string value) return _address; } + internal static AddressInfo FromStringVcardThree(string value) + { + // Get the value + string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1); + string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); + + // Check the provided address + string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); + if (splitAddressValues.Length < 7) + throw new InvalidDataException("Address information must specify exactly seven values (P.O. Box, extended address, street address, locality, region, postal code, and country)"); + + // Populate the fields + string[] _addressTypes = new string[] { "HOME" }; + string _addressPOBox = Regex.Unescape(splitAddressValues[0]); + string _addressExtended = Regex.Unescape(splitAddressValues[1]); + string _addressStreet = Regex.Unescape(splitAddressValues[2]); + string _addressLocality = Regex.Unescape(splitAddressValues[3]); + string _addressRegion = Regex.Unescape(splitAddressValues[4]); + string _addressPostalCode = Regex.Unescape(splitAddressValues[5]); + string _addressCountry = Regex.Unescape(splitAddressValues[6]); + AddressInfo _address = new(0, Array.Empty(), _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); + return _address; + } + internal static AddressInfo FromStringVcardThreeWithType(string value) { // Get the value - string adrValue = value.Substring(VcardConstants._addressSpecifierWithType.Length); + string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1); string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); if (splitAdr.Length < 2) throw new InvalidDataException("Address field must specify exactly two values (Type (must be prepended with TYPE=), and address information)"); @@ -225,10 +273,34 @@ internal static AddressInfo FromStringVcardThreeWithType(string value) return _address; } + internal static AddressInfo FromStringVcardFour(string value, int altId) + { + // Get the value + string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1); + string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); + + // Check the provided address + string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); + if (splitAddressValues.Length < 7) + throw new InvalidDataException("Address information must specify exactly seven values (P.O. Box, extended address, street address, locality, region, postal code, and country)"); + + // Populate the fields + string[] _addressTypes = new string[] { "HOME" }; + string _addressPOBox = Regex.Unescape(splitAddressValues[0]); + string _addressExtended = Regex.Unescape(splitAddressValues[1]); + string _addressStreet = Regex.Unescape(splitAddressValues[2]); + string _addressLocality = Regex.Unescape(splitAddressValues[3]); + string _addressRegion = Regex.Unescape(splitAddressValues[4]); + string _addressPostalCode = Regex.Unescape(splitAddressValues[5]); + string _addressCountry = Regex.Unescape(splitAddressValues[6]); + AddressInfo _address = new(altId, Array.Empty(), _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); + return _address; + } + internal static AddressInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string adrValue = value.Substring(VcardConstants._addressSpecifierWithType.Length); + string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1); string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); if (splitAdr.Length < 2) throw new InvalidDataException("Address field must specify exactly two values (Type (must be prepended with TYPE=), and address information)"); diff --git a/VisualCard/Parts/EmailInfo.cs b/VisualCard/Parts/EmailInfo.cs index f087d30..36fe7fb 100644 --- a/VisualCard/Parts/EmailInfo.cs +++ b/VisualCard/Parts/EmailInfo.cs @@ -96,7 +96,7 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._emailSpecifier}" + + $"{VcardConstants._emailSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactEmailAddress}"; } @@ -104,7 +104,7 @@ internal string ToStringVcardTwo() internal string ToStringVcardThree() { return - $"{VcardConstants._emailSpecifier}" + + $"{VcardConstants._emailSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactEmailAddress}"; } @@ -113,16 +113,40 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{VcardConstants._emailSpecifier}" + + $"{VcardConstants._emailSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactEmailAddress}"; } + internal static EmailInfo FromStringVcardTwo(string value) + { + // Get the value + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); + string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); + MailAddress mail; + + // Try to create mail address + try + { + mail = new MailAddress(splitMail[0]); + } + catch (ArgumentException aex) + { + throw new InvalidDataException("E-mail address is invalid", aex); + } + + // Populate the fields + string[] _emailTypes = new string[] { "HOME" }; + string _emailAddress = mail.Address; + EmailInfo _email = new(0, Array.Empty(), _emailTypes, _emailAddress); + return _email; + } + internal static EmailInfo FromStringVcardTwoWithType(string value) { // Get the value - string mailValue = value.Substring(VcardConstants._emailSpecifier.Length); + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); MailAddress mail; if (splitMail.Length < 2) @@ -145,10 +169,34 @@ internal static EmailInfo FromStringVcardTwoWithType(string value) return _email; } + internal static EmailInfo FromStringVcardThree(string value) + { + // Get the value + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); + string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); + MailAddress mail; + + // Try to create mail address + try + { + mail = new MailAddress(splitMail[0]); + } + catch (ArgumentException aex) + { + throw new InvalidDataException("E-mail address is invalid", aex); + } + + // Populate the fields + string[] _emailTypes = new string[] { "HOME" }; + string _emailAddress = mail.Address; + EmailInfo _email = new(0, Array.Empty(), _emailTypes, _emailAddress); + return _email; + } + internal static EmailInfo FromStringVcardThreeWithType(string value) { // Get the value - string mailValue = value.Substring(VcardConstants._emailSpecifier.Length); + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); MailAddress mail; if (splitMail.Length < 2) @@ -171,10 +219,34 @@ internal static EmailInfo FromStringVcardThreeWithType(string value) return _email; } + internal static EmailInfo FromStringVcardFour(string value, int altId) + { + // Get the value + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); + string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); + MailAddress mail; + + // Try to create mail address + try + { + mail = new MailAddress(splitMail[0]); + } + catch (ArgumentException aex) + { + throw new InvalidDataException("E-mail address is invalid", aex); + } + + // Populate the fields + string[] _emailTypes = new string[] { "HOME" }; + string _emailAddress = mail.Address; + EmailInfo _email = new(altId, Array.Empty(), _emailTypes, _emailAddress); + return _email; + } + internal static EmailInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string mailValue = value.Substring(VcardConstants._emailSpecifier.Length); + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); MailAddress mail; if (splitMail.Length < 2) diff --git a/VisualCard/Parts/GeoInfo.cs b/VisualCard/Parts/GeoInfo.cs index 133b824..3f05128 100644 --- a/VisualCard/Parts/GeoInfo.cs +++ b/VisualCard/Parts/GeoInfo.cs @@ -96,14 +96,14 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._geoSpecifier}" + + $"{VcardConstants._geoSpecifier}:" + $"{Geo}"; } internal string ToStringVcardThree() { return - $"{VcardConstants._geoSpecifier}" + + $"{VcardConstants._geoSpecifier}:" + $"{Geo}"; } @@ -111,7 +111,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{(installAltId ? VcardConstants._geoSpecifierWithType : VcardConstants._geoSpecifier)}" + + $"{VcardConstants._geoSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + $"{Geo}"; @@ -119,7 +119,7 @@ internal string ToStringVcardFour() internal static GeoInfo FromStringVcardTwo(string value) { - string geoValue = value.Substring(VcardConstants._geoSpecifier.Length); + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); string _geoStr = Regex.Unescape(geoValue); GeoInfo _geo = new(0, Array.Empty(), Array.Empty(), _geoStr); return _geo; @@ -127,7 +127,7 @@ internal static GeoInfo FromStringVcardTwo(string value) internal static GeoInfo FromStringVcardTwoWithType(string value) { - string geoValue = value.Substring(VcardConstants._geoSpecifierWithType.Length); + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); string[] splitGeo = geoValue.Split(VcardConstants._argumentDelimiter); if (splitGeo.Length < 2) throw new InvalidDataException("Geo field must specify exactly two values (VALUE=\"uri\", and geo info)"); @@ -144,7 +144,7 @@ internal static GeoInfo FromStringVcardTwoWithType(string value) internal static GeoInfo FromStringVcardThree(string value) { // Get the value - string geoValue = value.Substring(VcardConstants._geoSpecifier.Length); + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); // Populate the fields string[] _geoTypes = new string[] { "uri" }; @@ -156,7 +156,7 @@ internal static GeoInfo FromStringVcardThree(string value) internal static GeoInfo FromStringVcardThreeWithType(string value) { // Get the value - string geoValue = value.Substring(VcardConstants._geoSpecifierWithType.Length); + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); string[] splitGeo = geoValue.Split(VcardConstants._argumentDelimiter); if (splitGeo.Length < 2) throw new InvalidDataException("Geo field must specify exactly two values (VALUE=\"uri\", and geo info)"); @@ -173,7 +173,7 @@ internal static GeoInfo FromStringVcardThreeWithType(string value) internal static GeoInfo FromStringVcardFour(string value, int altId) { // Get the value - string geoValue = value.Substring(VcardConstants._geoSpecifier.Length); + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); // Populate the fields string[] _geoTypes = new string[] { "uri" }; @@ -185,7 +185,7 @@ internal static GeoInfo FromStringVcardFour(string value, int altId) internal static GeoInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string geoValue = value.Substring(VcardConstants._geoSpecifierWithType.Length); + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); string[] splitGeo = geoValue.Split(VcardConstants._argumentDelimiter); if (splitGeo.Length < 2) throw new InvalidDataException("Geo field must specify exactly two values (VALUE=\"uri\", and geo info)"); diff --git a/VisualCard/Parts/ImppInfo.cs b/VisualCard/Parts/ImppInfo.cs index d0dc853..940be1d 100644 --- a/VisualCard/Parts/ImppInfo.cs +++ b/VisualCard/Parts/ImppInfo.cs @@ -95,33 +95,36 @@ public override int GetHashCode() internal string ToStringVcardTwo() { + bool installType = ImppTypes.Length > 0 && ImppTypes[0].ToUpper() != "HOME"; return - $"{VcardConstants._imppSpecifierWithType}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ImppTypes)}{VcardConstants._argumentDelimiter}" + + $"{VcardConstants._imppSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ImppTypes)}{VcardConstants._argumentDelimiter}" : "")}" + $"{ContactIMPP}"; } internal string ToStringVcardThree() { + bool installType = ImppTypes.Length > 0 && ImppTypes[0].ToUpper() != "HOME"; return - $"{VcardConstants._imppSpecifierWithType}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ImppTypes)}{VcardConstants._argumentDelimiter}" + + $"{VcardConstants._imppSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ImppTypes)}{VcardConstants._argumentDelimiter}" : "")}" + $"{ContactIMPP}"; } internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; + bool installType = ImppTypes.Length > 0 && ImppTypes[0].ToUpper() != "HOME"; return - $"{(installAltId ? VcardConstants._imppSpecifierWithType : VcardConstants._imppSpecifier)}" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + + $"{VcardConstants._imppSpecifier}{(installType || installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + (installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter) : "")}" + + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ImppTypes)}{VcardConstants._argumentDelimiter}" : "")}" + $"{ContactIMPP}"; } internal static ImppInfo FromStringVcardTwo(string value) { - string imppValue = value.Substring(VcardConstants._imppSpecifier.Length); + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); string[] _imppTypes = new string[] { "HOME" }; string _impp = Regex.Unescape(imppValue); ImppInfo _imppInstance = new(0, Array.Empty(), _impp, _imppTypes); @@ -130,7 +133,7 @@ internal static ImppInfo FromStringVcardTwo(string value) internal static ImppInfo FromStringVcardTwoWithType(string value) { - string imppValue = value.Substring(VcardConstants._imppSpecifierWithType.Length); + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); string[] splitImpp = imppValue.Split(VcardConstants._argumentDelimiter); if (splitImpp.Length < 2) throw new InvalidDataException("IMPP information field must specify exactly two values (Type (must be prepended with TYPE=), and impp)"); @@ -145,7 +148,7 @@ internal static ImppInfo FromStringVcardTwoWithType(string value) internal static ImppInfo FromStringVcardThree(string value) { // Get the value - string imppValue = value.Substring(VcardConstants._imppSpecifier.Length); + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); // Populate the fields string[] _imppTypes = new string[] { "HOME" }; @@ -157,7 +160,7 @@ internal static ImppInfo FromStringVcardThree(string value) internal static ImppInfo FromStringVcardThreeWithType(string value) { // Get the value - string imppValue = value.Substring(VcardConstants._imppSpecifierWithType.Length); + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); string[] splitImpp = imppValue.Split(VcardConstants._argumentDelimiter); if (splitImpp.Length < 2) throw new InvalidDataException("IMPP information field must specify exactly two values (Type (must be prepended with TYPE=), and impp)"); @@ -172,7 +175,7 @@ internal static ImppInfo FromStringVcardThreeWithType(string value) internal static ImppInfo FromStringVcardFour(string value, int altId) { // Get the value - string imppValue = value.Substring(VcardConstants._imppSpecifier.Length); + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); // Populate the fields string[] _imppTypes = new string[] { "HOME" }; @@ -184,7 +187,7 @@ internal static ImppInfo FromStringVcardFour(string value, int altId) internal static ImppInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string imppValue = value.Substring(VcardConstants._imppSpecifierWithType.Length); + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); string[] splitImpp = imppValue.Split(VcardConstants._argumentDelimiter); if (splitImpp.Length < 2) throw new InvalidDataException("IMPP information field must specify exactly two values (Type (must be prepended with TYPE=), and impp)"); diff --git a/VisualCard/Parts/LogoInfo.cs b/VisualCard/Parts/LogoInfo.cs index 17bd115..9ec29f3 100644 --- a/VisualCard/Parts/LogoInfo.cs +++ b/VisualCard/Parts/LogoInfo.cs @@ -110,14 +110,14 @@ internal string ToStringVcardTwo() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._logoSpecifierWithType}" + + $"{VcardConstants._logoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + $"{LogoEncoded}"; } else { string logoArgsLine = - $"{VcardConstants._logoSpecifierWithType}" + + $"{VcardConstants._logoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; @@ -130,14 +130,14 @@ internal string ToStringVcardThree() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._logoSpecifierWithType}" + + $"{VcardConstants._logoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + $"{LogoEncoded}"; } else { string logoArgsLine = - $"{VcardConstants._logoSpecifierWithType}" + + $"{VcardConstants._logoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; @@ -151,7 +151,7 @@ internal string ToStringVcardFour() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._logoSpecifierWithType}" + + $"{VcardConstants._logoSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + @@ -160,7 +160,7 @@ internal string ToStringVcardFour() else { string logoArgsLine = - $"{VcardConstants._logoSpecifierWithType}" + + $"{VcardConstants._logoSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + @@ -173,7 +173,7 @@ internal string ToStringVcardFour() internal static LogoInfo FromStringVcardTwoWithType(string value, StreamReader cardContentReader) { // Get the value - string logoValue = value.Substring(VcardConstants._logoSpecifierWithType.Length); + string logoValue = value.Substring(VcardConstants._logoSpecifier.Length + 1); string[] splitLogo = logoValue.Split(VcardConstants._argumentDelimiter); if (splitLogo.Length < 2) throw new InvalidDataException("Logo field must specify exactly two values (Type and arguments, and logo information)"); @@ -212,7 +212,7 @@ internal static LogoInfo FromStringVcardTwoWithType(string value, StreamReader c internal static LogoInfo FromStringVcardThreeWithType(string value, StreamReader cardContentReader) { // Get the value - string logoValue = value.Substring(VcardConstants._logoSpecifierWithType.Length); + string logoValue = value.Substring(VcardConstants._logoSpecifier.Length + 1); string[] splitLogo = logoValue.Split(VcardConstants._argumentDelimiter); if (splitLogo.Length >= 2) throw new InvalidDataException("Logo field must specify exactly two values (Type and arguments, and logo information)"); @@ -251,7 +251,7 @@ internal static LogoInfo FromStringVcardThreeWithType(string value, StreamReader internal static LogoInfo FromStringVcardFourWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) { // Get the value - string logoValue = value.Substring(VcardConstants._logoSpecifierWithType.Length); + string logoValue = value.Substring(VcardConstants._logoSpecifier.Length + 1); string[] splitLogo = logoValue.Split(VcardConstants._argumentDelimiter); if (splitLogo.Length >= 2) throw new InvalidDataException("Logo field must specify exactly two values (Type and arguments, and logo information)"); diff --git a/VisualCard/Parts/NameInfo.cs b/VisualCard/Parts/NameInfo.cs index e8ef873..b70d4c8 100644 --- a/VisualCard/Parts/NameInfo.cs +++ b/VisualCard/Parts/NameInfo.cs @@ -118,12 +118,12 @@ internal string ToStringVcardTwo() string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); return - $"{VcardConstants._nameSpecifier}" + + $"{VcardConstants._nameSpecifier}:" + $"{ContactLastName}{VcardConstants._fieldDelimiter}" + $"{ContactFirstName}{VcardConstants._fieldDelimiter}" + $"{altNamesStr}{VcardConstants._fieldDelimiter}" + $"{prefixesStr}{VcardConstants._fieldDelimiter}" + - $"{suffixesStr}{VcardConstants._fieldDelimiter}"; + $"{suffixesStr}"; } internal string ToStringVcardThree() @@ -132,12 +132,12 @@ internal string ToStringVcardThree() string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); return - $"{VcardConstants._nameSpecifier}" + + $"{VcardConstants._nameSpecifier}:" + $"{ContactLastName}{VcardConstants._fieldDelimiter}" + $"{ContactFirstName}{VcardConstants._fieldDelimiter}" + $"{altNamesStr}{VcardConstants._fieldDelimiter}" + $"{prefixesStr}{VcardConstants._fieldDelimiter}" + - $"{suffixesStr}{VcardConstants._fieldDelimiter}"; + $"{suffixesStr}"; } internal string ToStringVcardFour() @@ -147,20 +147,20 @@ internal string ToStringVcardFour() string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); return - $"{(installAltId ? VcardConstants._nameSpecifierWithType : VcardConstants._nameSpecifier)}" + + $"{VcardConstants._nameSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + $"{ContactLastName}{VcardConstants._fieldDelimiter}" + $"{ContactFirstName}{VcardConstants._fieldDelimiter}" + $"{altNamesStr}{VcardConstants._fieldDelimiter}" + $"{prefixesStr}{VcardConstants._fieldDelimiter}" + - $"{suffixesStr}{VcardConstants._fieldDelimiter}"; + $"{suffixesStr}"; } internal static NameInfo FromStringVcardTwo(string value) { // Check the line - string nameValue = value.Substring(VcardConstants._nameSpecifier.Length); + string nameValue = value.Substring(VcardConstants._nameSpecifier.Length + 1); string[] splitName = nameValue.Split(VcardConstants._fieldDelimiter); if (splitName.Length < 2) throw new InvalidDataException("Name field must specify the first two or more of the five values (Last name, first name, alt names, prefixes, and suffixes)"); @@ -178,7 +178,7 @@ internal static NameInfo FromStringVcardTwo(string value) internal static NameInfo FromStringVcardThree(string value) { // Check the line - string nameValue = value.Substring(VcardConstants._nameSpecifier.Length); + string nameValue = value.Substring(VcardConstants._nameSpecifier.Length + 1); string[] splitName = nameValue.Split(VcardConstants._fieldDelimiter); if (splitName.Length < 2) throw new InvalidDataException("Name field must specify exactly five values (Last name, first name, alt names, prefixes, and suffixes)"); @@ -216,7 +216,7 @@ internal static NameInfo FromStringVcardFour(string[] splitValues, bool idReserv internal static NameInfo FromStringVcardFourWithType(string value, string[] splitArgs, List finalArgs, int altId, List _names, bool idReservedForName) { // Check the line - string nameValue = value.Substring(VcardConstants._nameSpecifierWithType.Length); + string nameValue = value.Substring(VcardConstants._nameSpecifier.Length + 1); string[] splitNameParts = nameValue.Split(VcardConstants._argumentDelimiter); string[] splitName = splitNameParts[1].Split(VcardConstants._fieldDelimiter); if (splitName.Length < 2) diff --git a/VisualCard/Parts/NicknameInfo.cs b/VisualCard/Parts/NicknameInfo.cs index 757ea86..00c0198 100644 --- a/VisualCard/Parts/NicknameInfo.cs +++ b/VisualCard/Parts/NicknameInfo.cs @@ -101,7 +101,7 @@ internal string ToStringVcardTwo() internal string ToStringVcardThree() { return - $"{VcardConstants._nicknameSpecifierWithType}" + + $"{VcardConstants._nicknameSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", NicknameTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactNickname}"; } @@ -110,7 +110,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{(installAltId ? VcardConstants._nicknameSpecifierWithType : VcardConstants._nicknameSpecifier)}" + + $"{VcardConstants._nicknameSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", NicknameTypes)}{VcardConstants._argumentDelimiter}" + @@ -120,7 +120,7 @@ internal string ToStringVcardFour() internal static NicknameInfo FromStringVcardThree(string value) { // Get the value - string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length); + string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); // Populate the fields string[] _nicknameTypes = new string[] { "HOME" }; @@ -132,7 +132,7 @@ internal static NicknameInfo FromStringVcardThree(string value) internal static NicknameInfo FromStringVcardThreeWithType(string value) { // Get the value - string nickValue = value.Substring(VcardConstants._nicknameSpecifierWithType.Length); + string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); string[] splitNick = nickValue.Split(VcardConstants._argumentDelimiter); if (splitNick.Length < 2) throw new InvalidDataException("Nickname field must specify exactly two values (Type (must be prepended with TYPE=), and nickname)"); @@ -147,7 +147,7 @@ internal static NicknameInfo FromStringVcardThreeWithType(string value) internal static NicknameInfo FromStringVcardFour(string value, int altId) { // Get the value - string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length); + string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); // Populate the fields string[] _nicknameTypes = new string[] { "HOME" }; @@ -159,7 +159,7 @@ internal static NicknameInfo FromStringVcardFour(string value, int altId) internal static NicknameInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string nickValue = value.Substring(VcardConstants._nicknameSpecifierWithType.Length); + string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); string[] splitNick = nickValue.Split(VcardConstants._argumentDelimiter); if (splitNick.Length < 2) throw new InvalidDataException("Nickname field must specify exactly two values (Type (must be prepended with TYPE=), and nickname)"); diff --git a/VisualCard/Parts/OrganizationInfo.cs b/VisualCard/Parts/OrganizationInfo.cs index ebb0371..b400c11 100644 --- a/VisualCard/Parts/OrganizationInfo.cs +++ b/VisualCard/Parts/OrganizationInfo.cs @@ -109,7 +109,7 @@ internal string ToStringVcardTwo() { bool installType = OrgTypes.Length > 0 && OrgTypes[0].ToUpper() != "WORK"; return - $"{(installType ? VcardConstants._orgSpecifierWithType : VcardConstants._orgSpecifier)}" + + $"{VcardConstants._orgSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", OrgTypes)}{VcardConstants._argumentDelimiter}" : "")}" + $"{Name}{VcardConstants._fieldDelimiter}" + $"{Unit}{VcardConstants._fieldDelimiter}" + @@ -120,7 +120,7 @@ internal string ToStringVcardThree() { bool installType = OrgTypes.Length > 0 && OrgTypes[0].ToUpper() != "WORK"; return - $"{(installType ? VcardConstants._orgSpecifierWithType : VcardConstants._orgSpecifier)}" + + $"{VcardConstants._orgSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", OrgTypes)}{VcardConstants._argumentDelimiter}" : "")}" + $"{Name}{VcardConstants._fieldDelimiter}" + $"{Unit}{VcardConstants._fieldDelimiter}" + @@ -132,7 +132,7 @@ internal string ToStringVcardFour() bool installAltId = AltId >= 0 && AltArguments.Length > 0; bool installType = (installAltId || OrgTypes.Length > 0) && OrgTypes[0].ToUpper() != "WORK"; return - $"{(installType ? VcardConstants._orgSpecifierWithType : VcardConstants._orgSpecifier)}" + + $"{VcardConstants._orgSpecifier}{(installType || installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + (installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter) : "")}" + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", OrgTypes)}{VcardConstants._argumentDelimiter}" : "")}" + $"{Name}{VcardConstants._fieldDelimiter}" + @@ -143,7 +143,7 @@ internal string ToStringVcardFour() internal static OrganizationInfo FromStringVcardTwo(string value) { // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifier.Length); + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); // Populate the fields @@ -158,7 +158,7 @@ internal static OrganizationInfo FromStringVcardTwo(string value) internal static OrganizationInfo FromStringVcardTwoWithType(string value) { // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifierWithType.Length); + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); string[] splitOrg = orgValue.Split(VcardConstants._argumentDelimiter); if (splitOrg.Length < 2) throw new InvalidDataException("Organization field must specify exactly two values (Type, and address information)"); @@ -180,7 +180,7 @@ internal static OrganizationInfo FromStringVcardTwoWithType(string value) internal static OrganizationInfo FromStringVcardThree(string value) { // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifier.Length); + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); // Populate the fields @@ -195,7 +195,7 @@ internal static OrganizationInfo FromStringVcardThree(string value) internal static OrganizationInfo FromStringVcardThreeWithType(string value) { // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifierWithType.Length); + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); string[] splitOrg = orgValue.Split(VcardConstants._argumentDelimiter); if (splitOrg.Length < 2) throw new InvalidDataException("Organization field must specify exactly two values (Type (must be prepended with TYPE=), and address information)"); @@ -217,7 +217,7 @@ internal static OrganizationInfo FromStringVcardThreeWithType(string value) internal static OrganizationInfo FromStringVcardFour(string value, int altId) { // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifier.Length); + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); // Populate the fields @@ -232,7 +232,7 @@ internal static OrganizationInfo FromStringVcardFour(string value, int altId) internal static OrganizationInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifierWithType.Length); + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); string[] splitOrg = orgValue.Split(VcardConstants._argumentDelimiter); if (splitOrg.Length < 2) throw new InvalidDataException("Organization field must specify exactly two values (Type (must be prepended with TYPE=), and address information)"); diff --git a/VisualCard/Parts/PhotoInfo.cs b/VisualCard/Parts/PhotoInfo.cs index 90715ef..27f5e19 100644 --- a/VisualCard/Parts/PhotoInfo.cs +++ b/VisualCard/Parts/PhotoInfo.cs @@ -111,14 +111,14 @@ internal string ToStringVcardTwo() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._photoSpecifierWithType}" + + $"{VcardConstants._photoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + $"{PhotoEncoded}"; } else { string photoArgsLine = - $"{VcardConstants._photoSpecifierWithType}" + + $"{VcardConstants._photoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; @@ -131,14 +131,14 @@ internal string ToStringVcardThree() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._photoSpecifierWithType}" + + $"{VcardConstants._photoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + $"{PhotoEncoded}"; } else { string photoArgsLine = - $"{VcardConstants._photoSpecifierWithType}" + + $"{VcardConstants._photoSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; @@ -152,7 +152,7 @@ internal string ToStringVcardFour() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._photoSpecifierWithType}" + + $"{VcardConstants._photoSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + @@ -161,7 +161,7 @@ internal string ToStringVcardFour() else { string photoArgsLine = - $"{VcardConstants._photoSpecifierWithType}" + + $"{VcardConstants._photoSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + @@ -174,7 +174,7 @@ internal string ToStringVcardFour() internal static PhotoInfo FromStringVcardTwoWithType(string value, StreamReader cardContentReader) { // Get the value - string photoValue = value.Substring(VcardConstants._photoSpecifierWithType.Length); + string photoValue = value.Substring(VcardConstants._photoSpecifier.Length + 1); string[] splitPhoto = photoValue.Split(VcardConstants._argumentDelimiter); if (splitPhoto.Length < 2) throw new InvalidDataException("Photo field must specify exactly two values (Type and arguments, and photo information)"); @@ -213,7 +213,7 @@ internal static PhotoInfo FromStringVcardTwoWithType(string value, StreamReader internal static PhotoInfo FromStringVcardThreeWithType(string value, StreamReader cardContentReader) { // Get the value - string photoValue = value.Substring(VcardConstants._photoSpecifierWithType.Length); + string photoValue = value.Substring(VcardConstants._photoSpecifier.Length + 1); string[] splitPhoto = photoValue.Split(VcardConstants._argumentDelimiter); if (splitPhoto.Length >= 2) throw new InvalidDataException("Photo field must specify exactly two values (Type and arguments, and photo information)"); @@ -252,7 +252,7 @@ internal static PhotoInfo FromStringVcardThreeWithType(string value, StreamReade internal static PhotoInfo FromStringVcardFourWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) { // Get the value - string photoValue = value.Substring(VcardConstants._photoSpecifierWithType.Length); + string photoValue = value.Substring(VcardConstants._photoSpecifier.Length + 1); string[] splitPhoto = photoValue.Split(VcardConstants._argumentDelimiter); if (splitPhoto.Length >= 2) throw new InvalidDataException("Photo field must specify exactly two values (Type and arguments, and photo information)"); diff --git a/VisualCard/Parts/RoleInfo.cs b/VisualCard/Parts/RoleInfo.cs index 45d7c65..41165a9 100644 --- a/VisualCard/Parts/RoleInfo.cs +++ b/VisualCard/Parts/RoleInfo.cs @@ -90,14 +90,14 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._roleSpecifier}" + + $"{VcardConstants._roleSpecifier};" + $"{ContactRole}"; } internal string ToStringVcardThree() { return - $"{VcardConstants._roleSpecifier}" + + $"{VcardConstants._roleSpecifier};" + $"{ContactRole}"; } @@ -105,7 +105,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{(installAltId ? VcardConstants._roleSpecifierWithType : VcardConstants._roleSpecifier)}" + + $"{VcardConstants._roleSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + $"{ContactRole}"; @@ -114,7 +114,7 @@ internal string ToStringVcardFour() internal static RoleInfo FromStringVcardTwo(string value) { // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length); + string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); // Populate the fields RoleInfo _role = new(0, Array.Empty(), roleValue); @@ -124,7 +124,7 @@ internal static RoleInfo FromStringVcardTwo(string value) internal static RoleInfo FromStringVcardThree(string value) { // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length); + string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); // Populate the fields RoleInfo _role = new(0, Array.Empty(), roleValue); @@ -134,7 +134,7 @@ internal static RoleInfo FromStringVcardThree(string value) internal static RoleInfo FromStringVcardFour(string value, int altId) { // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length); + string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); // Populate the fields RoleInfo _role = new(altId, Array.Empty(), roleValue); @@ -144,7 +144,7 @@ internal static RoleInfo FromStringVcardFour(string value, int altId) internal static RoleInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length); + string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); // Populate the fields RoleInfo _role = new(altId, finalArgs.ToArray(), roleValue); diff --git a/VisualCard/Parts/SoundInfo.cs b/VisualCard/Parts/SoundInfo.cs index 11068e4..836d23e 100644 --- a/VisualCard/Parts/SoundInfo.cs +++ b/VisualCard/Parts/SoundInfo.cs @@ -110,14 +110,14 @@ internal string ToStringVcardTwo() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._soundSpecifierWithType}" + + $"{VcardConstants._soundSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + $"{SoundEncoded}"; } else { string soundArgsLine = - $"{VcardConstants._soundSpecifierWithType}" + + $"{VcardConstants._soundSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; @@ -130,14 +130,14 @@ internal string ToStringVcardThree() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._soundSpecifierWithType}" + + $"{VcardConstants._soundSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + $"{SoundEncoded}"; } else { string soundArgsLine = - $"{VcardConstants._soundSpecifierWithType}" + + $"{VcardConstants._soundSpecifier};" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; @@ -151,7 +151,7 @@ internal string ToStringVcardFour() if (ValueType == "uri" || ValueType == "url") { return - $"{VcardConstants._soundSpecifierWithType}" + + $"{VcardConstants._soundSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + @@ -160,7 +160,7 @@ internal string ToStringVcardFour() else { string soundArgsLine = - $"{VcardConstants._soundSpecifierWithType}" + + $"{VcardConstants._soundSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + @@ -173,7 +173,7 @@ internal string ToStringVcardFour() internal static SoundInfo FromStringVcardTwoWithType(string value, StreamReader cardContentReader) { // Get the value - string soundValue = value.Substring(VcardConstants._soundSpecifierWithType.Length); + string soundValue = value.Substring(VcardConstants._soundSpecifier.Length + 1); string[] splitSound = soundValue.Split(VcardConstants._argumentDelimiter); if (splitSound.Length < 2) throw new InvalidDataException("Sound field must specify exactly two values (Type and arguments, and sound information)"); @@ -212,7 +212,7 @@ internal static SoundInfo FromStringVcardTwoWithType(string value, StreamReader internal static SoundInfo FromStringVcardThreeWithType(string value, StreamReader cardContentReader) { // Get the value - string soundValue = value.Substring(VcardConstants._soundSpecifierWithType.Length); + string soundValue = value.Substring(VcardConstants._soundSpecifier.Length + 1); string[] splitSound = soundValue.Split(VcardConstants._argumentDelimiter); if (splitSound.Length >= 2) throw new InvalidDataException("Sound field must specify exactly two values (Type and arguments, and sound information)"); @@ -251,7 +251,7 @@ internal static SoundInfo FromStringVcardThreeWithType(string value, StreamReade internal static SoundInfo FromStringVcardFourWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) { // Get the value - string soundValue = value.Substring(VcardConstants._soundSpecifierWithType.Length); + string soundValue = value.Substring(VcardConstants._soundSpecifier.Length + 1); string[] splitSound = soundValue.Split(VcardConstants._argumentDelimiter); if (splitSound.Length >= 2) throw new InvalidDataException("Sound field must specify exactly two values (Type and arguments, and sound information)"); diff --git a/VisualCard/Parts/TelephoneInfo.cs b/VisualCard/Parts/TelephoneInfo.cs index 5694739..10ff518 100644 --- a/VisualCard/Parts/TelephoneInfo.cs +++ b/VisualCard/Parts/TelephoneInfo.cs @@ -96,7 +96,7 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._telephoneSpecifierWithType}" + + $"{VcardConstants._telephoneSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactPhoneNumber}"; } @@ -104,7 +104,7 @@ internal string ToStringVcardTwo() internal string ToStringVcardThree() { return - $"{VcardConstants._telephoneSpecifierWithType}" + + $"{VcardConstants._telephoneSpecifier};" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactPhoneNumber}"; } @@ -113,7 +113,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{VcardConstants._telephoneSpecifierWithType}" + + $"{VcardConstants._telephoneSpecifier};" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + $"{ContactPhoneNumber}"; @@ -122,7 +122,7 @@ internal string ToStringVcardFour() internal static TelephoneInfo FromStringVcardTwo(string value) { // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length); + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); // Populate the fields string[] _telephoneTypes = new string[] { "CELL" }; @@ -134,7 +134,7 @@ internal static TelephoneInfo FromStringVcardTwo(string value) internal static TelephoneInfo FromStringVcardTwoWithType(string value) { // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifierWithType.Length); + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); string[] splitTel = telValue.Split(VcardConstants._argumentDelimiter); if (splitTel.Length < 2) throw new InvalidDataException("Telephone field must specify exactly two values (Type (optionally prepended with TYPE=), and phone number)"); @@ -149,7 +149,7 @@ internal static TelephoneInfo FromStringVcardTwoWithType(string value) internal static TelephoneInfo FromStringVcardThree(string value) { // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length); + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); // Populate the fields string[] _telephoneTypes = new string[] { "CELL" }; @@ -161,7 +161,7 @@ internal static TelephoneInfo FromStringVcardThree(string value) internal static TelephoneInfo FromStringVcardThreeWithType(string value) { // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifierWithType.Length); + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); string[] splitTel = telValue.Split(VcardConstants._argumentDelimiter); if (splitTel.Length < 2) throw new InvalidDataException("Telephone field must specify exactly two values (Type (must be prepended with TYPE=), and phone number)"); @@ -176,7 +176,7 @@ internal static TelephoneInfo FromStringVcardThreeWithType(string value) internal static TelephoneInfo FromStringVcardFour(string value, int altId) { // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length); + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); // Populate the fields string[] _telephoneTypes = new string[] { "CELL" }; @@ -188,7 +188,7 @@ internal static TelephoneInfo FromStringVcardFour(string value, int altId) internal static TelephoneInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifierWithType.Length); + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); string[] splitTel = telValue.Split(VcardConstants._argumentDelimiter); if (splitTel.Length < 2) throw new InvalidDataException("Telephone field must specify exactly two values (Type (must be prepended with TYPE=), and phone number)"); diff --git a/VisualCard/Parts/TimeZoneInfo.cs b/VisualCard/Parts/TimeZoneInfo.cs index c80650a..cb6b383 100644 --- a/VisualCard/Parts/TimeZoneInfo.cs +++ b/VisualCard/Parts/TimeZoneInfo.cs @@ -96,14 +96,14 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._timeZoneSpecifier}" + + $"{VcardConstants._timeZoneSpecifier}:" + $"{TimeZone}"; } internal string ToStringVcardThree() { return - $"{VcardConstants._timeZoneSpecifier}" + + $"{VcardConstants._timeZoneSpecifier}:" + $"{TimeZone}"; } @@ -111,7 +111,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{(installAltId ? VcardConstants._timeZoneSpecifierWithType : VcardConstants._timeZoneSpecifier)}" + + $"{VcardConstants._timeZoneSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + $"{TimeZone}"; @@ -119,7 +119,7 @@ internal string ToStringVcardFour() internal static TimeZoneInfo FromStringVcardTwo(string value) { - string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length); + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); string _timeZoneStr = Regex.Unescape(tzValue); TimeZoneInfo _timeZone = new(0, Array.Empty(), Array.Empty(), _timeZoneStr); return _timeZone; @@ -127,7 +127,7 @@ internal static TimeZoneInfo FromStringVcardTwo(string value) internal static TimeZoneInfo FromStringVcardTwoWithType(string value) { - string tzValue = value.Substring(VcardConstants._timeZoneSpecifierWithType.Length); + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); string[] splitTz = tzValue.Split(VcardConstants._argumentDelimiter); if (splitTz.Length < 2) throw new InvalidDataException("Time Zone field must specify exactly two values (VALUE=\"text\" / \"uri\" / \"utc-offset\", and time zone info)"); @@ -144,7 +144,7 @@ internal static TimeZoneInfo FromStringVcardTwoWithType(string value) internal static TimeZoneInfo FromStringVcardThree(string value) { // Get the value - string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length); + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); // Populate the fields string[] _timeZoneTypes = new string[] { "uri-offset" }; @@ -156,7 +156,7 @@ internal static TimeZoneInfo FromStringVcardThree(string value) internal static TimeZoneInfo FromStringVcardThreeWithType(string value) { // Get the value - string tzValue = value.Substring(VcardConstants._timeZoneSpecifierWithType.Length); + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); string[] splitTz = tzValue.Split(VcardConstants._argumentDelimiter); if (splitTz.Length < 2) throw new InvalidDataException("Time Zone field must specify exactly two values (VALUE=\"text\" / \"uri\" / \"utc-offset\", and time zone info)"); @@ -173,7 +173,7 @@ internal static TimeZoneInfo FromStringVcardThreeWithType(string value) internal static TimeZoneInfo FromStringVcardFour(string value, int altId) { // Get the value - string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length); + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); // Populate the fields string[] _timeZoneTypes = new string[] { "uri-offset" }; @@ -185,7 +185,7 @@ internal static TimeZoneInfo FromStringVcardFour(string value, int altId) internal static TimeZoneInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string tzValue = value.Substring(VcardConstants._timeZoneSpecifierWithType.Length); + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); string[] splitTz = tzValue.Split(VcardConstants._argumentDelimiter); if (splitTz.Length < 2) throw new InvalidDataException("Time Zone field must specify exactly two values (VALUE=\"text\" / \"uri\" / \"utc-offset\", and time zone info)"); diff --git a/VisualCard/Parts/TitleInfo.cs b/VisualCard/Parts/TitleInfo.cs index 5bfc78f..8da7b87 100644 --- a/VisualCard/Parts/TitleInfo.cs +++ b/VisualCard/Parts/TitleInfo.cs @@ -90,14 +90,14 @@ public override int GetHashCode() internal string ToStringVcardTwo() { return - $"{VcardConstants._titleSpecifier}" + + $"{VcardConstants._titleSpecifier}:" + $"{ContactTitle}"; } internal string ToStringVcardThree() { return - $"{VcardConstants._titleSpecifier}" + + $"{VcardConstants._titleSpecifier}:" + $"{ContactTitle}"; } @@ -105,7 +105,7 @@ internal string ToStringVcardFour() { bool installAltId = AltId >= 0 && AltArguments.Length > 0; return - $"{(installAltId ? VcardConstants._titleSpecifierWithArguments : VcardConstants._titleSpecifier)}" + + $"{(installAltId ? $"{VcardConstants._titleSpecifier};" : $"{VcardConstants._titleSpecifier}:")}" + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + $"{ContactTitle}"; @@ -114,7 +114,7 @@ internal string ToStringVcardFour() internal static TitleInfo FromStringVcardTwo(string value) { // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length); + string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); // Populate field string _title = Regex.Unescape(titleValue); @@ -125,7 +125,7 @@ internal static TitleInfo FromStringVcardTwo(string value) internal static TitleInfo FromStringVcardThree(string value) { // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length); + string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); // Populate field string _title = Regex.Unescape(titleValue); @@ -136,7 +136,7 @@ internal static TitleInfo FromStringVcardThree(string value) internal static TitleInfo FromStringVcardFour(string value, int altId) { // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length); + string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); // Populate field string _title = Regex.Unescape(titleValue); @@ -147,7 +147,7 @@ internal static TitleInfo FromStringVcardFour(string value, int altId) internal static TitleInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) { // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifierWithArguments.Length); + string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); string[] splitTitleParts = titleValue.Split(VcardConstants._argumentDelimiter); // Populate field