From 15d2bdd2098e8824d690319d01b72ec4bab104b1 Mon Sep 17 00:00:00 2001 From: Aptivi Date: Thu, 28 Mar 2024 12:46:07 +0300 Subject: [PATCH] imp - brk|doc - Condensed parsers --- We've condensed all the vCard parsers so that BaseVcardParser handles all the cases (vCard 2.1, 3.0, 4.0, and 5.0). This is necessary to make Card instances more secure. This is a breaking change involving the card parsers. The only thing remaining is to get rid of an extra step to get all the cards. This way, we would have internalized the card parsers, which were initially reserved for internal use, and we would have made it one-step to getting an array of Cards. --- Type: imp Breaking: True Doc Required: True Part: 1/1 --- VisualCard.ShowContacts/Program.cs | 33 +- VisualCard.Tests/ContactData.cs | 738 +---------------- VisualCard.Tests/ContactDataBogus.cs | 16 +- VisualCard.Tests/ContactParseTests.cs | 37 +- VisualCard/CardTools.cs | 18 +- VisualCard/Parsers/BaseVcardParser.cs | 763 +++++++++++++++++- VisualCard/Parsers/Five/VcardFive.cs | 670 --------------- VisualCard/Parsers/Four/VcardFour.cs | 610 -------------- VisualCard/Parsers/IVcardParser.cs | 14 +- VisualCard/Parsers/Three/VcardThree.cs | 535 ------------ VisualCard/Parsers/Two/VcardTwo.cs | 474 ----------- VisualCard/Parsers/Versioned/VcardFive.cs | 39 + VisualCard/Parsers/Versioned/VcardFour.cs | 39 + VisualCard/Parsers/Versioned/VcardThree.cs | 39 + VisualCard/Parsers/Versioned/VcardTwo.cs | 39 + VisualCard/Parts/AddressInfo.cs | 352 -------- VisualCard/Parts/AgentInfo.cs | 264 ------ VisualCard/Parts/BaseCardPartInfo.cs | 98 +++ VisualCard/Parts/Card.cs | 341 ++++---- VisualCard/Parts/Comparers/PartComparison.cs | 107 +++ VisualCard/Parts/EmailInfo.cs | 291 ------- VisualCard/Parts/Enums/PartsArrayEnum.cs | 100 +++ VisualCard/Parts/Enums/PartsEnum.cs | 36 + VisualCard/Parts/Enums/StringsEnum.cs | 80 ++ VisualCard/Parts/GeoInfo.cs | 222 ----- .../Parts/Implementations/AddressInfo.cs | 241 ++++++ VisualCard/Parts/Implementations/AgentInfo.cs | 183 +++++ .../Parts/Implementations/BirthDateInfo.cs | 168 ++++ .../Parts/Implementations/CategoryInfo.cs | 136 ++++ VisualCard/Parts/Implementations/EmailInfo.cs | 182 +++++ VisualCard/Parts/Implementations/GeoInfo.cs | 171 ++++ VisualCard/Parts/Implementations/ImppInfo.cs | 178 ++++ .../Parts/Implementations/LabelAddressInfo.cs | 182 +++++ VisualCard/Parts/Implementations/LogoInfo.cs | 225 ++++++ VisualCard/Parts/Implementations/NameInfo.cs | 210 +++++ .../{ => Implementations}/NicknameInfo.cs | 168 ++-- .../Parts/Implementations/OrganizationInfo.cs | 199 +++++ VisualCard/Parts/Implementations/PhotoInfo.cs | 225 ++++++ .../Parts/Implementations/RevisionInfo.cs | 140 ++++ .../Parts/{ => Implementations}/RoleInfo.cs | 146 ++-- VisualCard/Parts/Implementations/SoundInfo.cs | 225 ++++++ .../Parts/Implementations/TelephoneInfo.cs | 171 ++++ .../Parts/Implementations/TimeDateZoneInfo.cs | 171 ++++ .../Parts/{ => Implementations}/TitleInfo.cs | 151 ++-- .../Parts/{ => Implementations}/XNameInfo.cs | 183 ++--- VisualCard/Parts/ImppInfo.cs | 222 ----- VisualCard/Parts/LabelAddressInfo.cs | 247 ------ VisualCard/Parts/LogoInfo.cs | 309 ------- VisualCard/Parts/NameInfo.cs | 265 ------ VisualCard/Parts/OrganizationInfo.cs | 276 ------- VisualCard/Parts/PhotoInfo.cs | 309 ------- VisualCard/Parts/SoundInfo.cs | 309 ------- VisualCard/Parts/TelephoneInfo.cs | 223 ----- VisualCard/Parts/TimeZoneInfo.cs | 222 ----- 54 files changed, 4843 insertions(+), 7149 deletions(-) delete mode 100644 VisualCard/Parsers/Five/VcardFive.cs delete mode 100644 VisualCard/Parsers/Four/VcardFour.cs delete mode 100644 VisualCard/Parsers/Three/VcardThree.cs delete mode 100644 VisualCard/Parsers/Two/VcardTwo.cs create mode 100644 VisualCard/Parsers/Versioned/VcardFive.cs create mode 100644 VisualCard/Parsers/Versioned/VcardFour.cs create mode 100644 VisualCard/Parsers/Versioned/VcardThree.cs create mode 100644 VisualCard/Parsers/Versioned/VcardTwo.cs delete mode 100644 VisualCard/Parts/AddressInfo.cs delete mode 100644 VisualCard/Parts/AgentInfo.cs create mode 100644 VisualCard/Parts/BaseCardPartInfo.cs create mode 100644 VisualCard/Parts/Comparers/PartComparison.cs delete mode 100644 VisualCard/Parts/EmailInfo.cs create mode 100644 VisualCard/Parts/Enums/PartsArrayEnum.cs create mode 100644 VisualCard/Parts/Enums/PartsEnum.cs create mode 100644 VisualCard/Parts/Enums/StringsEnum.cs delete mode 100644 VisualCard/Parts/GeoInfo.cs create mode 100644 VisualCard/Parts/Implementations/AddressInfo.cs create mode 100644 VisualCard/Parts/Implementations/AgentInfo.cs create mode 100644 VisualCard/Parts/Implementations/BirthDateInfo.cs create mode 100644 VisualCard/Parts/Implementations/CategoryInfo.cs create mode 100644 VisualCard/Parts/Implementations/EmailInfo.cs create mode 100644 VisualCard/Parts/Implementations/GeoInfo.cs create mode 100644 VisualCard/Parts/Implementations/ImppInfo.cs create mode 100644 VisualCard/Parts/Implementations/LabelAddressInfo.cs create mode 100644 VisualCard/Parts/Implementations/LogoInfo.cs create mode 100644 VisualCard/Parts/Implementations/NameInfo.cs rename VisualCard/Parts/{ => Implementations}/NicknameInfo.cs (54%) create mode 100644 VisualCard/Parts/Implementations/OrganizationInfo.cs create mode 100644 VisualCard/Parts/Implementations/PhotoInfo.cs create mode 100644 VisualCard/Parts/Implementations/RevisionInfo.cs rename VisualCard/Parts/{ => Implementations}/RoleInfo.cs (56%) create mode 100644 VisualCard/Parts/Implementations/SoundInfo.cs create mode 100644 VisualCard/Parts/Implementations/TelephoneInfo.cs create mode 100644 VisualCard/Parts/Implementations/TimeDateZoneInfo.cs rename VisualCard/Parts/{ => Implementations}/TitleInfo.cs (54%) rename VisualCard/Parts/{ => Implementations}/XNameInfo.cs (54%) delete mode 100644 VisualCard/Parts/ImppInfo.cs delete mode 100644 VisualCard/Parts/LabelAddressInfo.cs delete mode 100644 VisualCard/Parts/LogoInfo.cs delete mode 100644 VisualCard/Parts/NameInfo.cs delete mode 100644 VisualCard/Parts/OrganizationInfo.cs delete mode 100644 VisualCard/Parts/PhotoInfo.cs delete mode 100644 VisualCard/Parts/SoundInfo.cs delete mode 100644 VisualCard/Parts/TelephoneInfo.cs delete mode 100644 VisualCard/Parts/TimeZoneInfo.cs diff --git a/VisualCard.ShowContacts/Program.cs b/VisualCard.ShowContacts/Program.cs index 9bebe77..7f4d759 100644 --- a/VisualCard.ShowContacts/Program.cs +++ b/VisualCard.ShowContacts/Program.cs @@ -18,12 +18,13 @@ // using System.Diagnostics; -using Terminaux.Colors; using Terminaux.Colors.Data; using Terminaux.Writer.ConsoleWriters; using VisualCard.Converters; using VisualCard.Parsers; using VisualCard.Parts; +using VisualCard.Parts.Enums; +using VisualCard.Parts.Implementations; namespace VisualCard.ShowContacts { @@ -82,15 +83,15 @@ static void Main(string[] args) } // Show contact information - bool showVcard5Disclaimer = Contacts.Any((card) => card.CardVersion == "5.0"); + bool showVcard5Disclaimer = Contacts.Any((card) => card.CardVersion.ToString(2) == "5.0"); foreach (Card Contact in Contacts) { TextWriterColor.WriteColor("----------------------------", ConsoleColors.Green); - TextWriterColor.WriteColor("Name: {0}", ConsoleColors.Green, Contact.ContactFullName); - TextWriterColor.WriteColor("Revision: {0}", ConsoleColors.Green, Contact.CardRevision); + TextWriterColor.WriteColor("Name: {0}", ConsoleColors.Green, Contact.GetString(StringsEnum.FullName)); + TextWriterColor.WriteColor("Revision: {0}", ConsoleColors.Green, Contact.GetPart(PartsEnum.Revision)); // List names - foreach (NameInfo name in Contact.ContactNames) + foreach (NameInfo name in Contact.GetPartsArray(PartsArrayEnum.Names)) { TextWriterColor.Write("First name: {0}", name.ContactFirstName); TextWriterColor.Write("Last name: {0}", name.ContactLastName); @@ -100,7 +101,7 @@ static void Main(string[] args) } // List titles - foreach (TitleInfo title in Contact.ContactTitles) + foreach (TitleInfo title in Contact.GetPartsArray(PartsArrayEnum.Titles)) { TextWriterColor.Write("Title or Job: {0}", title.ContactTitle); TextWriterColor.Write("ALTID: {0}", title.AltId); @@ -109,7 +110,7 @@ static void Main(string[] args) } // List addresses - foreach (AddressInfo Address in Contact.ContactAddresses) + foreach (AddressInfo Address in Contact.GetPartsArray(PartsArrayEnum.Addresses)) { TextWriterColor.Write("P.O. Box: {0}", Address.PostOfficeBox); TextWriterColor.Write("Extended Address: {0}", Address.ExtendedAddress); @@ -121,14 +122,14 @@ static void Main(string[] args) } // List e-mails - foreach (EmailInfo Email in Contact.ContactMails) + foreach (EmailInfo Email in Contact.GetPartsArray(PartsArrayEnum.Mails)) { TextWriterColor.Write("Email types: {0}", Email.ContactEmailTypes); TextWriterColor.Write("Email address: {0}", Email.ContactEmailAddress); } // List organizations - foreach (OrganizationInfo Organization in Contact.ContactOrganizations) + foreach (OrganizationInfo Organization in Contact.GetPartsArray(PartsArrayEnum.Organizations)) { TextWriterColor.Write("Organization Name: {0}", Organization.Name); TextWriterColor.Write("Organization Unit: {0}", Organization.Unit); @@ -136,14 +137,14 @@ static void Main(string[] args) } // List telephones - foreach (TelephoneInfo Telephone in Contact.ContactTelephones) + foreach (TelephoneInfo Telephone in Contact.GetPartsArray(PartsArrayEnum.Telephones)) { TextWriterColor.Write("Phone types: {0}", Telephone.ContactPhoneTypes); TextWriterColor.Write("Phone number: {0}", Telephone.ContactPhoneNumber); } // List photos - foreach (PhotoInfo Photo in Contact.ContactPhotos) + foreach (PhotoInfo Photo in Contact.GetPartsArray(PartsArrayEnum.Photos)) { TextWriterColor.Write("Photo encoding: {0}", Photo.Encoding); TextWriterColor.Write("Photo type: {0}", Photo.PhotoType); @@ -155,7 +156,7 @@ static void Main(string[] args) } // List roles - foreach (RoleInfo Role in Contact.ContactRoles) + foreach (RoleInfo Role in Contact.GetPartsArray(PartsArrayEnum.Roles)) { TextWriterColor.Write("Role: {0}", Role.ContactRole); TextWriterColor.Write("ALTID: {0}", Role.AltId); @@ -164,10 +165,10 @@ static void Main(string[] args) } // List remaining - TextWriterColor.Write("Contact birthdate: {0}", Contact.ContactBirthdate); - TextWriterColor.Write("Contact mailer: {0}", Contact.ContactMailer); - TextWriterColor.Write("Contact URL: {0}", Contact.ContactURL); - TextWriterColor.Write("Contact Note: {0}", Contact.ContactNotes); + TextWriterColor.Write("Contact birthdate: {0}", Contact.GetPart(PartsEnum.Birthdate)); + TextWriterColor.Write("Contact mailer: {0}", Contact.GetString(StringsEnum.Mailer)); + TextWriterColor.Write("Contact URL: {0}", Contact.GetString(StringsEnum.Url)); + TextWriterColor.Write("Contact Note: {0}", Contact.GetString(StringsEnum.Notes)); // Print VCard string raw = Contact.SaveToString(); diff --git a/VisualCard.Tests/ContactData.cs b/VisualCard.Tests/ContactData.cs index d881d56..4cc7794 100644 --- a/VisualCard.Tests/ContactData.cs +++ b/VisualCard.Tests/ContactData.cs @@ -17,9 +17,7 @@ // along with this program. If not, see . // -using System; using System.Collections.Generic; -using VisualCard.Parts; namespace VisualCard.Tests { @@ -52,32 +50,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardContactShortFromMeCardInstance = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Rick", "Hood", [], [], []) - ], - ContactFullName = "Rick Hood" - }; - - private static readonly Card singleVcardTwoContactShortInstance = new - ( - null, - "2.1" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Rick", "Hood", [], [], []) - ], - ContactFullName = "Rick Hood" - }; #endregion #region singleVcardThreeContactShort @@ -90,19 +62,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardThreeContactShortInstance = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Rick", "Hood", [], [], []) - ], - ContactFullName = "Rick Hood" - }; #endregion #region singleVcardFourContactShort @@ -115,19 +74,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardFourContactShortInstance = new - ( - null, - "4.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Rick", "Hood", [], [], []) - ], - ContactFullName = "Rick Hood" - }; #endregion #region singleVcardFiveContactShort @@ -140,19 +86,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardFiveContactShortInstance = new - ( - null, - "5.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Rick", "Hood", [], [], []) - ], - ContactFullName = "Rick Hood" - }; #endregion #region singleVcardTwoContact @@ -167,42 +100,16 @@ public static class ContactData BEGIN:VCARD VERSION:3.0 FN:John Sanders + NOTE:Note test for VisualCard N:Sanders;John;;; TEL;TYPE=CELL:495-522-3560 ADR;TYPE=HOME:;;Los Angeles;;;;USA EMAIL;TYPE=HOME:john.s@acme.co - NOTE:Note test for VisualCard END:VCARD """ ; - private static readonly Card singleVcardContactInstanceFromMeCard = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "John", "Sanders", [], [], []) - ], - ContactFullName = "John Sanders", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["CELL"], "495-522-3560") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["HOME"], "", "", "Los Angeles", "", "", "", "USA") - ], - ContactNotes = "Note test for VisualCard", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "john.s@acme.co") - ] - }; - private static readonly string singleVcardTwoContact = """ BEGIN:VCARD @@ -222,47 +129,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardTwoContactInstance = new - ( - null, - "2.1" - ) - { - ContactNames = - [ - new NameInfo(0, [], "John", "Sanders", [], [], []) - ], - ContactFullName = "John Sanders", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["CELL"], "495-522-3560") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["HOME"], "", "", "Los Angeles, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Acme Co.", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Product Manager") - ], - ContactNotes = "Note test for VisualCard", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "john.s@acme.co") - ], - ContactXNames = - [ - new XNameInfo(0, [], "PHONETIC-FIRST-NAME", ["Saunders"], []), - new XNameInfo(0, [], "PHONETIC-LAST-NAME", ["John"], []), - new XNameInfo(0, [], "ANDROID-CUSTOM", ["vnd.android.cursor.item/nickname", "JS", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], []), - new XNameInfo(0, [], "AIM", ["john.s"], []), - ] - }; #endregion #region singleVcardThreeContact @@ -285,50 +151,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardThreeContactInstance = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "John", "Sanders", [], [], []) - ], - ContactFullName = "John Sanders", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "495-522-3560") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["home"], "", "", "Los Angeles, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Acme Co.", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Product Manager") - ], - ContactNotes = "Note test for VisualCard", - ContactMails = - [ - new EmailInfo(0, [], ["home"], "john.s@acme.co") - ], - ContactXNames = - [ - new XNameInfo(0, [], "PHONETIC-FIRST-NAME", ["Saunders"], []), - new XNameInfo(0, [], "PHONETIC-LAST-NAME", ["John"], []), - new XNameInfo(0, [], "AIM", ["john.s"], []), - ], - ContactNicknames = - [ - new NicknameInfo(0, [], "JS", ["HOME"]) - ] - }; #endregion #region singleVcardFourContact @@ -351,50 +173,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardFourContactInstance = new - ( - null, - "4.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "John", "Sanders", [], [], []) - ], - ContactFullName = "John Sanders", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "495-522-3560") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["home"], "", "", "Los Angeles, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Acme Co.", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Product Manager") - ], - ContactNotes = "Note test for VisualCard", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "john.s@acme.co") - ], - ContactXNames = - [ - new XNameInfo(0, [], "ANDROID-CUSTOM", ["vnd.android.cursor.item/nickname", "JS", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], []), - new XNameInfo(0, [], "PHONETIC-FIRST-NAME", ["Saunders"], []), - new XNameInfo(0, [], "PHONETIC-LAST-NAME", ["John"], []) - ], - ContactImpps = - [ - new ImppInfo(0, [], "aim:john.s", ["HOME"]) - ] - }; #endregion #region singleVcardFiveContact @@ -418,51 +196,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card singleVcardFiveContactInstance = new - ( - null, - "5.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "John", "Sanders", [], [], []) - ], - ContactFullName = "John Sanders", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "495-522-3560") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["home"], "", "", "Los Angeles, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Acme Co.", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Product Manager") - ], - ContactNotes = "Note test for VisualCard", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "john.s@acme.co") - ], - ContactXNames = - [ - new XNameInfo(0, [], "ANDROID-CUSTOM", ["vnd.android.cursor.item/nickname", "JS", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], []), - new XNameInfo(0, [], "PHONETIC-FIRST-NAME", ["Saunders"], []), - new XNameInfo(0, [], "PHONETIC-LAST-NAME", ["John"], []) - ], - ContactImpps = - [ - new ImppInfo(0, [], "aim:john.s", ["HOME"]) - ], - ContactSortString = "johnsanders" - }; #endregion #region multipleVcardTwoContacts @@ -526,92 +259,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card multipleVcardTwoContactsInstanceOne = singleVcardTwoContactInstance; - private static readonly Card multipleVcardTwoContactsInstanceTwo = new - ( - null, - "2.1" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Sarah", "Santos", [], [], []) - ], - ContactFullName = "Sarah Santos", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["CELL"], "589-210-1059") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["HOME"], "", "", "New York, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Support Scammer Outcry Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Chief Executive Officer") - ], - ContactURL = "https://sso.org/", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "sarah.s@gmail.com"), - new EmailInfo(0, [], ["WORK"], "sarah.s@sso.org"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "SIP", ["sip test"], []), - ] - }; - private static readonly Card multipleVcardTwoContactsInstanceThree = new - ( - null, - "2.1" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Neville", "Navasquillo", ["Neville", "Nevile"], ["Mr."], ["Jr."]) - ], - ContactFullName = "Neville Navasquillo", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["CELL"], "1-234-567-890"), - new TelephoneInfo(0, [], ["WORK"], "098-765-4321"), - new TelephoneInfo(0, [], ["VOICE"], "078-494-6434"), - new TelephoneInfo(0, [], ["HOME"], "348-404-8404"), - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["WORK"], "POBOX", "", "Street Address ExtAddress", "Reg", "Loc", "Postal", "Country"), - new AddressInfo(0, [], ["HOME"], "", "", "Street Address", "", "", "", ""), - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Title") - ], - ContactNotes = "Notes", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "neville.nvs@gmail.com"), - new EmailInfo(0, [], ["WORK"], "neville.nvs@nvsc.com"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "ANDROID-CUSTOM", ["vnd.android.cursor.item/nickname", "NVL.N", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], []), - new XNameInfo(0, [], "AIM", ["IM"], ["HOME"]), - new XNameInfo(0, [], "MSN", ["Windows LIVE"], ["HOME"]), - new XNameInfo(0, [], "YAHOO", ["Yahoo"], ["HOME"]), - ] - }; - private static readonly Card multipleVcardTwoContactsInstanceFour = singleVcardTwoContactShortInstance; #endregion #region multipleVcardThreeContacts @@ -675,95 +322,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card multipleVcardThreeContactsInstanceOne = singleVcardThreeContactInstance; - private static readonly Card multipleVcardThreeContactsInstanceTwo = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Sarah", "Santos", [], [], []) - ], - ContactFullName = "Sarah Santos", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "589-210-1059") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["home"], "", "", "New York, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Support Scammer Outcry Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Chief Executive Officer") - ], - ContactURL = "https://sso.org/", - ContactMails = - [ - new EmailInfo(0, [], ["home"], "sarah.s@gmail.com"), - new EmailInfo(0, [], ["work"], "sarah.s@sso.org"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "SIP", ["sip test"], []), - ] - }; - private static readonly Card multipleVcardThreeContactsInstanceThree = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Neville", "Navasquillo", ["Neville", "Nevile"], ["Mr."], ["Jr."]) - ], - ContactFullName = "Neville Navasquillo", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "1-234-567-890"), - new TelephoneInfo(0, [], ["work"], "098-765-4321"), - new TelephoneInfo(0, [], ["voice"], "078-494-6434"), - new TelephoneInfo(0, [], ["home"], "348-404-8404"), - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["work"], "POBOX", "", "Street Address ExtAddress", "Reg", "Loc", "Postal", "Country"), - new AddressInfo(0, [], ["home"], "", "", "Street Address", "", "", "", ""), - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Title") - ], - ContactNotes = "Notes", - ContactMails = - [ - new EmailInfo(0, [], ["home"], "neville.nvs@gmail.com"), - new EmailInfo(0, [], ["work"], "neville.nvs@nvsc.com"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "AIM", ["IM"], ["HOME"]), - new XNameInfo(0, [], "MSN", ["Windows LIVE"], ["HOME"]), - new XNameInfo(0, [], "YAHOO", ["Yahoo"], ["HOME"]), - ], - ContactNicknames = - [ - new NicknameInfo(0, [], "NVL.N", ["HOME"]) - ] - }; - private static readonly Card multipleVcardThreeContactsInstanceFour = singleVcardThreeContactShortInstance; #endregion #region multipleVcardFourContacts @@ -829,97 +387,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card multipleVcardFourContactsInstanceOne = singleVcardFourContactShortInstance; - private static readonly Card multipleVcardFourContactsInstanceTwo = new - ( - null, - "4.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Neville", "Navasquillo", ["Neville", "Nevile"], ["Mr."], ["Jr."]), - new NameInfo(0, ["LANGUAGE=de"], "Neville", "NAVASQUILLO", ["Neville", "Nevile"], ["Mr."], ["Jr."]) - ], - ContactFullName = "Neville Navasquillo", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["work"], "098-765-4321"), - new TelephoneInfo(0, [], ["cell"], "1-234-567-890"), - new TelephoneInfo(0, [], ["voice"], "078-494-6434"), - new TelephoneInfo(0, [], ["home"], "348-404-8404"), - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["work"], "POBOX", "", "Street Address ExtAddress", "Reg", "Loc", "Postal", "Country"), - new AddressInfo(0, [], ["home"], "", "", "Street Address", "", "", "", ""), - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Title") - ], - ContactNotes = "Notes", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "neville.nvs@gmail.com"), - new EmailInfo(0, [], ["WORK"], "neville.nvs@nvsc.com"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "ANDROID-CUSTOM", ["vnd.android.cursor.item/nickname", "NVL.N", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], []), - ], - ContactImpps = - [ - new ImppInfo(0, [], "aim:IM", ["HOME"]), - new ImppInfo(0, [], "msn:Windows LIVE", ["HOME"]), - new ImppInfo(0, [], "ymsgr:Yahoo", ["HOME"]) - ], - }; - private static readonly Card multipleVcardFourContactsInstanceThree = new - ( - null, - "4.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Sarah", "Santos", [], [], []) - ], - ContactFullName = "Sarah Santos", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "589-210-1059") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["home"], "", "", "New York, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Support Scammer Outcry Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Chief Executive Officer") - ], - ContactURL = "https://sso.org/", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "sarah.s@gmail.com"), - new EmailInfo(0, [], ["WORK"], "sarah.s@sso.org"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "SIP-SIP", ["sip test"], []), - ], - ContactBirthdate = new DateTime(1989, 2, 22), - }; - private static readonly Card multipleVcardFourContactsInstanceFour = singleVcardFourContactInstance; #endregion #region multipleVcardFiveContacts @@ -987,98 +454,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card multipleVcardFiveContactsInstanceOne = singleVcardFiveContactShortInstance; - private static readonly Card multipleVcardFiveContactsInstanceTwo = new - ( - null, - "5.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Neville", "Navasquillo", ["Neville", "Nevile"], ["Mr."], ["Jr."]), - new NameInfo(0, ["LANGUAGE=de"], "Neville", "NAVASQUILLO", ["Neville", "Nevile"], ["Mr."], ["Jr."]) - ], - ContactFullName = "Neville Navasquillo", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["work"], "098-765-4321"), - new TelephoneInfo(0, [], ["cell"], "1-234-567-890"), - new TelephoneInfo(0, [], ["voice"], "078-494-6434"), - new TelephoneInfo(0, [], ["home"], "348-404-8404"), - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["work"], "POBOX", "", "Street Address ExtAddress", "Reg", "Loc", "Postal", "Country"), - new AddressInfo(0, [], ["home"], "", "", "Street Address", "", "", "", ""), - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Title") - ], - ContactNotes = "Notes", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "neville.nvs@gmail.com"), - new EmailInfo(0, [], ["WORK"], "neville.nvs@nvsc.com"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "ANDROID-CUSTOM", ["vnd.android.cursor.item/nickname", "NVL.N", "1", "", "", "", "", "", "", "", "", "", "", "", "", ""], []), - ], - ContactImpps = - [ - new ImppInfo(0, [], "aim:IM", ["HOME"]), - new ImppInfo(0, [], "msn:Windows LIVE", ["HOME"]), - new ImppInfo(0, [], "ymsgr:Yahoo", ["HOME"]) - ], - }; - private static readonly Card multipleVcardFiveContactsInstanceThree = new - ( - null, - "5.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Sarah", "Santos", [], [], []) - ], - ContactFullName = "Sarah Santos", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["cell"], "589-210-1059") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["home"], "", "", "New York, USA", "", "", "", "") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Support Scammer Outcry Organization", "", "", ["WORK"]) - ], - ContactTitles = - [ - new TitleInfo(0, [], "Chief Executive Officer") - ], - ContactURL = "https://sso.org/", - ContactMails = - [ - new EmailInfo(0, [], ["HOME"], "sarah.s@gmail.com"), - new EmailInfo(0, [], ["WORK"], "sarah.s@sso.org"), - ], - ContactXNames = - [ - new XNameInfo(0, [], "SIP-SIP", ["sip test"], []), - ], - ContactBirthdate = new DateTime(1989, 2, 22), - ContactSortString = "sarahsantos" - }; - private static readonly Card multipleVcardFiveContactsInstanceFour = singleVcardFiveContactInstance; #endregion #region vcardThreeOldSample @@ -1112,81 +487,6 @@ public static class ContactData END:VCARD """ ; - - private static readonly Card vcardThreeOldSampleInstanceOne = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Derik", "Stenerson", [], [], []) - ], - ContactFullName = "Derik Stenerson", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["WORK", "MSG"], "+1-425-936-5522"), - new TelephoneInfo(0, [], ["WORK", "FAX"], "+1-425-936-7329") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["WORK", "POSTAL", "PARCEL"], "", "", "One Microsoft Way", "Redmond", "WA", "98052-6399", "USA") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], "Microsoft Corporation", "", "", ["WORK"]) - ], - ContactMails = - [ - new EmailInfo(0, [], ["INTERNET"], "deriks@Microsoft.com") - ], - ContactBirthdate = new DateTime(1963, 9, 21), - }; - private static readonly Card vcardThreeOldSampleInstanceTwo = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Anik", "Ganguly", [], [], []) - ], - ContactFullName = "Anik Ganguly", - ContactTelephones = - [ - new TelephoneInfo(0, [], ["WORK", "MSG"], "+1-734-542-5955") - ], - ContactAddresses = - [ - new AddressInfo(0, [], ["WORK", "POSTAL", "PARCEL"], "", "Suite 101", "38777 West Six Mile Road", "Livonia", "MI", "48152", "USA") - ], - ContactOrganizations = - [ - new OrganizationInfo(0, [], " Open Text Inc.", "", "", ["WORK"]) - ], - ContactMails = - [ - new EmailInfo(0, [], ["INTERNET"], "ganguly@acm.org") - ] - }; - private static readonly Card vcardThreeOldSampleInstanceThree = new - ( - null, - "3.0" - ) - { - ContactNames = - [ - new NameInfo(0, [], "Robert", "Moskowitz", [], [], []) - ], - ContactFullName = "Robert Moskowitz", - ContactMails = - [ - new EmailInfo(0, [], ["INTERNET"], "rgm-ietf@htt-consult.com") - ] - }; #endregion /// @@ -1281,41 +581,5 @@ public static class ContactData vcardThreeOldSample, ] ]; - - /// - /// VCard instances for equality check - /// - public static readonly Card[] vCardContactsInstances = - [ - singleVcardTwoContactShortInstance, - singleVcardThreeContactShortInstance, - singleVcardFourContactShortInstance, - singleVcardFiveContactShortInstance, - singleVcardTwoContactInstance, - singleVcardThreeContactInstance, - singleVcardFourContactInstance, - singleVcardFiveContactInstance, - multipleVcardTwoContactsInstanceOne, - multipleVcardTwoContactsInstanceTwo, - multipleVcardTwoContactsInstanceThree, - multipleVcardTwoContactsInstanceFour, - multipleVcardThreeContactsInstanceOne, - multipleVcardThreeContactsInstanceTwo, - multipleVcardThreeContactsInstanceThree, - multipleVcardThreeContactsInstanceFour, - multipleVcardFourContactsInstanceOne, - multipleVcardFourContactsInstanceTwo, - multipleVcardFourContactsInstanceThree, - multipleVcardFourContactsInstanceFour, - multipleVcardFiveContactsInstanceOne, - multipleVcardFiveContactsInstanceTwo, - multipleVcardFiveContactsInstanceThree, - multipleVcardFiveContactsInstanceFour, - vcardThreeOldSampleInstanceOne, - vcardThreeOldSampleInstanceTwo, - vcardThreeOldSampleInstanceThree, - singleVcardContactInstanceFromMeCard, - singleVcardContactShortFromMeCardInstance, - ]; } } diff --git a/VisualCard.Tests/ContactDataBogus.cs b/VisualCard.Tests/ContactDataBogus.cs index 1895aef..d46181c 100644 --- a/VisualCard.Tests/ContactDataBogus.cs +++ b/VisualCard.Tests/ContactDataBogus.cs @@ -33,6 +33,17 @@ public static class ContactDataBogus """ ; + private static readonly string vcardTwoWithUnsupportedParts = + """ + BEGIN:VCARD + VERSION:2.1 + N:Hood;Rick;;; + CALURI:https://www.rickhood.com/events/calendar?type=MusicHoodFestival2024 + NICKNAME:R.H. + END:VCARD + """ + ; + private static readonly string vcardThreeNoFullName = """ BEGIN:VCARD @@ -104,7 +115,10 @@ public static class ContactDataBogus [ [ vcardTwoNoFullName, - ] + ], + [ + vcardTwoWithUnsupportedParts, + ], ]; /// diff --git a/VisualCard.Tests/ContactParseTests.cs b/VisualCard.Tests/ContactParseTests.cs index de6421f..5b44d48 100644 --- a/VisualCard.Tests/ContactParseTests.cs +++ b/VisualCard.Tests/ContactParseTests.cs @@ -102,19 +102,23 @@ internal void ParseDifferentContactsAndTestEqualityInternal(string cardText) { List parsers = []; List cards = []; + List secondCards = []; // Parse the cards Should.NotThrow(() => parsers = CardTools.GetCardParsersFromString(cardText)); foreach (BaseVcardParser parser in parsers) + { cards.Add(Should.NotThrow(parser.Parse)); + secondCards.Add(Should.NotThrow(parser.Parse)); + } // Test equality with available data List foundCards = []; foreach (Card card in cards) { bool found = false; - foreach (Card expectedCard in ContactData.vCardContactsInstances) - if (expectedCard == card) + foreach (Card second in secondCards) + if (second == card) { found = true; break; @@ -148,12 +152,16 @@ public void ParseDifferentContactsSaveToStringAndTestEqualityInternal(string car { List parsers = []; List cards = []; + List secondCards = []; List savedCards = []; // Parse the cards Should.NotThrow(() => parsers = CardTools.GetCardParsersFromString(cardText)); foreach (BaseVcardParser parser in parsers) + { cards.Add(Should.NotThrow(parser.Parse)); + secondCards.Add(Should.NotThrow(parser.Parse)); + } // Save all the cards to strings and re-parse foreach (Card card in cards) @@ -169,8 +177,8 @@ public void ParseDifferentContactsSaveToStringAndTestEqualityInternal(string car foreach (Card card in savedCards) { bool found = false; - foreach (Card expectedCard in ContactData.vCardContactsInstances) - if (expectedCard == card) + foreach (Card second in secondCards) + if (second == card) { found = true; break; @@ -208,19 +216,23 @@ public void ParseDifferentMeCardContactsAndTestEquality(string cardText) { List parsers = []; List cards = []; + List secondCards = []; // Parse the cards Should.NotThrow(() => parsers = MeCard.GetContactsFromMeCardString(cardText)); foreach (BaseVcardParser parser in parsers) + { cards.Add(Should.NotThrow(parser.Parse)); + secondCards.Add(Should.NotThrow(parser.Parse)); + } // Test equality with available data List foundCards = []; foreach (Card card in cards) { bool found = false; - foreach (Card expectedCard in ContactData.vCardContactsInstances) - if (expectedCard == card) + foreach (Card second in secondCards) + if (second == card) { found = true; break; @@ -236,12 +248,16 @@ public void ParseDifferentMeCardContactsSaveToStringAndTestEquality(string cardT { List parsers = []; List cards = []; + List secondCards = []; List savedCards = []; // Parse the cards Should.NotThrow(() => parsers = MeCard.GetContactsFromMeCardString(cardText)); foreach (BaseVcardParser parser in parsers) + { cards.Add(Should.NotThrow(parser.Parse)); + secondCards.Add(Should.NotThrow(parser.Parse)); + } // Save all the cards to strings and re-parse foreach (Card card in cards) @@ -257,8 +273,8 @@ public void ParseDifferentMeCardContactsSaveToStringAndTestEquality(string cardT foreach (Card card in savedCards) { bool found = false; - foreach (Card expectedCard in ContactData.vCardContactsInstances) - if (expectedCard == card) + foreach (Card second in secondCards) + if (second == card) { found = true; break; @@ -295,7 +311,10 @@ public void BogusButSeemsValidShouldNotThrowWhenParsing(string cardText) List parsers = []; Should.NotThrow(() => parsers = CardTools.GetCardParsersFromString(cardText)); foreach (BaseVcardParser parser in parsers) - Should.NotThrow(parser.Parse); + { + var resultingCard = Should.NotThrow(parser.Parse); + resultingCard.ShouldNotBeNull(); + } } } } diff --git a/VisualCard/CardTools.cs b/VisualCard/CardTools.cs index 7d07f0c..4601da4 100644 --- a/VisualCard/CardTools.cs +++ b/VisualCard/CardTools.cs @@ -21,10 +21,8 @@ using System.IO; using System.Text; using VisualCard.Parsers; -using VisualCard.Parsers.Two; -using VisualCard.Parsers.Three; -using VisualCard.Parsers.Four; -using VisualCard.Parsers.Five; +using System; +using VisualCard.Parsers.Versioned; namespace VisualCard { @@ -75,7 +73,7 @@ public static List GetCardParsers(StreamReader stream) // Parse the lines of the card file string CardLine; StringBuilder CardContent = new(); - string CardVersion = ""; + Version CardVersion = new(); bool CardSawNull = false; CardLine = stream.ReadLine(); while (!stream.EndOfStream) @@ -107,12 +105,16 @@ public static List GetCardParsers(StreamReader stream) if (!CardSawNull) CardLine = stream.ReadLine(); CardSawNull = false; - if (CardLine != "VERSION:2.1" && CardLine != "VERSION:3.0" && CardLine != "VERSION:4.0" && CardLine != "VERSION:5.0" && !VersionSpotted) + if (CardLine != "VERSION:2.1" && + CardLine != "VERSION:3.0" && + CardLine != "VERSION:4.0" && + CardLine != "VERSION:5.0" && + !VersionSpotted) throw new InvalidDataException($"This has an invalid VCard version {CardLine}."); else if (!VersionSpotted) { VersionSpotted = true; - CardVersion = CardLine.Substring(8); + CardVersion = new(CardLine.Substring(8)); } // If the ending tag is spotted, reset everything. @@ -123,7 +125,7 @@ public static List GetCardParsers(StreamReader stream) // Select parser BaseVcardParser CardParser; - switch (CardVersion) + switch (CardVersion.ToString(2)) { case "2.1": CardParser = new VcardTwo(CardContent.ToString(), CardVersion); diff --git a/VisualCard/Parsers/BaseVcardParser.cs b/VisualCard/Parsers/BaseVcardParser.cs index abd856e..ef4fd62 100644 --- a/VisualCard/Parsers/BaseVcardParser.cs +++ b/VisualCard/Parsers/BaseVcardParser.cs @@ -17,34 +17,695 @@ // along with this program. If not, see . // +using System; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Text; +using System.Text.RegularExpressions; +using VisualCard.Exceptions; using VisualCard.Parts; +using VisualCard.Parts.Enums; +using VisualCard.Parts.Implementations; namespace VisualCard.Parsers { /// - /// The base VCard parser + /// The base vCard parser /// - [DebuggerDisplay("VCard contact, version {CardVersion}")] + [DebuggerDisplay("vCard contact, version {CardVersion.ToString()}, expected {ExpectedCardVersion.ToString()}, {CardContent.Length} bytes")] public abstract class BaseVcardParser : IVcardParser { /// /// VCard card content /// - public virtual string CardContent => ""; + public virtual string CardContent { get; internal set; } = ""; /// /// VCard card version /// - public virtual string CardVersion => ""; + public virtual Version CardVersion { get; internal set; } = new(); + /// + /// VCard expected card version + /// + public virtual Version ExpectedCardVersion => new(); /// /// Parses a VCard contact /// /// A strongly-typed instance holding information about the card - public abstract Card Parse(); - internal abstract string SaveToString(Card card); - internal abstract void SaveTo(string path, Card card); + public virtual Card Parse() + { + // Check the version to ensure that we're really dealing with the correct vCard version + if (CardVersion != ExpectedCardVersion) + throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"2.1\"."); + + // Check the content to ensure that we really have data + if (string.IsNullOrEmpty(CardContent)) + throw new InvalidDataException($"Card content is empty."); + + // Now, make a stream out of card content + byte[] CardContentData = Encoding.Default.GetBytes(CardContent); + MemoryStream CardContentStream = new(CardContentData, false); + StreamReader CardContentReader = new(CardContentStream); + + // Make a new vCard + var card = new Card(this); + + // Track the required fields + List expectedFields = []; + List actualFields = []; + switch (CardVersion.ToString(2)) + { + case "2.1": + expectedFields.Add(VcardConstants._nameSpecifier); + break; + case "4.0": + expectedFields.Add(VcardConstants._fullNameSpecifier); + break; + case "3.0": + case "5.0": + expectedFields.Add(VcardConstants._nameSpecifier); + expectedFields.Add(VcardConstants._fullNameSpecifier); + break; + } + + // Iterate through all the lines + int lineNumber = 0; + while (!CardContentReader.EndOfStream) + { + // Get line + 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; + + // Helper function to wrap things around + bool StartsWithPrefix(string prefix) => + _value.StartsWith(prefix + delimiter); + + try + { + // Variables + string[] splitValueParts = _value.Split(VcardConstants._argumentDelimiter); + string[] splitArgs = splitValueParts[0].Split(VcardConstants._fieldDelimiter); + splitArgs = splitArgs.Except([splitArgs[0]]).ToArray(); + string[] splitValues = splitValueParts[1].Split(VcardConstants._fieldDelimiter); + List finalArgs = []; + int altId = 0; + + if (splitArgs.Length > 0) + { + // If we have more than one argument, check for ALTID + if (splitArgs[0].StartsWith(VcardConstants._altIdArgumentSpecifier) && CardVersion.Major >= 4) + { + if (!int.TryParse(splitArgs[0].Substring(VcardConstants._altIdArgumentSpecifier.Length), out altId)) + throw new InvalidDataException("ALTID must be numeric"); + + // Here, we require arguments for ALTID + if (splitArgs.Length <= 1) + throw new InvalidDataException("ALTID must have one or more arguments to specify why is this instance an alternative"); + } + + // Finalize the arguments + finalArgs.AddRange(splitArgs.Except( + splitArgs.Where((arg) => + arg.StartsWith(VcardConstants._altIdArgumentSpecifier) || + arg.StartsWith(VcardConstants._valueArgumentSpecifier) || + arg.StartsWith(VcardConstants._typeArgumentSpecifier) + ) + )); + } + + // The name (N:Sanders;John;;;) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._nameSpecifier) && !actualFields.Contains(VcardConstants._nameSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + NameInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + NameInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Names, partInfo); + + // Set flag to indicate that the required field is spotted + if (expectedFields.Contains(VcardConstants._nameSpecifier)) + actualFields.Add(VcardConstants._nameSpecifier); + } + + // Full name (FN:John Sanders) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._fullNameSpecifier) && !actualFields.Contains(VcardConstants._fullNameSpecifier)) + { + // Get the value + string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); + fullNameValue = Regex.Unescape(fullNameValue); + + // Populate field + card.SetString(StringsEnum.FullName, fullNameValue); + + // Set flag to indicate that the required field is spotted + if (expectedFields.Contains(VcardConstants._fullNameSpecifier)) + actualFields.Add(VcardConstants._fullNameSpecifier); + } + + // Telephone (TEL;CELL;HOME:495-522-3560 or TEL;TYPE=cell,home:495-522-3560 or TEL:495-522-3560) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._telephoneSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + TelephoneInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + TelephoneInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Telephones, partInfo); + } + + // Address (ADR;HOME:;;Los Angeles, USA;;;; or ADR:;;Los Angeles, USA;;;;) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._addressSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + AddressInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + AddressInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Addresses, partInfo); + } + + // Email (EMAIL;HOME;INTERNET:john.s@acme.co or EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._emailSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + EmailInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + EmailInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Mails, partInfo); + } + + // Organization (ORG:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._orgSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + OrganizationInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + OrganizationInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Organizations, partInfo); + } + + // Title (TITLE:Product Manager) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._titleSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + TitleInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + TitleInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Titles, partInfo); + } + + // Website link (URL:https://sso.org/) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._urlSpecifier)) + { + // Get the value + 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)) + throw new InvalidDataException($"URL {urlValue} is invalid"); + + // Populate field + card.SetString(StringsEnum.Url, uri.ToString()); + } + + // Note (NOTE:Product Manager) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._noteSpecifier)) + { + // Get the value + string noteValue = _value.Substring(VcardConstants._noteSpecifier.Length + 1); + noteValue = Regex.Unescape(noteValue); + + // Populate field + card.SetString(StringsEnum.Notes, noteValue); + } + + // Photo (PHOTO;ENCODING=BASE64;JPEG:... or PHOTO;VALUE=URL:file:///jqpublic.gif or PHOTO;ENCODING=BASE64;TYPE=GIF:...) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._photoSpecifier)) + { + if (isWithType) + { + var partInfo = PhotoInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Photos, partInfo); + } + 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 (StartsWithPrefix(VcardConstants._logoSpecifier)) + { + if (isWithType) + { + var partInfo = LogoInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Logos, partInfo); + } + 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:...) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._soundSpecifier)) + { + if (isWithType) + { + var partInfo = SoundInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Sounds, partInfo); + } + else + throw new InvalidDataException("Sound field must not have empty type."); + } + + // Revision (REV:1995-10-31T22:27:10Z or REV:19951031T222710) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._revSpecifier)) + { + var partInfo = RevisionInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.SetPart(PartsEnum.Revision, partInfo); + } + + // Birthdate (BDAY:19950415 or BDAY:1953-10-15T23:10:00Z) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._birthSpecifier)) + { + var partInfo = + isWithType ? + BirthDateInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + BirthDateInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.SetPart(PartsEnum.Birthdate, partInfo); + } + + // Role (ROLE:Programmer) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._roleSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + RoleInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + RoleInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Roles, partInfo); + } + + // Categories (CATEGORIES:INTERNET or CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._categoriesSpecifier)) + { + // Get the value + string categoriesValue = _value.Substring(VcardConstants._categoriesSpecifier.Length + 1); + + // Populate field + var categories = CategoryInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Categories, categories); + } + + // Time Zone (TZ;VALUE=text:-05:00; EST; Raleigh/North America) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._timeZoneSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + TimeDateZoneInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + TimeDateZoneInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.TimeZone, partInfo); + } + + // Geo (GEO;VALUE=uri:https://...) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._geoSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + GeoInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + GeoInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Geo, partInfo); + } + + // Source (SOURCE:http://johndoe.com/vcard.vcf) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._sourceSpecifier)) + { + // Get the value + 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)) + throw new InvalidDataException($"URL {sourceStringValue} is invalid"); + + // Populate field + string sourceString = uri.ToString(); + card.SetString(StringsEnum.Source, sourceString); + } + + // Non-standard names (X-AIM:john.s or X-DL;Design Work Group:List Item 1;List Item 2;List Item 3) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._xSpecifier)) + { + // Get the name + var partInfo = + isWithType ? + XNameInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + XNameInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.NonstandardNames, partInfo); + } + + // Now, the keys that are only available in specific versions of vCard + + // Card type (KIND:individual, KIND:group, KIND:org, KIND:location, ...) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._kindSpecifier) && + StringSupported(StringsEnum.Kind, CardVersion)) + { + // Get the value + string kindValue = _value.Substring(VcardConstants._kindSpecifier.Length + 1); + + // Populate field + if (!string.IsNullOrEmpty(kindValue)) + kindValue = Regex.Unescape(kindValue); + card.SetString(StringsEnum.Kind, kindValue); + + // Let VisualCard know that we've explicitly specified a kind. + card.kindExplicitlySpecified = true; + } + + // Label (LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\, Esq.\nMail Drop: TNE QB\n123 Main Street\nAny Town\, CA 91921 - 1234\nU.S.A.) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._labelSpecifier) && + EnumArrayTypeSupported(PartsArrayEnum.Labels, CardVersion)) + { + // Get the name + var partInfo = + isWithType ? + LabelAddressInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + LabelAddressInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Labels, partInfo); + } + + // Agent (AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1...) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._agentSpecifier) && + EnumArrayTypeSupported(PartsArrayEnum.Agents, CardVersion)) + { + // Get the name + var partInfo = + isWithType ? + AgentInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + AgentInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Agents, partInfo); + } + + // Nickname (NICKNAME;TYPE=cell,home:495-522-3560) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._nicknameSpecifier) && + EnumArrayTypeSupported(PartsArrayEnum.Nicknames, CardVersion)) + { + // Get the name + var partInfo = + isWithType ? + NicknameInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + NicknameInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Nicknames, partInfo); + } + + // Mailer (MAILER:ccMail 2.2 or MAILER:PigeonMail 2.1) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._mailerSpecifier) && + StringSupported(StringsEnum.Mailer, CardVersion)) + { + // Get the value + string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length + 1); + + // Populate field + mailerValue = Regex.Unescape(mailerValue); + card.SetString(StringsEnum.Mailer, mailerValue); + } + + // Product ID (PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._productIdSpecifier) && + StringSupported(StringsEnum.ProductId, CardVersion)) + { + // Get the value + string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length + 1); + + // Populate field + prodIdValue = Regex.Unescape(prodIdValue); + card.SetString(StringsEnum.ProductId, prodIdValue); + } + + // Sort string (SORT-STRING:Harten) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._sortStringSpecifier) && + StringSupported(StringsEnum.SortString, CardVersion)) + { + // Get the value + string sortStringValue = _value.Substring(VcardConstants._sortStringSpecifier.Length + 1); + + // Populate field + sortStringValue = Regex.Unescape(sortStringValue); + card.SetString(StringsEnum.SortString, sortStringValue); + } + + // IMPP information (IMPP;TYPE=home:sip:test) + // ALTID is supported. + if (StartsWithPrefix(VcardConstants._imppSpecifier) && + EnumArrayTypeSupported(PartsArrayEnum.Impps, CardVersion)) + { + // Get the name + var partInfo = + isWithType ? + ImppInfo.FromStringVcardWithTypeStatic(_value, [.. finalArgs], altId, CardVersion, CardContentReader) : + ImppInfo.FromStringVcardStatic(_value, altId, CardVersion, CardContentReader); + card.AddPartToArray(PartsArrayEnum.Impps, partInfo); + } + + // Access classification (CLASS:PUBLIC, CLASS:PRIVATE, or CLASS:CONFIDENTIAL) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._classSpecifier) && + StringSupported(StringsEnum.AccessClassification, CardVersion)) + { + // Get the value + string classValue = _value.Substring(VcardConstants._classSpecifier.Length + 1); + + // Populate field + classValue = Regex.Unescape(classValue); + card.SetString(StringsEnum.AccessClassification, classValue); + } + + // XML code (XML:Not an xCard XML element) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._xmlSpecifier) && + StringSupported(StringsEnum.Xml, CardVersion)) + { + // Get the value + string xmlStringValue = _value.Substring(VcardConstants._xmlSpecifier.Length + 1); + + // Populate field + xmlStringValue = Regex.Unescape(xmlStringValue); + card.SetString(StringsEnum.Xml, xmlStringValue); + } + + // Free/busy URL (FBURL:http://example.com/fb/jdoe) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._fbUrlSpecifier) && + StringSupported(StringsEnum.FreeBusyUrl, CardVersion)) + { + // Get the value + 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)) + throw new InvalidDataException($"URL {fbUrlStringValue} is invalid"); + + // Populate field + fbUrlStringValue = uri.ToString(); + card.SetString(StringsEnum.FreeBusyUrl, fbUrlStringValue); + } + + // Calendar URL (CALURI:http://example.com/calendar/jdoe) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._calUriSpecifier) && + StringSupported(StringsEnum.CalendarUrl, CardVersion)) + { + // Get the value + 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)) + throw new InvalidDataException($"URL {calUriStringValue} is invalid"); + + // Populate field + calUriStringValue = uri.ToString(); + card.SetString(StringsEnum.CalendarUrl, calUriStringValue); + } + + // Calendar Request URL (CALADRURI:http://example.com/calendar/jdoe/request) + // Here, we don't support ALTID. + if (StartsWithPrefix(VcardConstants._caladrUriSpecifier) && + StringSupported(StringsEnum.CalendarSchedulingRequestUrl, CardVersion)) + { + // Get the value + 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)) + throw new InvalidDataException($"URL {caladrUriStringValue} is invalid"); + + // Populate field + caladrUriStringValue = uri.ToString(); + card.SetString(StringsEnum.CalendarSchedulingRequestUrl, caladrUriStringValue); + } + } + catch (Exception ex) + { + throw new VCardParseException(ex.Message, _value, lineNumber, ex); + } + } + + // Requirement checks + actualFields.Sort(); + expectedFields.Sort(); + if (!actualFields.SequenceEqual(expectedFields)) + throw new InvalidDataException($"The following keys [{string.Join(", ", expectedFields)}] are required. Got [{string.Join(", ", actualFields)}]."); + + // Return this card + return card; + } + + /// + /// Saves a parsed card to the string + /// + /// Parsed card + public virtual string SaveToString(Card card) + { + // Verify the card data + VerifyCardData(); + + // Initialize the card builder + var cardBuilder = new StringBuilder(); + var version = card.CardVersion; + + // First, write the header + cardBuilder.AppendLine("BEGIN:VCARD"); + cardBuilder.AppendLine($"VERSION:{version}"); + + // Then, enumerate all the strings + StringsEnum[] stringEnums = (StringsEnum[])Enum.GetValues(typeof(StringsEnum)); + foreach (StringsEnum stringEnum in stringEnums) + { + // Get the string value + string stringValue = card.GetString(stringEnum); + if (string.IsNullOrEmpty(stringValue)) + continue; + + // Check to see if kind is specified + if (!card.kindExplicitlySpecified && stringEnum == StringsEnum.Kind) + continue; + + // Now, locate the prefix and assemble the line + string prefix = stringEnum switch + { + StringsEnum.AccessClassification => VcardConstants._classSpecifier, + StringsEnum.CalendarSchedulingRequestUrl => VcardConstants._caladrUriSpecifier, + StringsEnum.CalendarUrl => VcardConstants._calUriSpecifier, + StringsEnum.FreeBusyUrl => VcardConstants._fbUrlSpecifier, + StringsEnum.FullName => VcardConstants._fullNameSpecifier, + StringsEnum.Kind => VcardConstants._kindSpecifier, + StringsEnum.Mailer => VcardConstants._mailerSpecifier, + StringsEnum.Notes => VcardConstants._noteSpecifier, + StringsEnum.ProductId => VcardConstants._productIdSpecifier, + StringsEnum.SortString => VcardConstants._sortStringSpecifier, + StringsEnum.Source => VcardConstants._sourceSpecifier, + StringsEnum.Url => VcardConstants._urlSpecifier, + StringsEnum.Xml => VcardConstants._xmlSpecifier, + _ => throw new NotImplementedException($"String enumeration {stringEnum} is not implemented.") + }; + cardBuilder.AppendLine($"{prefix}:{stringValue}"); + } + + // Next, enumerate all the arrays + PartsArrayEnum[] partsArrayEnums = (PartsArrayEnum[])Enum.GetValues(typeof(PartsArrayEnum)); + foreach (PartsArrayEnum partsArrayEnum in partsArrayEnums) + { + // Get the array value + var array = card.GetPartsArray(partsArrayEnum); + if (array is null || array.Length == 0) + continue; + + // Now, assemble the line + foreach (var part in array) + cardBuilder.AppendLine($"{part.ToStringVcardInternal(version)}"); + } + + // Finally, enumerate all the parts + PartsEnum[] partsEnums = (PartsEnum[])Enum.GetValues(typeof(PartsEnum)); + foreach (PartsEnum partsEnum in partsEnums) + { + // Get the part value + var part = card.GetPart(partsEnum); + if (part is null) + continue; + + // Now, assemble the line + cardBuilder.AppendLine($"{part.ToStringVcardInternal(version)}"); + } + + // End the card and return it + cardBuilder.AppendLine("END:VCARD"); + return cardBuilder.ToString(); + } + + /// + /// Saves a parsed card to a file path + /// + /// File path to save the card to + /// Parsed card + public void SaveTo(string path, Card card) + { + // Verify the card data + VerifyCardData(); + + // Save all the changes to the file + var cardString = SaveToString(card); + File.WriteAllText(path, cardString); + } + + internal void VerifyCardData() + { + // Check the version to ensure that we're really dealing with VCard 2.1 contact + if (CardVersion != ExpectedCardVersion) + throw new InvalidDataException($"Card version {CardVersion} doesn't match expected {ExpectedCardVersion}."); + + // Check the content to ensure that we really have data + if (string.IsNullOrEmpty(CardContent)) + throw new InvalidDataException($"Card content is empty."); + } internal static string MakeStringBlock(string target, int firstLength) { @@ -69,5 +730,93 @@ internal static string MakeStringBlock(string target, int firstLength) } return block.ToString(); } + + internal static bool StringSupported(StringsEnum stringsEnum, Version cardVersion) => + stringsEnum switch + { + StringsEnum.FullName => true, + StringsEnum.Url => true, + StringsEnum.Notes => true, + StringsEnum.Source => true, + StringsEnum.Kind => cardVersion.Major >= 4, + StringsEnum.Mailer => cardVersion.Major != 4, + StringsEnum.ProductId => cardVersion.Major >= 3, + StringsEnum.SortString => cardVersion.Major == 3 || cardVersion.Major == 5, + StringsEnum.AccessClassification => cardVersion.Major != 2 || cardVersion.Major != 4, + StringsEnum.Xml => cardVersion.Major == 4, + StringsEnum.FreeBusyUrl => cardVersion.Major >= 4, + StringsEnum.CalendarUrl => cardVersion.Major >= 4, + StringsEnum.CalendarSchedulingRequestUrl => cardVersion.Major >= 4, + _ => + throw new InvalidOperationException("Invalid string enumeration type to get supported value"), + }; + + internal static bool EnumArrayTypeSupported(PartsArrayEnum partsArrayEnum, Version cardVersion) => + partsArrayEnum switch + { + PartsArrayEnum.Names => true, + PartsArrayEnum.Telephones => true, + PartsArrayEnum.Addresses => true, + PartsArrayEnum.Mails => true, + PartsArrayEnum.Organizations => true, + PartsArrayEnum.Titles => true, + PartsArrayEnum.Photos => true, + PartsArrayEnum.Roles => true, + PartsArrayEnum.Logos => true, + PartsArrayEnum.TimeZone => true, + PartsArrayEnum.Geo => true, + PartsArrayEnum.Sounds => true, + PartsArrayEnum.Categories => true, + PartsArrayEnum.NonstandardNames => true, + PartsArrayEnum.Impps => cardVersion.Major >= 3, + PartsArrayEnum.Nicknames => cardVersion.Major >= 3, + PartsArrayEnum.Labels => cardVersion.Major != 4, + PartsArrayEnum.Agents => cardVersion.Major != 4, + _ => + throw new InvalidOperationException("Invalid parts array enumeration type to get supported value"), + }; + + internal static bool EnumTypeSupported(PartsEnum partsEnum) => + partsEnum switch + { + PartsEnum.Revision => true, + PartsEnum.Birthdate => true, + _ => + throw new InvalidOperationException("Invalid parts enumeration type to get supported value"), + }; + + internal static Type GetEnumArrayType(PartsArrayEnum partsArrayEnum) => + partsArrayEnum switch + { + PartsArrayEnum.Names => typeof(NameInfo), + PartsArrayEnum.Telephones => typeof(TelephoneInfo), + PartsArrayEnum.Addresses => typeof(AddressInfo), + PartsArrayEnum.Labels => typeof(LabelAddressInfo), + PartsArrayEnum.Agents => typeof(AgentInfo), + PartsArrayEnum.Mails => typeof(EmailInfo), + PartsArrayEnum.Organizations => typeof(OrganizationInfo), + PartsArrayEnum.Titles => typeof(TitleInfo), + PartsArrayEnum.Photos => typeof(PhotoInfo), + PartsArrayEnum.Nicknames => typeof(NicknameInfo), + PartsArrayEnum.Roles => typeof(RoleInfo), + PartsArrayEnum.Logos => typeof(LogoInfo), + PartsArrayEnum.TimeZone => typeof(TimeDateZoneInfo), + PartsArrayEnum.Geo => typeof(GeoInfo), + PartsArrayEnum.Sounds => typeof(SoundInfo), + PartsArrayEnum.Impps => typeof(ImppInfo), + PartsArrayEnum.Categories => typeof(CategoryInfo), + PartsArrayEnum.NonstandardNames => typeof(XNameInfo), + _ => + throw new InvalidOperationException("Invalid parts array enumeration type"), + }; + + internal static Type GetEnumType(PartsEnum partsEnum) => + partsEnum switch + { + PartsEnum.Revision => typeof(RevisionInfo), + PartsEnum.Birthdate => typeof(BirthDateInfo), + _ => + throw new InvalidOperationException("Invalid parts enumeration type"), + }; } } diff --git a/VisualCard/Parsers/Five/VcardFive.cs b/VisualCard/Parsers/Five/VcardFive.cs deleted file mode 100644 index 2452e02..0000000 --- a/VisualCard/Parsers/Five/VcardFive.cs +++ /dev/null @@ -1,670 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using VisualCard.Exceptions; -using VisualCard.Parts; -using TimeZoneInfo = VisualCard.Parts.TimeZoneInfo; - -namespace VisualCard.Parsers.Five -{ - /// - /// Parser for VCard version 5.0. Consult the vcard-40-rfc6350.txt file in source for the specification. - /// - public class VcardFive : BaseVcardParser, IVcardParser - { - /// - public override string CardContent { get; } - /// - public override string CardVersion { get; } - - /// - public override Card Parse() - { - // Check the version to ensure that we're really dealing with VCard 5.0 contact - if (CardVersion != "5.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"5.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Now, make a stream out of card content - byte[] CardContentData = Encoding.Default.GetBytes(CardContent); - MemoryStream CardContentStream = new(CardContentData, false); - StreamReader CardContentReader = new(CardContentStream); - - // Some variables to assign to the Card() ctor - string _kind = "individual"; - string _fullName = ""; - string _url = ""; - string _note = ""; - string _prodId = ""; - string _sortString = ""; - string _source = ""; - string _fbUrl = ""; - string _calUri = ""; - string _caladrUri = ""; - string _class = ""; - string _mailer = ""; - DateTime _rev = DateTime.MinValue; - DateTime _bday = DateTime.MinValue; - List _names = []; - List _telephones = []; - List _emails = []; - List _addresses = []; - List _labels = []; - List _orgs = []; - List _titles = []; - List _logos = []; - List _photos = []; - List _sounds = []; - List _nicks = []; - List _roles = []; - List _categories = []; - List _timezones = []; - List _geos = []; - List _impps = []; - List _agents = []; - List _xes = []; - - // Name and Full Name specifiers are required - bool nameSpecifierSpotted = false; - bool fullNameSpecifierSpotted = false; - - // Flags - bool idReservedForName = false; - - // Iterate through all the lines - int lineNumber = 0; - while (!CardContentReader.EndOfStream) - { - // Get line - 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 - string[] splitValueParts = _value.Split(VcardConstants._argumentDelimiter); - string[] splitArgs = splitValueParts[0].Split(VcardConstants._fieldDelimiter); - splitArgs = splitArgs.Except([splitArgs[0]]).ToArray(); - string[] splitValues = splitValueParts[1].Split(VcardConstants._fieldDelimiter); - List finalArgs = []; - int altId = 0; - - if (splitArgs.Length > 0) - { - // If we have more than one argument, check for ALTID - if (splitArgs[0].StartsWith(VcardConstants._altIdArgumentSpecifier)) - { - if (!int.TryParse(splitArgs[0].Substring(VcardConstants._altIdArgumentSpecifier.Length), out altId)) - throw new InvalidDataException("ALTID must be numeric"); - - // Here, we require arguments for ALTID - if (splitArgs.Length <= 1) - throw new InvalidDataException("ALTID must have one or more arguments to specify why is this instance an alternative"); - } - - // Finalize the arguments - finalArgs.AddRange(splitArgs.Except( - splitArgs.Where((arg) => - arg.StartsWith(VcardConstants._altIdArgumentSpecifier) || - arg.StartsWith(VcardConstants._valueArgumentSpecifier) || - arg.StartsWith(VcardConstants._typeArgumentSpecifier) - ) - )); - } - - // Card type (KIND:individual, KIND:group, KIND:org, KIND:location, ...) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._kindSpecifier + delimiter)) - { - // Get the value - string kindValue = _value.Substring(VcardConstants._kindSpecifier.Length + 1); - - // Populate field - if (!string.IsNullOrEmpty(kindValue)) - _kind = Regex.Unescape(kindValue); - } - - // The name (N:Sanders;John;;; or N;ALTID=1;LANGUAGE=en:Sanders;John;;;) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._nameSpecifier + delimiter)) - { - // Get the name - if (isWithType) - _names.Add(NameInfo.FromStringVcardFiveWithType(_value, splitArgs, finalArgs, altId, _names, idReservedForName)); - else - _names.Add(NameInfo.FromStringVcardFive(splitValues, idReservedForName)); - - // Set flag to indicate that the required field is spotted - nameSpecifierSpotted = true; - - // Since we've reserved id 0, set the flag - idReservedForName = true; - } - - // Full name (FN:John Sanders) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) - { - // Get the value - string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); - - // Populate field - _fullName = Regex.Unescape(fullNameValue); - - // Set flag to indicate that the required field is spotted - fullNameSpecifierSpotted = true; - } - - // 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.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _telephones.Add(TelephoneInfo.FromStringVcardFive(_value, altId)); - } - - // Address (ADR;TYPE=HOME:;;Los Angeles, USA;;;; or ADR:;;Los Angeles, USA;;;;) - if (_value.StartsWith(VcardConstants._addressSpecifier + delimiter)) - { - if (isWithType) - _addresses.Add(AddressInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _addresses.Add(AddressInfo.FromStringVcardFive(_value, altId)); - } - - // Label (LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\, Esq.\nMail Drop: TNE QB\n123 Main Street\nAny Town\, CA 91921 - 1234\nU.S.A.) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._labelSpecifier + delimiter)) - { - if (isWithType) - _labels.Add(LabelAddressInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _labels.Add(LabelAddressInfo.FromStringVcardFive(_value, altId)); - } - - // Agent (AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1...) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._agentSpecifier + delimiter)) - { - if (isWithType) - _agents.Add(AgentInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _agents.Add(AgentInfo.FromStringVcardFive(_value, altId)); - } - - // Email (EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._emailSpecifier + delimiter)) - { - if (isWithType) - _emails.Add(EmailInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _emails.Add(EmailInfo.FromStringVcardFive(_value, altId)); - } - - // Organization (ORG:Acme Co. or ORG:ABC, Inc.;North American Division;Marketing) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._orgSpecifier + delimiter)) - { - if (isWithType) - _orgs.Add(OrganizationInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _orgs.Add(OrganizationInfo.FromStringVcardFive(_value, altId)); - } - - // Title (TITLE:Product Manager) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._titleSpecifier + delimiter)) - { - if (isWithType) - _titles.Add(TitleInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _titles.Add(TitleInfo.FromStringVcardFive(_value, altId)); - } - - // Website link (URL:https://sso.org/) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._urlSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {urlValue} is invalid"); - - // Populate field - _url = uri.ToString(); - } - - // Note (NOTE:Product Manager) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) - { - // Get the value - 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:...) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._photoSpecifier + delimiter)) - { - if (isWithType) - _photos.Add(PhotoInfo.FromStringVcardFiveWithType(_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._logoSpecifier + delimiter)) - { - if (isWithType) - _logos.Add(LogoInfo.FromStringVcardFiveWithType(_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._soundSpecifier + delimiter)) - { - if (isWithType) - _sounds.Add(SoundInfo.FromStringVcardFiveWithType(_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 + delimiter)) - { - // Get the value - string revValue = _value.Substring(VcardConstants._revSpecifier.Length + 1); - - // Populate field - _rev = DateTime.Parse(revValue); - } - - // Nickname (NICKNAME;TYPE=cell,home:495-522-3560) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._nicknameSpecifier + delimiter)) - { - if (isWithType) - _nicks.Add(NicknameInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _nicks.Add(NicknameInfo.FromStringVcardFive(_value, altId)); - } - - // Birthdate (BDAY:19950415 or BDAY:1953-10-15T23:10:00Z) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._birthSpecifier + delimiter)) - { - // 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); - } - - // Role (ROLE:Programmer) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._roleSpecifier + delimiter)) - { - if (isWithType) - _roles.Add(RoleInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _roles.Add(RoleInfo.FromStringVcardFive(_value, altId)); - } - - // Categories (CATEGORIES:INTERNET or CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._categoriesSpecifier + delimiter)) - { - // Get the value - 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) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._productIdSpecifier + delimiter)) - { - // Get the value - string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length + 1); - - // Populate field - _prodId = Regex.Unescape(prodIdValue); - } - - // Sort string (SORT-STRING:Harten) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._sortStringSpecifier + delimiter)) - { - // Get the value - 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) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._timeZoneSpecifier + delimiter)) - { - if (isWithType) - _timezones.Add(TimeZoneInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _timezones.Add(TimeZoneInfo.FromStringVcardFive(_value, altId)); - } - - // Geo (GEO;VALUE=uri:https://...) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._geoSpecifier + delimiter)) - { - if (isWithType) - _geos.Add(GeoInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _geos.Add(GeoInfo.FromStringVcardFive(_value, altId)); - } - - // IMPP information (IMPP;TYPE=home:sip:test) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._imppSpecifier + delimiter)) - { - if (isWithType) - _impps.Add(ImppInfo.FromStringVcardFiveWithType(_value, finalArgs, altId)); - else - _impps.Add(ImppInfo.FromStringVcardFive(_value, altId)); - } - - // Source (SOURCE:http://johndoe.com/vcard.vcf) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._sourceSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {sourceStringValue} is invalid"); - - // Populate field - _source = uri.ToString(); - } - - // Mailer (MAILER:ccMail 2.2 or MAILER:PigeonMail 2.1) - if (_value.StartsWith(VcardConstants._mailerSpecifier + delimiter)) - { - // Get the value - string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length + 1); - - // Populate field - _mailer = Regex.Unescape(mailerValue); - } - - // Free/busy URL (FBURL:http://example.com/fb/jdoe) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._fbUrlSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {fbUrlStringValue} is invalid"); - - // Populate field - _fbUrl = uri.ToString(); - } - - // Calendar URL (CALURI:http://example.com/calendar/jdoe) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._calUriSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {calUriStringValue} is invalid"); - - // Populate field - _calUri = uri.ToString(); - } - - // Calendar Request URL (CALADRURI:http://example.com/calendar/jdoe) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._caladrUriSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {caladrUriStringValue} is invalid"); - - // Populate field - _caladrUri = uri.ToString(); - } - - // Class (CLASS:PUBLIC, CLASS:PRIVATE, or CLASS:CONFIDENTIAL) - if (_value.StartsWith(VcardConstants._classSpecifier + delimiter)) - { - // Get the value - string classValue = _value.Substring(VcardConstants._classSpecifier.Length + 1); - - // Populate field - _class = Regex.Unescape(classValue); - } - - // X-nonstandard (X-AIM:john.s or X-DL;Design Work Group:List Item 1;List Item 2;List Item 3) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._xSpecifier)) - _xes.Add(XNameInfo.FromStringVcardFive(_value, finalArgs, altId)); - } - catch (Exception ex) - { - throw new VCardParseException(ex.Message, _value, lineNumber, ex); - } - } - - // Requirement checks - if (!nameSpecifierSpotted) - throw new InvalidDataException("The name specifier, \"N:\", is required."); - if (!fullNameSpecifierSpotted) - throw new InvalidDataException("The full name specifier, \"FN:\", is required."); - - // Make a new instance of the card - return new Card(this, CardVersion, _kind) - { - CardRevision = _rev, - ContactNames = [.. _names], - ContactFullName = _fullName, - ContactTelephones = [.. _telephones], - ContactAddresses = [.. _addresses], - ContactLabels = [.. _labels], - ContactAgents = [.. _agents], - ContactOrganizations = [.. _orgs], - ContactTitles = [.. _titles], - ContactURL = _url, - ContactNotes = _note, - ContactMails = [.. _emails], - ContactXNames = [.. _xes], - ContactPhotos = [.. _photos], - ContactNicknames = [.. _nicks], - ContactBirthdate = _bday, - ContactMailer = _mailer, - ContactRoles = [.. _roles], - ContactCategories = [.. _categories], - ContactLogos = [.. _logos], - ContactProdId = _prodId, - ContactSortString = _sortString, - ContactTimeZone = [.. _timezones], - ContactGeo = [.. _geos], - ContactSounds = [.. _sounds], - ContactImpps = [.. _impps], - ContactSource = _source, - ContactFreeBusyUrl = _fbUrl, - ContactCalendarUrl = _calUri, - ContactCalendarSchedulingRequestUrl = _caladrUri, - ContactAccessClassification = _class - }; - } - - internal override string SaveToString(Card card) - { - // Check the version to ensure that we're really dealing with VCard 5.0 contact - if (CardVersion != "5.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"5.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Initialize the card builder - var cardBuilder = new StringBuilder(); - - // First, write the header - cardBuilder.AppendLine("BEGIN:VCARD"); - cardBuilder.AppendLine($"VERSION:{CardVersion}"); - 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}"); - foreach (NameInfo name in card.ContactNames) - cardBuilder.AppendLine(name.ToStringVcardFive()); - - // Now, start filling in the rest... - foreach (TelephoneInfo telephone in card.ContactTelephones) - cardBuilder.AppendLine(telephone.ToStringVcardFive()); - foreach (AddressInfo address in card.ContactAddresses) - cardBuilder.AppendLine(address.ToStringVcardFive()); - foreach (LabelAddressInfo label in card.ContactLabels) - cardBuilder.AppendLine(label.ToStringVcardFive()); - foreach (AgentInfo agent in card.ContactAgents) - cardBuilder.AppendLine(agent.ToStringVcardFive()); - foreach (EmailInfo email in card.ContactMails) - cardBuilder.AppendLine(email.ToStringVcardFive()); - foreach (OrganizationInfo organization in card.ContactOrganizations) - cardBuilder.AppendLine(organization.ToStringVcardFive()); - foreach (TitleInfo title in card.ContactTitles) - cardBuilder.AppendLine(title.ToStringVcardFive()); - if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); - if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); - foreach (PhotoInfo photo in card.ContactPhotos) - cardBuilder.AppendLine(photo.ToStringVcardFive()); - foreach (LogoInfo logo in card.ContactLogos) - cardBuilder.AppendLine(logo.ToStringVcardFive()); - foreach (SoundInfo sound in card.ContactSounds) - cardBuilder.AppendLine(sound.ToStringVcardFive()); - if (card.CardRevision is not null && card.CardRevision != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._revSpecifier}:{card.CardRevision:yyyy-MM-dd HH:mm:ss}"); - foreach (NicknameInfo nickname in card.ContactNicknames) - cardBuilder.AppendLine(nickname.ToStringVcardFive()); - if (card.ContactBirthdate is not null && card.ContactBirthdate != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}:{card.ContactBirthdate:yyyy-MM-dd}"); - if (!string.IsNullOrWhiteSpace(card.ContactMailer)) - cardBuilder.AppendLine($"{VcardConstants._mailerSpecifier}:{card.ContactMailer}"); - foreach (RoleInfo role in card.ContactRoles) - cardBuilder.AppendLine(role.ToStringVcardFive()); - if (card.ContactCategories is not null && card.ContactCategories.Length > 0) - cardBuilder.AppendLine($"{VcardConstants._categoriesSpecifier}:{string.Join(",", card.ContactCategories)}"); - if (!string.IsNullOrWhiteSpace(card.ContactProdId)) - cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}:{card.ContactProdId}"); - if (!string.IsNullOrWhiteSpace(card.ContactSortString)) - cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}:{card.ContactSortString}"); - foreach (TimeZoneInfo timeZone in card.ContactTimeZone) - cardBuilder.AppendLine(timeZone.ToStringVcardFive()); - foreach (GeoInfo geo in card.ContactGeo) - cardBuilder.AppendLine(geo.ToStringVcardFive()); - foreach (ImppInfo impp in card.ContactImpps) - cardBuilder.AppendLine(impp.ToStringVcardFive()); - if (!string.IsNullOrWhiteSpace(card.ContactAccessClassification)) - cardBuilder.AppendLine($"{VcardConstants._classSpecifier}:{card.ContactAccessClassification}"); - foreach (XNameInfo xname in card.ContactXNames) - cardBuilder.AppendLine(xname.ToStringVcardFive()); - - // Finally, end the card and return it - cardBuilder.AppendLine("END:VCARD"); - return cardBuilder.ToString(); - } - - internal override void SaveTo(string path, Card card) - { - // Check the version to ensure that we're really dealing with VCard 5.0 contact - if (CardVersion != "5.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"5.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Save all the changes to the file - var cardString = SaveToString(card); - File.WriteAllText(path, cardString); - } - - internal VcardFive(string cardContent, string cardVersion) - { - CardContent = cardContent; - CardVersion = cardVersion; - } - } -} diff --git a/VisualCard/Parsers/Four/VcardFour.cs b/VisualCard/Parsers/Four/VcardFour.cs deleted file mode 100644 index ca3839f..0000000 --- a/VisualCard/Parsers/Four/VcardFour.cs +++ /dev/null @@ -1,610 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using VisualCard.Exceptions; -using VisualCard.Parts; -using TimeZoneInfo = VisualCard.Parts.TimeZoneInfo; - -namespace VisualCard.Parsers.Four -{ - /// - /// Parser for VCard version 4.0. Consult the vcard-40-rfc6350.txt file in source for the specification. - /// - public class VcardFour : BaseVcardParser, IVcardParser - { - /// - public override string CardContent { get; } - /// - public override string CardVersion { get; } - - /// - public override Card Parse() - { - // Check the version to ensure that we're really dealing with VCard 4.0 contact - if (CardVersion != "4.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"4.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Now, make a stream out of card content - byte[] CardContentData = Encoding.Default.GetBytes(CardContent); - MemoryStream CardContentStream = new(CardContentData, false); - StreamReader CardContentReader = new(CardContentStream); - - // Some variables to assign to the Card() ctor - string _kind = "individual"; - string _fullName = ""; - string _url = ""; - string _note = ""; - string _prodId = ""; - string _source = ""; - string _xml = ""; - string _fbUrl = ""; - string _calUri = ""; - string _caladrUri = ""; - DateTime _rev = DateTime.MinValue; - DateTime _bday = DateTime.MinValue; - List _names = []; - List _telephones = []; - List _emails = []; - List _addresses = []; - List _orgs = []; - List _titles = []; - List _logos = []; - List _photos = []; - List _sounds = []; - List _nicks = []; - List _roles = []; - List _categories = []; - List _timezones = []; - List _geos = []; - List _impps = []; - List _xes = []; - - // Full Name specifier is required - bool fullNameSpecifierSpotted = false; - - // Flags - bool idReservedForName = false; - - // Iterate through all the lines - int lineNumber = 0; - while (!CardContentReader.EndOfStream) - { - // Get line - 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 - string[] splitValueParts = _value.Split(VcardConstants._argumentDelimiter); - string[] splitArgs = splitValueParts[0].Split(VcardConstants._fieldDelimiter); - splitArgs = splitArgs.Except([splitArgs[0]]).ToArray(); - string[] splitValues = splitValueParts[1].Split(VcardConstants._fieldDelimiter); - List finalArgs = []; - int altId = 0; - - if (splitArgs.Length > 0) - { - // If we have more than one argument, check for ALTID - if (splitArgs[0].StartsWith(VcardConstants._altIdArgumentSpecifier)) - { - if (!int.TryParse(splitArgs[0].Substring(VcardConstants._altIdArgumentSpecifier.Length), out altId)) - throw new InvalidDataException("ALTID must be numeric"); - - // Here, we require arguments for ALTID - if (splitArgs.Length <= 1) - throw new InvalidDataException("ALTID must have one or more arguments to specify why is this instance an alternative"); - } - - // Finalize the arguments - finalArgs.AddRange(splitArgs.Except( - splitArgs.Where((arg) => - arg.StartsWith(VcardConstants._altIdArgumentSpecifier) || - arg.StartsWith(VcardConstants._valueArgumentSpecifier) || - arg.StartsWith(VcardConstants._typeArgumentSpecifier) - ) - )); - } - - // Card type (KIND:individual, KIND:group, KIND:org, KIND:location, ...) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._kindSpecifier + delimiter)) - { - // Get the value - string kindValue = _value.Substring(VcardConstants._kindSpecifier.Length + 1); - - // Populate field - if (!string.IsNullOrEmpty(kindValue)) - _kind = Regex.Unescape(kindValue); - } - - // The name (N:Sanders;John;;; or N;ALTID=1;LANGUAGE=en:Sanders;John;;;) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._nameSpecifier + delimiter)) - { - // Get the name - 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; - } - - // Full name (FN:John Sanders) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) - { - // Get the value - string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); - - // Populate field - _fullName = Regex.Unescape(fullNameValue); - - // Set flag to indicate that the required field is spotted - fullNameSpecifierSpotted = true; - } - - // 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;;;; 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 + 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 + 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 + 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 + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {urlValue} is invalid"); - - // Populate field - _url = uri.ToString(); - } - - // Note (NOTE:Product Manager) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) - { - // Get the value - 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:...) - // ALTID is supported. - 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._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._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 + delimiter)) - { - // Get the value - string revValue = _value.Substring(VcardConstants._revSpecifier.Length + 1); - - // Populate field - _rev = DateTime.Parse(revValue); - } - - // Nickname (NICKNAME;TYPE=cell,home:495-522-3560) - // ALTID is supported. - 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 + delimiter)) - { - // 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); - } - - // Role (ROLE:Programmer) - // ALTID is supported. - 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 + delimiter)) - { - // Get the value - 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) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._productIdSpecifier + delimiter)) - { - // Get the value - string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length + 1); - - // Populate field - _prodId = Regex.Unescape(prodIdValue); - } - - // Time Zone (TZ;VALUE=text:-05:00; EST; Raleigh/North America) - // ALTID is supported. - 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._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._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 + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {sourceStringValue} is invalid"); - - // Populate field - _source = uri.ToString(); - } - - // XML code (XML:Not an xCard XML element) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._xmlSpecifier + delimiter)) - { - // Get the value - string xmlStringValue = _value.Substring(VcardConstants._xmlSpecifier.Length + 1); - - // Populate field - _xml = Regex.Unescape(xmlStringValue); - } - - // Free/busy URL (FBURL:http://example.com/fb/jdoe) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._fbUrlSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {fbUrlStringValue} is invalid"); - - // Populate field - _fbUrl = uri.ToString(); - } - - // Calendar URL (CALURI:http://example.com/calendar/jdoe) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._calUriSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {calUriStringValue} is invalid"); - - // Populate field - _calUri = uri.ToString(); - } - - // Calendar Request URL (CALADRURI:http://example.com/calendar/jdoe) - // Here, we don't support ALTID. - if (_value.StartsWith(VcardConstants._caladrUriSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {caladrUriStringValue} is invalid"); - - // Populate field - _caladrUri = uri.ToString(); - } - - // X-nonstandard (X-AIM:john.s or X-DL;Design Work Group:List Item 1;List Item 2;List Item 3) - // ALTID is supported. - if (_value.StartsWith(VcardConstants._xSpecifier)) - _xes.Add(XNameInfo.FromStringVcardFour(_value, finalArgs, altId)); - } - catch (Exception ex) - { - throw new VCardParseException(ex.Message, _value, lineNumber, ex); - } - } - - // Requirement checks - if (!fullNameSpecifierSpotted) - throw new InvalidDataException("The full name specifier, \"FN:\", is required."); - - // Make a new instance of the card - return new Card(this, CardVersion, _kind) - { - CardRevision = _rev, - ContactNames = [.. _names], - ContactFullName = _fullName, - ContactTelephones = [.. _telephones], - ContactAddresses = [.. _addresses], - ContactOrganizations = [.. _orgs], - ContactTitles = [.. _titles], - ContactURL = _url, - ContactNotes = _note, - ContactMails = [.. _emails], - ContactXNames = [.. _xes], - ContactPhotos = [.. _photos], - ContactNicknames = [.. _nicks], - ContactBirthdate = _bday, - ContactMailer = "", - ContactRoles = [.. _roles], - ContactCategories = [.. _categories], - ContactLogos = [.. _logos], - ContactProdId = _prodId, - ContactTimeZone = [.. _timezones], - ContactGeo = [.. _geos], - ContactSounds = [.. _sounds], - ContactImpps = [.. _impps], - ContactSource = _source, - ContactXml = _xml, - ContactFreeBusyUrl = _fbUrl, - ContactCalendarUrl = _calUri, - ContactCalendarSchedulingRequestUrl = _caladrUri, - ContactAccessClassification = "" - }; - } - - internal override string SaveToString(Card card) - { - // Check the version to ensure that we're really dealing with VCard 4.0 contact - if (CardVersion != "4.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"4.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Initialize the card builder - var cardBuilder = new StringBuilder(); - - // First, write the header - cardBuilder.AppendLine("BEGIN:VCARD"); - cardBuilder.AppendLine($"VERSION:{CardVersion}"); - 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}"); - foreach (NameInfo name in card.ContactNames) - cardBuilder.AppendLine(name.ToStringVcardFour()); - - // Now, start filling in the rest... - foreach (TelephoneInfo telephone in card.ContactTelephones) - cardBuilder.AppendLine(telephone.ToStringVcardFour()); - foreach (AddressInfo address in card.ContactAddresses) - cardBuilder.AppendLine(address.ToStringVcardFour()); - foreach (EmailInfo email in card.ContactMails) - cardBuilder.AppendLine(email.ToStringVcardFour()); - foreach (OrganizationInfo organization in card.ContactOrganizations) - cardBuilder.AppendLine(organization.ToStringVcardFour()); - foreach (TitleInfo title in card.ContactTitles) - cardBuilder.AppendLine(title.ToStringVcardFour()); - if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); - if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); - foreach (PhotoInfo photo in card.ContactPhotos) - cardBuilder.AppendLine(photo.ToStringVcardFour()); - foreach (LogoInfo logo in card.ContactLogos) - cardBuilder.AppendLine(logo.ToStringVcardFour()); - 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: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: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)}"); - if (!string.IsNullOrWhiteSpace(card.ContactProdId)) - cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}:{card.ContactProdId}"); - if (!string.IsNullOrWhiteSpace(card.ContactSortString)) - cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}:{card.ContactSortString}"); - foreach (TimeZoneInfo timeZone in card.ContactTimeZone) - cardBuilder.AppendLine(timeZone.ToStringVcardFour()); - foreach (GeoInfo geo in card.ContactGeo) - cardBuilder.AppendLine(geo.ToStringVcardFour()); - foreach (ImppInfo impp in card.ContactImpps) - cardBuilder.AppendLine(impp.ToStringVcardFour()); - foreach (XNameInfo xname in card.ContactXNames) - cardBuilder.AppendLine(xname.ToStringVcardFour()); - - // Finally, end the card and return it - cardBuilder.AppendLine("END:VCARD"); - return cardBuilder.ToString(); - } - - internal override void SaveTo(string path, Card card) - { - // Check the version to ensure that we're really dealing with VCard 4.0 contact - if (CardVersion != "4.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"4.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Save all the changes to the file - var cardString = SaveToString(card); - File.WriteAllText(path, cardString); - } - - internal VcardFour(string cardContent, string cardVersion) - { - CardContent = cardContent; - CardVersion = cardVersion; - } - } -} diff --git a/VisualCard/Parsers/IVcardParser.cs b/VisualCard/Parsers/IVcardParser.cs index a50e0ca..8cd22b5 100644 --- a/VisualCard/Parsers/IVcardParser.cs +++ b/VisualCard/Parsers/IVcardParser.cs @@ -17,6 +17,7 @@ // along with this program. If not, see . // +using System; using VisualCard.Parts; namespace VisualCard.Parsers @@ -34,11 +35,22 @@ public interface IVcardParser /// /// The version of the card /// - public string CardVersion { get; } + public Version CardVersion { get; } + + /// + /// VCard expected card version + /// + internal Version ExpectedCardVersion { get; } /// /// Parses the VCard file /// Card Parse(); + + /// + /// Saves a parsed card to the string + /// + /// Parsed card + string SaveToString(Card card); } } diff --git a/VisualCard/Parsers/Three/VcardThree.cs b/VisualCard/Parsers/Three/VcardThree.cs deleted file mode 100644 index 3079a0d..0000000 --- a/VisualCard/Parsers/Three/VcardThree.cs +++ /dev/null @@ -1,535 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using VisualCard.Exceptions; -using VisualCard.Parts; -using TimeZoneInfo = VisualCard.Parts.TimeZoneInfo; - -namespace VisualCard.Parsers.Three -{ - /// - /// Parser for VCard version 3.0. Consult the vcard-30-rfc2426.txt file in source for the specification. - /// - public class VcardThree : BaseVcardParser, IVcardParser - { - /// - public override string CardContent { get; } - /// - public override string CardVersion { get; } - - /// - public override Card Parse() - { - // Check the version to ensure that we're really dealing with VCard 3.0 contact - if (CardVersion != "3.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"3.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Now, make a stream out of card content - byte[] CardContentData = Encoding.Default.GetBytes(CardContent); - MemoryStream CardContentStream = new(CardContentData, false); - StreamReader CardContentReader = new(CardContentStream); - - // Some variables to assign to the Card() ctor - string _fullName = ""; - string _url = ""; - string _note = ""; - string _mailer = ""; - string _prodId = ""; - string _sortString = ""; - string _source = ""; - string _class = ""; - DateTime _rev = DateTime.MinValue; - DateTime _bday = DateTime.MinValue; - List _names = []; - List _telephones = []; - List _emails = []; - List _addresses = []; - List _labels = []; - List _orgs = []; - List _titles = []; - List _photos = []; - List _logos = []; - List _sounds = []; - List _nicks = []; - List _roles = []; - List _categories = []; - List _timezones = []; - List _geos = []; - List _impps = []; - List _agents = []; - List _xes = []; - - // Name and Full Name specifiers are required - bool nameSpecifierSpotted = false; - bool fullNameSpecifierSpotted = false; - - // Iterate through all the lines - int lineNumber = 0; - while (!CardContentReader.EndOfStream) - { - // Get line - 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 + delimiter)) - { - // Get the name - _names.Add(NameInfo.FromStringVcardThree(_value)); - - // Set flag to indicate that the required field is spotted - nameSpecifierSpotted = true; - } - - // Full name (FN:John Sanders) - if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) - { - // Get the value - string fullNameValue = _value.Substring(VcardConstants._fullNameSpecifier.Length + 1); - - // Populate field - _fullName = Regex.Unescape(fullNameValue); - - // Set flag to indicate that the required field is spotted - fullNameSpecifierSpotted = true; - } - - // 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;;;; or ADR:;;Los Angeles, USA;;;;) - if (_value.StartsWith(VcardConstants._addressSpecifier + delimiter)) - { - if (isWithType) - _addresses.Add(AddressInfo.FromStringVcardThreeWithType(_value)); - else - _addresses.Add(AddressInfo.FromStringVcardThree(_value)); - } - - // Label (LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\, Esq.\nMail Drop: TNE QB\n123 Main Street\nAny Town\, CA 91921 - 1234\nU.S.A.) - if (_value.StartsWith(VcardConstants._labelSpecifier + delimiter)) - { - if (isWithType) - _labels.Add(LabelAddressInfo.FromStringVcardThreeWithType(_value)); - else - _labels.Add(LabelAddressInfo.FromStringVcardThree(_value)); - } - - // Agent (AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1...) - if (_value.StartsWith(VcardConstants._agentSpecifier + delimiter)) - { - if (isWithType) - _agents.Add(AgentInfo.FromStringVcardThreeWithType(_value)); - else - _agents.Add(AgentInfo.FromStringVcardThree(_value)); - } - - // Email (EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) - 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 + delimiter)) - { - if (isWithType) - _orgs.Add(OrganizationInfo.FromStringVcardThreeWithType(_value)); - else - _orgs.Add(OrganizationInfo.FromStringVcardThree(_value)); - } - - // Title (TITLE:Product Manager) - if (_value.StartsWith(VcardConstants._titleSpecifier + delimiter)) - _titles.Add(TitleInfo.FromStringVcardThree(_value)); - - // Website link (URL:https://sso.org/) - if (_value.StartsWith(VcardConstants._urlSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {urlValue} is invalid"); - - // Populate field - _url = uri.ToString(); - } - - // Note (NOTE:Product Manager) - if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) - { - // Get the value - 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._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._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._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 + delimiter)) - { - // Get the value - string revValue = _value.Substring(VcardConstants._revSpecifier.Length + 1); - - // Populate field - _rev = DateTime.Parse(revValue); - } - - // Nickname (NICKNAME;TYPE=work:Boss) - 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 + delimiter)) - { - // 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 + delimiter)) - { - // Get the value - string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length + 1); - - // Populate field - _mailer = Regex.Unescape(mailerValue); - } - - // Role (ROLE:Programmer) - 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 + delimiter)) - { - // Get the value - 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 + delimiter)) - { - // Get the value - string prodIdValue = _value.Substring(VcardConstants._productIdSpecifier.Length + 1); - - // Populate field - _prodId = Regex.Unescape(prodIdValue); - } - - // Sort string (SORT-STRING:Harten) - if (_value.StartsWith(VcardConstants._sortStringSpecifier + delimiter)) - { - // Get the value - 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._timeZoneSpecifier + delimiter)) - { - if (isWithType) - _timezones.Add(TimeZoneInfo.FromStringVcardThreeWithType(_value)); - else - _timezones.Add(TimeZoneInfo.FromStringVcardThree(_value)); - } - - // Geo (GEO;VALUE=uri:https://...) - 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._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 + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {sourceStringValue} is invalid"); - - // Populate field - _source = uri.ToString(); - } - - // Class (CLASS:PUBLIC, CLASS:PRIVATE, or CLASS:CONFIDENTIAL) - if (_value.StartsWith(VcardConstants._classSpecifier + delimiter)) - { - // Get the value - string classValue = _value.Substring(VcardConstants._classSpecifier.Length + 1); - - // Populate field - _class = Regex.Unescape(classValue); - } - - // X-nonstandard (X-AIM:john.s or X-DL;Design Work Group:List Item 1;List Item 2;List Item 3) - if (_value.StartsWith(VcardConstants._xSpecifier)) - _xes.Add(XNameInfo.FromStringVcardThree(_value)); - } - catch (Exception ex) - { - throw new VCardParseException(ex.Message, _value, lineNumber, ex); - } - } - - // Requirement checks - if (!nameSpecifierSpotted) - throw new InvalidDataException("The name specifier, \"N:\", is required."); - if (!fullNameSpecifierSpotted) - throw new InvalidDataException("The full name specifier, \"FN:\", is required."); - - // Make a new instance of the card - return new Card(this, CardVersion, "individual") - { - CardRevision = _rev, - ContactNames = [.. _names], - ContactFullName = _fullName, - ContactTelephones = [.. _telephones], - ContactAddresses = [.. _addresses], - ContactLabels = [.. _labels], - ContactAgents = [.. _agents], - ContactOrganizations = [.. _orgs], - ContactTitles = [.. _titles], - ContactURL = _url, - ContactNotes = _note, - ContactMails = [.. _emails], - ContactXNames = [.. _xes], - ContactPhotos = [.. _photos], - ContactNicknames = [.. _nicks], - ContactBirthdate = _bday, - ContactMailer = _mailer, - ContactRoles = [.. _roles], - ContactCategories = [.. _categories], - ContactLogos = [.. _logos], - ContactProdId = _prodId, - ContactSortString = _sortString, - ContactTimeZone = [.. _timezones], - ContactGeo = [.. _geos], - ContactSounds = [.. _sounds], - ContactImpps = [.. _impps], - ContactSource = _source, - ContactXml = "", - ContactFreeBusyUrl = "", - ContactCalendarUrl = "", - ContactCalendarSchedulingRequestUrl = "", - ContactAccessClassification = _class - }; - } - - internal override string SaveToString(Card card) - { - // Check the version to ensure that we're really dealing with VCard 3.0 contact - if (CardVersion != "3.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"3.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Initialize the card builder - var cardBuilder = new StringBuilder(); - - // First, write the header - cardBuilder.AppendLine("BEGIN:VCARD"); - cardBuilder.AppendLine($"VERSION:{CardVersion}"); - - // Then, write the full name and the name - if (!string.IsNullOrWhiteSpace(card.ContactFullName)) - cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}:{card.ContactFullName}"); - foreach (NameInfo name in card.ContactNames) - cardBuilder.AppendLine(name.ToStringVcardThree()); - - // Now, start filling in the rest... - foreach (TelephoneInfo telephone in card.ContactTelephones) - cardBuilder.AppendLine(telephone.ToStringVcardThree()); - foreach (AddressInfo address in card.ContactAddresses) - cardBuilder.AppendLine(address.ToStringVcardThree()); - foreach (LabelAddressInfo label in card.ContactLabels) - cardBuilder.AppendLine(label.ToStringVcardThree()); - foreach (AgentInfo agent in card.ContactAgents) - cardBuilder.AppendLine(agent.ToStringVcardThree()); - foreach (EmailInfo email in card.ContactMails) - cardBuilder.AppendLine(email.ToStringVcardThree()); - foreach (OrganizationInfo organization in card.ContactOrganizations) - cardBuilder.AppendLine(organization.ToStringVcardThree()); - foreach (TitleInfo title in card.ContactTitles) - cardBuilder.AppendLine(title.ToStringVcardThree()); - if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); - if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); - foreach (PhotoInfo photo in card.ContactPhotos) - cardBuilder.AppendLine(photo.ToStringVcardThree()); - foreach (LogoInfo logo in card.ContactLogos) - cardBuilder.AppendLine(logo.ToStringVcardThree()); - 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: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:yyyy-MM-dd}"); - if (!string.IsNullOrWhiteSpace(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)}"); - if (!string.IsNullOrWhiteSpace(card.ContactProdId)) - cardBuilder.AppendLine($"{VcardConstants._productIdSpecifier}:{card.ContactProdId}"); - if (!string.IsNullOrWhiteSpace(card.ContactSortString)) - cardBuilder.AppendLine($"{VcardConstants._sortStringSpecifier}:{card.ContactSortString}"); - foreach (TimeZoneInfo timeZone in card.ContactTimeZone) - cardBuilder.AppendLine(timeZone.ToStringVcardThree()); - foreach (GeoInfo geo in card.ContactGeo) - cardBuilder.AppendLine(geo.ToStringVcardThree()); - foreach (ImppInfo impp in card.ContactImpps) - cardBuilder.AppendLine(impp.ToStringVcardThree()); - if (!string.IsNullOrWhiteSpace(card.ContactAccessClassification)) - cardBuilder.AppendLine($"{VcardConstants._classSpecifier}:{card.ContactAccessClassification}"); - foreach (XNameInfo xname in card.ContactXNames) - cardBuilder.AppendLine(xname.ToStringVcardThree()); - - // Finally, end the card and return it - cardBuilder.AppendLine("END:VCARD"); - return cardBuilder.ToString(); - } - - internal override void SaveTo(string path, Card card) - { - // Check the version to ensure that we're really dealing with VCard 3.0 contact - if (CardVersion != "3.0") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"3.0\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Save all the changes to the file - var cardString = SaveToString(card); - File.WriteAllText(path, cardString); - } - - internal VcardThree(string cardContent, string cardVersion) - { - CardContent = cardContent; - CardVersion = cardVersion; - } - } -} diff --git a/VisualCard/Parsers/Two/VcardTwo.cs b/VisualCard/Parsers/Two/VcardTwo.cs deleted file mode 100644 index 0548410..0000000 --- a/VisualCard/Parsers/Two/VcardTwo.cs +++ /dev/null @@ -1,474 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using VisualCard.Exceptions; -using VisualCard.Parts; -using TimeZoneInfo = VisualCard.Parts.TimeZoneInfo; - -namespace VisualCard.Parsers.Two -{ - /// - /// Parser for VCard version 2.1. Consult the vcard-21.txt file in source for the specification. - /// - public class VcardTwo : BaseVcardParser, IVcardParser - { - /// - public override string CardContent { get; } - /// - public override string CardVersion { get; } - - /// - public override Card Parse() - { - // Check the version to ensure that we're really dealing with VCard 2.1 contact - if (CardVersion != "2.1") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"2.1\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Now, make a stream out of card content - byte[] CardContentData = Encoding.Default.GetBytes(CardContent); - MemoryStream CardContentStream = new(CardContentData, false); - StreamReader CardContentReader = new(CardContentStream); - - // Some variables to assign to the Card() ctor - string _fullName = ""; - string _url = ""; - string _note = ""; - string _mailer = ""; - string _source = ""; - DateTime _rev = DateTime.MinValue; - DateTime _bday = DateTime.MinValue; - List _telephones = []; - List _names = []; - List _emails = []; - List _addresses = []; - List _labels = []; - List _orgs = []; - List _titles = []; - List _photos = []; - List _logos = []; - List _sounds = []; - List _roles = []; - List _categories = []; - List _timezones = []; - List _geos = []; - List _impps = []; - List _agents = []; - List _xes = []; - - // Name specifier is required - bool nameSpecifierSpotted = false; - - // Iterate through all the lines - int lineNumber = 0; - while (!CardContentReader.EndOfStream) - { - // Get line - 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;;;) - // Type is not supported - if (_value.StartsWith(VcardConstants._nameSpecifier + delimiter)) - { - // Get the name - _names.Add(NameInfo.FromStringVcardTwo(_value)); - - // Set flag to indicate that the required field is spotted - nameSpecifierSpotted = true; - } - - // Full name (FN:John Sanders) - // Type is not supported - if (_value.StartsWith(VcardConstants._fullNameSpecifier + delimiter)) - { - // Get the value - 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 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;;;; or ADR:;;Los Angeles, USA;;;;) - if (_value.StartsWith(VcardConstants._addressSpecifier + delimiter)) - { - if (isWithType) - _addresses.Add(AddressInfo.FromStringVcardTwoWithType(_value)); - else - _addresses.Add(AddressInfo.FromStringVcardTwo(_value)); - } - - // Label (LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\, Esq.\nMail Drop: TNE QB\n123 Main Street\nAny Town\, CA 91921 - 1234\nU.S.A.) - if (_value.StartsWith(VcardConstants._labelSpecifier + delimiter)) - { - if (isWithType) - _labels.Add(LabelAddressInfo.FromStringVcardTwoWithType(_value)); - else - _labels.Add(LabelAddressInfo.FromStringVcardTwo(_value)); - } - - // Agent (AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1...) - if (_value.StartsWith(VcardConstants._agentSpecifier + delimiter)) - { - if (isWithType) - _agents.Add(AgentInfo.FromStringVcardTwoWithType(_value)); - else - _agents.Add(AgentInfo.FromStringVcardTwo(_value)); - } - - // Email (EMAIL;HOME;INTERNET:john.s@acme.co or EMAIL;TYPE=HOME,INTERNET:john.s@acme.co) - 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 + delimiter)) - { - if (isWithType) - _orgs.Add(OrganizationInfo.FromStringVcardTwoWithType(_value)); - else - _orgs.Add(OrganizationInfo.FromStringVcardTwo(_value)); - } - - // Title (TITLE:Product Manager) - if (_value.StartsWith(VcardConstants._titleSpecifier + delimiter)) - _titles.Add(TitleInfo.FromStringVcardTwo(_value)); - - // Website link (URL:https://sso.org/) - if (_value.StartsWith(VcardConstants._urlSpecifier + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {urlValue} is invalid"); - - // Populate field - _url = uri.ToString(); - } - - // Note (NOTE:Product Manager) - if (_value.StartsWith(VcardConstants._noteSpecifier + delimiter)) - { - // Get the value - 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._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._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._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 + delimiter)) - { - 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 + delimiter)) - { - // 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 + delimiter)) - { - string mailerValue = _value.Substring(VcardConstants._mailerSpecifier.Length + 1); - _mailer = Regex.Unescape(mailerValue); - } - - // Role (ROLE:Programmer) - if (_value.StartsWith(VcardConstants._roleSpecifier + delimiter)) - _roles.Add(RoleInfo.FromStringVcardTwo(_value)); - - // Categories (CATEGORIES:INTERNET or CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY) - if (_value.StartsWith(VcardConstants._categoriesSpecifier + delimiter)) - { - // Get the value - string categoriesValue = _value.Substring(VcardConstants._categoriesSpecifier.Length + 1); - - // Populate field - _categories.AddRange(Regex.Unescape(categoriesValue).Split(',')); - } - - // Time Zone (TZ;VALUE=text:-05:00; EST; Raleigh/North America) - 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._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._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 + delimiter)) - { - // Get the value - 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)) - throw new InvalidDataException($"URL {sourceStringValue} is invalid"); - - // Populate field - _source = uri.ToString(); - } - - // X-nonstandard (X-AIM:john.s or X-DL;Design Work Group:List Item 1;List Item 2;List Item 3) - if (_value.StartsWith(VcardConstants._xSpecifier)) - _xes.Add(XNameInfo.FromStringVcardTwo(_value)); - } - catch (Exception ex) - { - throw new VCardParseException(ex.Message, _value, lineNumber, ex); - } - } - - // Requirement checks - if (!nameSpecifierSpotted) - throw new InvalidDataException("The name specifier, \"N:\", is required."); - - // Make a new instance of the card - return new Card(this, CardVersion, "individual") - { - CardRevision = _rev, - ContactNames = [.. _names], - ContactFullName = _fullName, - ContactTelephones = [.. _telephones], - ContactAddresses = [.. _addresses], - ContactLabels = [.. _labels], - ContactAgents = [.. _agents], - ContactOrganizations = [.. _orgs], - ContactTitles = [.. _titles], - ContactURL = _url, - ContactNotes = _note, - ContactMails = [.. _emails], - ContactXNames = [.. _xes], - ContactPhotos = [.. _photos], - ContactNicknames = [], - ContactBirthdate = _bday, - ContactMailer = _mailer, - ContactRoles = [.. _roles], - ContactCategories = [.. _categories], - ContactLogos = [.. _logos], - ContactProdId = "", - ContactSortString = "", - ContactTimeZone = [.. _timezones], - ContactGeo = [.. _geos], - ContactSounds = [.. _sounds], - ContactImpps = [.. _impps], - ContactSource = _source, - ContactXml = "", - ContactFreeBusyUrl = "", - ContactCalendarUrl = "", - ContactCalendarSchedulingRequestUrl = "", - ContactAccessClassification = "" - }; - } - - internal override string SaveToString(Card card) - { - // Check the version to ensure that we're really dealing with VCard 2.1 contact - if (CardVersion != "2.1") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"2.1\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Initialize the card builder - var cardBuilder = new StringBuilder(); - - // First, write the header - cardBuilder.AppendLine("BEGIN:VCARD"); - cardBuilder.AppendLine($"VERSION:{CardVersion}"); - - // Then, write the full name and the name - if (!string.IsNullOrWhiteSpace(card.ContactFullName)) - cardBuilder.AppendLine($"{VcardConstants._fullNameSpecifier}:{card.ContactFullName}"); - foreach (NameInfo name in card.ContactNames) - cardBuilder.AppendLine(name.ToStringVcardTwo()); - - // Now, start filling in the rest... - foreach (TelephoneInfo telephone in card.ContactTelephones) - cardBuilder.AppendLine(telephone.ToStringVcardTwo()); - foreach (AddressInfo address in card.ContactAddresses) - cardBuilder.AppendLine(address.ToStringVcardTwo()); - foreach (LabelAddressInfo label in card.ContactLabels) - cardBuilder.AppendLine(label.ToStringVcardTwo()); - foreach (AgentInfo agent in card.ContactAgents) - cardBuilder.AppendLine(agent.ToStringVcardTwo()); - foreach (EmailInfo email in card.ContactMails) - cardBuilder.AppendLine(email.ToStringVcardTwo()); - foreach (OrganizationInfo organization in card.ContactOrganizations) - cardBuilder.AppendLine(organization.ToStringVcardTwo()); - foreach (TitleInfo title in card.ContactTitles) - cardBuilder.AppendLine(title.ToStringVcardTwo()); - if (!string.IsNullOrWhiteSpace(card.ContactURL)) - cardBuilder.AppendLine($"{VcardConstants._urlSpecifier}:{card.ContactURL}"); - if (!string.IsNullOrWhiteSpace(card.ContactNotes)) - cardBuilder.AppendLine($"{VcardConstants._noteSpecifier}:{card.ContactNotes}"); - foreach (PhotoInfo photo in card.ContactPhotos) - cardBuilder.AppendLine(photo.ToStringVcardTwo()); - foreach (LogoInfo logo in card.ContactLogos) - cardBuilder.AppendLine(logo.ToStringVcardTwo()); - 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:yyyy-MM-dd HH:mm:ss}"); - if (card.ContactBirthdate is not null && card.ContactBirthdate != DateTime.MinValue) - cardBuilder.AppendLine($"{VcardConstants._birthSpecifier}:{card.ContactBirthdate:yyyy-MM-dd}"); - if (!string.IsNullOrWhiteSpace(card.ContactMailer)) - cardBuilder.AppendLine($"{VcardConstants._mailerSpecifier}:{card.ContactMailer}"); - foreach (RoleInfo role in card.ContactRoles) - cardBuilder.AppendLine(role.ToStringVcardTwo()); - if (card.ContactCategories is not null && card.ContactCategories.Length > 0) - cardBuilder.AppendLine($"{VcardConstants._categoriesSpecifier}:{string.Join(",", card.ContactCategories)}"); - foreach (TimeZoneInfo timeZone in card.ContactTimeZone) - cardBuilder.AppendLine(timeZone.ToStringVcardTwo()); - foreach (GeoInfo geo in card.ContactGeo) - cardBuilder.AppendLine(geo.ToStringVcardTwo()); - foreach (ImppInfo impp in card.ContactImpps) - cardBuilder.AppendLine(impp.ToStringVcardTwo()); - foreach (XNameInfo xname in card.ContactXNames) - cardBuilder.AppendLine(xname.ToStringVcardTwo()); - - // Finally, end the card and return it - cardBuilder.AppendLine("END:VCARD"); - return cardBuilder.ToString(); - } - - internal override void SaveTo(string path, Card card) - { - // Check the version to ensure that we're really dealing with VCard 2.1 contact - if (CardVersion != "2.1") - throw new InvalidDataException($"Card version {CardVersion} doesn't match expected \"2.1\"."); - - // Check the content to ensure that we really have data - if (string.IsNullOrEmpty(CardContent)) - throw new InvalidDataException($"Card content is empty."); - - // Save all the changes to the file - var cardString = SaveToString(card); - File.WriteAllText(path, cardString); - } - - internal VcardTwo(string cardContent, string cardVersion) - { - CardContent = cardContent; - CardVersion = cardVersion; - } - } -} diff --git a/VisualCard/Parsers/Versioned/VcardFive.cs b/VisualCard/Parsers/Versioned/VcardFive.cs new file mode 100644 index 0000000..4532380 --- /dev/null +++ b/VisualCard/Parsers/Versioned/VcardFive.cs @@ -0,0 +1,39 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; + +namespace VisualCard.Parsers.Versioned +{ + /// + /// Parser for VCard version 5.0. Consult the vcard-40-rfc6350.txt file in source for the specification. + /// + internal class VcardFive : BaseVcardParser, IVcardParser + { + /// + public override Version ExpectedCardVersion => + new(5, 0); + + internal VcardFive(string cardContent, Version cardVersion) + { + CardContent = cardContent; + CardVersion = cardVersion; + } + } +} diff --git a/VisualCard/Parsers/Versioned/VcardFour.cs b/VisualCard/Parsers/Versioned/VcardFour.cs new file mode 100644 index 0000000..31d195e --- /dev/null +++ b/VisualCard/Parsers/Versioned/VcardFour.cs @@ -0,0 +1,39 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; + +namespace VisualCard.Parsers.Versioned +{ + /// + /// Parser for VCard version 4.0. Consult the vcard-40-rfc6350.txt file in source for the specification. + /// + internal class VcardFour : BaseVcardParser, IVcardParser + { + /// + public override Version ExpectedCardVersion => + new(4, 0); + + internal VcardFour(string cardContent, Version cardVersion) + { + CardContent = cardContent; + CardVersion = cardVersion; + } + } +} diff --git a/VisualCard/Parsers/Versioned/VcardThree.cs b/VisualCard/Parsers/Versioned/VcardThree.cs new file mode 100644 index 0000000..549ad18 --- /dev/null +++ b/VisualCard/Parsers/Versioned/VcardThree.cs @@ -0,0 +1,39 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; + +namespace VisualCard.Parsers.Versioned +{ + /// + /// Parser for VCard version 3.0. Consult the vcard-30-rfc2426.txt file in source for the specification. + /// + internal class VcardThree : BaseVcardParser, IVcardParser + { + /// + public override Version ExpectedCardVersion => + new(3, 0); + + internal VcardThree(string cardContent, Version cardVersion) + { + CardContent = cardContent; + CardVersion = cardVersion; + } + } +} diff --git a/VisualCard/Parsers/Versioned/VcardTwo.cs b/VisualCard/Parsers/Versioned/VcardTwo.cs new file mode 100644 index 0000000..47666aa --- /dev/null +++ b/VisualCard/Parsers/Versioned/VcardTwo.cs @@ -0,0 +1,39 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; + +namespace VisualCard.Parsers.Versioned +{ + /// + /// Parser for VCard version 2.1. Consult the vcard-21.txt file in source for the specification. + /// + internal class VcardTwo : BaseVcardParser, IVcardParser + { + /// + public override Version ExpectedCardVersion => + new(2, 1); + + internal VcardTwo(string cardContent, Version cardVersion) + { + CardContent = cardContent; + CardVersion = cardVersion; + } + } +} diff --git a/VisualCard/Parts/AddressInfo.cs b/VisualCard/Parts/AddressInfo.cs deleted file mode 100644 index e813a3c..0000000 --- a/VisualCard/Parts/AddressInfo.cs +++ /dev/null @@ -1,352 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact address information - /// - [DebuggerDisplay("Address = {PostOfficeBox}, {ExtendedAddress}, {StreetAddress}, {Locality}, {Region}, {PostalCode}, {Country}")] - public class AddressInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's address types - /// - public string[] AddressTypes { get; } - /// - /// The contact's post office box - /// - public string PostOfficeBox { get; } - /// - /// The contact's extended address - /// - public string ExtendedAddress { get; } - /// - /// The contact's street address - /// - public string StreetAddress { get; } - /// - /// The contact's locality - /// - public string Locality { get; } - /// - /// The contact's region - /// - public string Region { get; } - /// - /// The contact's postal code - /// - public string PostalCode { get; } - /// - /// The contact's country - /// - public string Country { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(AddressInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(AddressInfo source, AddressInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AddressTypes.SequenceEqual(target.AddressTypes) && - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.PostOfficeBox == target.PostOfficeBox && - source.ExtendedAddress == target.ExtendedAddress && - source.StreetAddress == target.StreetAddress && - source.Locality == target.Locality && - source.Region == target.Region && - source.PostalCode == target.PostalCode && - source.Country == target.Country - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -1858114484; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AddressTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PostOfficeBox); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ExtendedAddress); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StreetAddress); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Locality); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Region); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PostalCode); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Country); - return hashCode; - } - - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._addressSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + - $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + - $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + - $"{StreetAddress}{VcardConstants._fieldDelimiter}" + - $"{Locality}{VcardConstants._fieldDelimiter}" + - $"{Region}{VcardConstants._fieldDelimiter}" + - $"{PostalCode}{VcardConstants._fieldDelimiter}" + - $"{Country}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._addressSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + - $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + - $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + - $"{StreetAddress}{VcardConstants._fieldDelimiter}" + - $"{Locality}{VcardConstants._fieldDelimiter}" + - $"{Region}{VcardConstants._fieldDelimiter}" + - $"{PostalCode}{VcardConstants._fieldDelimiter}" + - $"{Country}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._addressSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + - $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + - $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + - $"{StreetAddress}{VcardConstants._fieldDelimiter}" + - $"{Locality}{VcardConstants._fieldDelimiter}" + - $"{Region}{VcardConstants._fieldDelimiter}" + - $"{PostalCode}{VcardConstants._fieldDelimiter}" + - $"{Country}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - 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 = ["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, [], _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); - return _address; - } - - internal static AddressInfo FromStringVcardTwoWithType(string value) - { - // Get the value - 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)"); - - // Check the provided address - string[] splitAddressValues = splitAdr[1].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 = VcardParserTools.GetTypes(splitAdr, "HOME", false); - 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, [], _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); - 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 = ["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, [], _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); - return _address; - } - - internal static AddressInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Check the provided address - string[] splitAddressValues = splitAdr[1].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 = VcardParserTools.GetTypes(splitAdr, "HOME", true); - 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, [], _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); - 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 = ["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, [], _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._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)"); - - // Check the provided address - string[] splitAddressValues = splitAdr[1].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 = VcardParserTools.GetTypes(splitAdr, "HOME", true); - 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, [.. finalArgs], _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); - return _address; - } - - internal static AddressInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static AddressInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal AddressInfo() { } - - internal AddressInfo(int altId, string[] altArguments, string[] addressTypes, string postOfficeBox, string extendedAddress, string streetAddress, string locality, string region, string postalCode, string country) - { - AltId = altId; - AltArguments = altArguments; - AddressTypes = addressTypes; - PostOfficeBox = postOfficeBox; - ExtendedAddress = extendedAddress; - StreetAddress = streetAddress; - Locality = locality; - Region = region; - PostalCode = postalCode; - Country = country; - } - } -} diff --git a/VisualCard/Parts/AgentInfo.cs b/VisualCard/Parts/AgentInfo.cs deleted file mode 100644 index 11a78df..0000000 --- a/VisualCard/Parts/AgentInfo.cs +++ /dev/null @@ -1,264 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Textify.General; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact agent information - /// - [DebuggerDisplay("{AgentCards.Length} agents")] - public class AgentInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's agent instances - /// - public Card[] AgentCards { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(AgentInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(AgentInfo source, AgentInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.AgentCards == target.AgentCards - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -1716393954; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AgentCards); - return hashCode; - } - - internal string ToStringVcardTwo() - { - var agents = new StringBuilder(); - foreach (var a in AgentCards) - { - agents.Append( - $"{VcardConstants._agentSpecifier}{VcardConstants._argumentDelimiter}" + - $"{string.Join("\\n", a.SaveToString().SplitNewLines())}" - ); - } - return agents.ToString(); - } - - internal string ToStringVcardThree() - { - var agents = new StringBuilder(); - foreach (var a in AgentCards) - { - agents.Append( - $"{VcardConstants._agentSpecifier}{VcardConstants._argumentDelimiter}" + - $"{string.Join("\\n", a.SaveToString().SplitNewLines())}" - ); - } - return agents.ToString(); - } - - internal string ToStringVcardFive() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - var agents = new StringBuilder(); - foreach (var a in AgentCards) - { - agents.Append( - $"{VcardConstants._agentSpecifier}" + - $"{(installAltId ? $"{VcardConstants._fieldDelimiter}{VcardConstants._altIdArgumentSpecifier}" + AltId : "")}" + - $"{VcardConstants._argumentDelimiter}" + - $"{string.Join("\\n", a.SaveToString().SplitNewLines())}" - ); - } - return agents.ToString(); - } - - internal static AgentInfo FromStringVcardTwo(string value) - { - // Get the value - string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); - string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); - - // Check the provided agent - string[] splitAgentValues = splitAgent[0].Split(VcardConstants._fieldDelimiter); - if (splitAgentValues.Length < 1) - throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); - - // Populate the fields - string _agentVcard = Regex.Unescape(agentValue).Replace("\\n", "\n"); - var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); - var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); - AgentInfo _agent = new(0, [], _agentVcardFinal); - return _agent; - } - - internal static AgentInfo FromStringVcardTwoWithType(string value) - { - // Get the value - string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); - string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); - if (splitAgent.Length < 2) - throw new InvalidDataException("Agent field must specify exactly two values (Type (optionally prepended with TYPE=), and agent information)"); - - // Check the provided agent - string[] splitAgentValues = splitAgent[1].Split(VcardConstants._fieldDelimiter); - if (splitAgentValues.Length < 1) - throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); - - // Populate the fields - string _agentVcard = Regex.Unescape(agentValue).Replace("\\n", "\n"); - var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); - var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); - AgentInfo _agent = new(0, [], _agentVcardFinal); - return _agent; - } - - internal static AgentInfo FromStringVcardThree(string value) - { - // Get the value - string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); - string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); - - // Check the provided agent - string[] splitAgentValues = splitAgent[0].Split(VcardConstants._fieldDelimiter); - if (splitAgentValues.Length < 1) - throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); - - // Populate the fields - string _agentVcard = Regex.Unescape(agentValue).Replace("\\n", "\n"); - var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); - var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); - AgentInfo _agent = new(0, [], _agentVcardFinal); - return _agent; - } - - internal static AgentInfo FromStringVcardThreeWithType(string value) - { - // Get the value - string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); - string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); - if (splitAgent.Length < 2) - throw new InvalidDataException("Agent field must specify exactly two values (Type (must be prepended with TYPE=), and agent information)"); - - // Check the provided agent - string[] splitAgentValues = splitAgent[1].Split(VcardConstants._fieldDelimiter); - if (splitAgentValues.Length < 1) - throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); - - // Populate the fields - string _agentVcard = Regex.Unescape(agentValue).Replace("\\n", "\n"); - var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); - var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); - AgentInfo _agent = new(0, [], _agentVcardFinal); - return _agent; - } - - internal static AgentInfo FromStringVcardFive(string value, int altId) - { - // Get the value - string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); - string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); - - // Check the provided agent - string[] splitAgentValues = splitAgent[0].Split(VcardConstants._fieldDelimiter); - if (splitAgentValues.Length < 1) - throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); - - // Populate the fields - string _agentVcard = Regex.Unescape(agentValue).Replace("\\n", "\n"); - var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); - var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); - AgentInfo _agent = new(altId, [], _agentVcardFinal); - return _agent; - } - - internal static AgentInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) - { - // Get the value - string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); - string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); - if (splitAgent.Length < 2) - throw new InvalidDataException("Agent field must specify exactly two values (Type (must be prepended with TYPE=), and agent information)"); - - // Check the provided agent - string[] splitAgentValues = splitAgent[1].Split(VcardConstants._fieldDelimiter); - if (splitAgentValues.Length < 1) - throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); - - // Populate the fields - string _agentVcard = Regex.Unescape(agentValue).Replace("\\n", "\n"); - var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); - var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); - AgentInfo _agent = new(altId, [.. finalArgs], _agentVcardFinal); - return _agent; - } - - internal AgentInfo() { } - - internal AgentInfo(int altId, string[] altArguments, Card[] agentCard) - { - AltId = altId; - AltArguments = altArguments; - AgentCards = agentCard; - } - } -} diff --git a/VisualCard/Parts/BaseCardPartInfo.cs b/VisualCard/Parts/BaseCardPartInfo.cs new file mode 100644 index 0000000..73bc453 --- /dev/null +++ b/VisualCard/Parts/BaseCardPartInfo.cs @@ -0,0 +1,98 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace VisualCard.Parts +{ + /// + /// Base card part class + /// + [DebuggerDisplay("Base card part = ALTID: {AltId}")] + public abstract class BaseCardPartInfo : IEquatable + { + /// + /// Arguments that follow the AltId + /// + public virtual string[] AltArguments { get; internal set; } + + /// + /// Alternative ID. Zero if unspecified. + /// + public virtual int AltId { get; internal set; } + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(BaseCardPartInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(BaseCardPartInfo source, BaseCardPartInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId + ; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + public override int GetHashCode() + { + int hashCode = -2100286935; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + return hashCode; + } + + /// + public static bool operator ==(BaseCardPartInfo left, BaseCardPartInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(BaseCardPartInfo left, BaseCardPartInfo right) => + !(left == right); + + internal abstract BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader); + + internal abstract BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader); + + internal abstract string ToStringVcardInternal(Version cardVersion); + } +} diff --git a/VisualCard/Parts/Card.cs b/VisualCard/Parts/Card.cs index 0a6ddcf..f13dc4c 100644 --- a/VisualCard/Parts/Card.cs +++ b/VisualCard/Parts/Card.cs @@ -20,157 +20,32 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using VisualCard.Parsers; +using VisualCard.Parts.Comparers; +using VisualCard.Parts.Enums; namespace VisualCard.Parts { /// - /// A VCard card + /// A vCard card instance /// - [DebuggerDisplay("VCard version {CardVersion}, kind: {CardKind}, name: {ContactFullName}")] + [DebuggerDisplay("vCard version {CardVersion.ToString()}, parts: (P [{parts.Count}] | A [{partsArray.Count}] | S [{strings.Count}]), explicit kind: {kindExplicitlySpecified}")] public class Card : IEquatable { + internal bool kindExplicitlySpecified = false; private readonly BaseVcardParser _parser; + private readonly Dictionary parts = []; + private readonly Dictionary> partsArray = []; + private readonly Dictionary strings = []; /// /// The VCard version /// - public string CardVersion { get; } = ""; - /// - /// The VCard kind (individual is the default) - /// - public string CardKind { get; } = "individual"; - /// - /// The contact's names - /// - public NameInfo[] ContactNames { get; set; } = []; - /// - /// The contact's full name - /// - public string ContactFullName { get; set; } = ""; - /// - /// The contact's telephones - /// - public TelephoneInfo[] ContactTelephones { get; set; } = []; - /// - /// The contact's addresses - /// - public AddressInfo[] ContactAddresses { get; set; } = []; - /// - /// The contact's delivery address labels - /// - public LabelAddressInfo[] ContactLabels { get; set; } = []; - /// - /// The contact's agents - /// - public AgentInfo[] ContactAgents { get; set; } = []; - /// - /// The contact's e-mails - /// - public EmailInfo[] ContactMails { get; set; } = []; - /// - /// The contact's organizations - /// - public OrganizationInfo[] ContactOrganizations { get; set; } = []; - /// - /// The contact's titles - /// - public TitleInfo[] ContactTitles { get; set; } = []; - /// - /// The contact's URL - /// - public string ContactURL { get; set; } = ""; - /// - /// The contact's photos - /// - public PhotoInfo[] ContactPhotos { get; set; } = []; - /// - /// The contact's notes - /// - public string ContactNotes { get; set; } = ""; - /// - /// The contact's extended options (usually starts with X-SOMETHING:Value1;Value2...) - /// - public XNameInfo[] ContactXNames { get; set; } = []; - /// - /// The card revision - /// - public DateTime? CardRevision { get; set; } = default(DateTime); - /// - /// The contact's nicknames - /// - public NicknameInfo[] ContactNicknames { get; set; } = []; - /// - /// The contact's birthdate - /// - public DateTime? ContactBirthdate { get; set; } = default(DateTime); - /// - /// The contact's mailing software - /// - public string ContactMailer { get; set; } = ""; - /// - /// The contact's roles - /// - public RoleInfo[] ContactRoles { get; set; } = []; - /// - /// The contact's categories - /// - public string[] ContactCategories { get; set; } = []; - /// - /// The contact's logos - /// - public LogoInfo[] ContactLogos { get; set; } = []; - /// - /// The contact's product ID - /// - public string ContactProdId { get; set; } = ""; - /// - /// The contact's sort string - /// - public string ContactSortString { get; set; } = ""; - /// - /// The contact's time zones - /// - public TimeZoneInfo[] ContactTimeZone { get; set; } = []; - /// - /// The contact's geographical coordinates in (lat;long) - /// - public GeoInfo[] ContactGeo { get; set; } = []; - /// - /// The contact's sounds - /// - public SoundInfo[] ContactSounds { get; set; } = []; - /// - /// The contact's IMPP information - /// - public ImppInfo[] ContactImpps { get; set; } = []; - /// - /// The contact's card source - /// - public string ContactSource { get; set; } = ""; - /// - /// The contact's XML code - /// - public string ContactXml { get; set; } = ""; - /// - /// The contact's free/busy indicator URL - /// - public string ContactFreeBusyUrl { get; set; } = ""; - /// - /// The contact's calendar URL - /// - public string ContactCalendarUrl { get; set; } = ""; - /// - /// The contact's calendar scheduling request URL - /// - public string ContactCalendarSchedulingRequestUrl { get; set; } = ""; - /// - /// The contact's access classification - /// - public string ContactAccessClassification { get; set; } = ""; + public Version CardVersion => + Parser.CardVersion; - internal BaseVcardParser Parser => _parser; + internal BaseVcardParser Parser => + _parser; /// /// Saves the contact file to the path @@ -185,6 +60,76 @@ public void SaveTo(string path) => public string SaveToString() => Parser.SaveToString(this); + /// + /// Gets a part array from a specified key + /// + /// A key to use + /// An array of values or an empty part array [] + public BaseCardPartInfo[] GetPartsArray(PartsArrayEnum key) + { + // Check for version support + if (!BaseVcardParser.EnumArrayTypeSupported(key, CardVersion)) + return null; + + // Get the fallback value + BaseCardPartInfo[] fallback = []; + + // Check to see if the partarray has a value or not + bool hasValue = partsArray.TryGetValue(key, out List value); + if (!hasValue) + return fallback; + + // Now, return the value + return [.. value]; + } + + /// + /// Gets a part from a specified key + /// + /// A key to use + /// A value or an empty part if any other type doesn't exist + public BaseCardPartInfo GetPart(PartsEnum key) + { + // Check for version support + if (!BaseVcardParser.EnumTypeSupported(key)) + return null; + + // Get the fallback value + BaseCardPartInfo fallback = default; + + // Check to see if the part has a value or not + bool hasValue = parts.TryGetValue(key, out BaseCardPartInfo value); + if (!hasValue) + return fallback; + + // Now, return the value + return value; + } + + /// + /// Gets a string from a specified key + /// + /// A key to use + /// A value, or "individual" if the kind doesn't exist, or an empty string ("") if any other type either doesn't exist or the type is not supported by the card version + public string GetString(StringsEnum key) + { + // Check for version support + if (!BaseVcardParser.StringSupported(key, CardVersion)) + return ""; + + // Get the fallback value + string fallback = key == StringsEnum.Kind ? "individual" : ""; + + // Check to see if the string has a value or not + bool hasValue = strings.TryGetValue(key, out string value); + if (!hasValue) + return fallback; + + // Now, verify that the string is not empty + hasValue = !string.IsNullOrEmpty(value); + return hasValue ? value : fallback; + } + /// /// Saves the contact to the returned string /// @@ -217,77 +162,19 @@ public bool Equals(Card source, Card target) // Check all the properties return - source.ContactNames.SequenceEqual(target.ContactNames) && - source.ContactTelephones.SequenceEqual(target.ContactTelephones) && - source.ContactAddresses.SequenceEqual(target.ContactAddresses) && - source.ContactLabels.SequenceEqual(target.ContactLabels) && - source.ContactAgents.SequenceEqual(target.ContactAgents) && - source.ContactMails.SequenceEqual(target.ContactMails) && - source.ContactOrganizations.SequenceEqual(target.ContactOrganizations) && - source.ContactTitles.SequenceEqual(target.ContactTitles) && - source.ContactPhotos.SequenceEqual(target.ContactPhotos) && - source.ContactXNames.SequenceEqual(target.ContactXNames) && - source.ContactNicknames.SequenceEqual(target.ContactNicknames) && - source.ContactRoles.SequenceEqual(target.ContactRoles) && - source.ContactCategories.SequenceEqual(target.ContactCategories) && - source.ContactLogos.SequenceEqual(target.ContactLogos) && - source.ContactTimeZone.SequenceEqual(target.ContactTimeZone) && - source.ContactGeo.SequenceEqual(target.ContactGeo) && - source.ContactSounds.SequenceEqual(target.ContactSounds) && - source.ContactImpps.SequenceEqual(target.ContactImpps) && - source.ContactFullName == target.ContactFullName && - source.ContactURL == target.ContactURL && - source.ContactNotes == target.ContactNotes && - source.CardRevision == target.CardRevision && - source.ContactBirthdate == target.ContactBirthdate && - source.ContactMailer == target.ContactMailer && - source.ContactProdId == target.ContactProdId && - source.ContactSortString == target.ContactSortString && - source.ContactSource == target.ContactSource && - source.ContactXml == target.ContactXml && - source.ContactFreeBusyUrl == target.ContactFreeBusyUrl && - source.ContactCalendarUrl == target.ContactCalendarUrl && - source.ContactCalendarSchedulingRequestUrl == target.ContactCalendarSchedulingRequestUrl && - source.ContactAccessClassification == target.ContactAccessClassification + PartComparison.PartsEnumEqual(source.parts, target.parts) && + PartComparison.PartsArrayEnumEqual(source.partsArray, target.partsArray) && + PartComparison.StringsEqual(source.strings, target.strings) ; } /// public override int GetHashCode() { - int hashCode = -1467509753; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactNames); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactFullName); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactTelephones); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactAddresses); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactLabels); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactAgents); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactMails); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactOrganizations); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactTitles); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactURL); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactPhotos); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactNotes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactXNames); - hashCode = hashCode * -1521134295 + CardRevision.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactNicknames); - hashCode = hashCode * -1521134295 + ContactBirthdate.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactMailer); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactRoles); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactCategories); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactLogos); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactProdId); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactSortString); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactTimeZone); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactGeo); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactSounds); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactImpps); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactSource); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactXml); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactFreeBusyUrl); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactCalendarUrl); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactCalendarSchedulingRequestUrl); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactAccessClassification); + int hashCode = -1645291684; + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(parts); + hashCode = hashCode * -1521134295 + EqualityComparer>>.Default.GetHashCode(partsArray); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(strings); return hashCode; } @@ -299,11 +186,53 @@ public override int GetHashCode() public static bool operator !=(Card a, Card b) => !a.Equals(b); - internal Card(BaseVcardParser parser, string cardVersion, string cardKind = "individual") + internal void AddPartToArray(PartsArrayEnum key, BaseCardPartInfo value) { - _parser = parser; - CardVersion = cardVersion; - CardKind = cardKind; + if (value is null) + return; + + // Get the appropriate type and check it + var enumType = BaseVcardParser.GetEnumArrayType(key); + if (value.GetType() != enumType) + return; + + // If we don't have this key yet, add it. + if (!partsArray.ContainsKey(key)) + partsArray.Add(key, [value]); + else + partsArray[key].Add(value); + } + + internal void SetPart(PartsEnum key, BaseCardPartInfo value) + { + if (value is null) + return; + + // Get the appropriate type and check it + var enumType = BaseVcardParser.GetEnumType(key); + if (value.GetType() != enumType) + return; + + // If we don't have this key yet, add it. + if (!parts.ContainsKey(key)) + parts.Add(key, value); + else + parts[key] = value; } + + internal void SetString(StringsEnum key, string value) + { + if (string.IsNullOrEmpty(value)) + return; + + // If we don't have this key yet, add it. + if (!strings.ContainsKey(key)) + strings.Add(key, value); + else + strings[key] = value; + } + + internal Card(BaseVcardParser parser) => + _parser = parser; } } diff --git a/VisualCard/Parts/Comparers/PartComparison.cs b/VisualCard/Parts/Comparers/PartComparison.cs new file mode 100644 index 0000000..55be23a --- /dev/null +++ b/VisualCard/Parts/Comparers/PartComparison.cs @@ -0,0 +1,107 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Linq; +using VisualCard.Parts.Enums; + +namespace VisualCard.Parts.Comparers +{ + internal static class PartComparison + { + internal static bool PartsEnumEqual( + IDictionary source, + IDictionary target) + { + // Verify the dictionaries + if (!VerifyDicts(source, target)) + return false; + + // If they are really equal using the equals operator, return true. + if (source == target) + return true; + + // Now, test the equality + bool equal = source.All(kvp => + { + bool exists = target.TryGetValue(kvp.Key, out BaseCardPartInfo part); + bool partsEqual = EqualityComparer.Default.Equals(kvp.Value, part); + return exists && partsEqual; + }); + return equal; + } + + internal static bool PartsArrayEnumEqual( + IDictionary> source, + IDictionary> target) + { + // Verify the dictionaries + if (!VerifyDicts(source, target)) + return false; + + // If they are really equal using the equals operator, return true. + if (source == target) + return true; + + // Now, test the equality + bool equal = source.All(kvp => + { + bool exists = target.TryGetValue(kvp.Key, out List parts); + bool partsEqual = kvp.Value.SequenceEqual(parts); + return exists && partsEqual; + }); + return equal; + } + + internal static bool StringsEqual( + IDictionary source, + IDictionary target) + { + // Verify the dictionaries + if (!VerifyDicts(source, target)) + return false; + + // If they are really equal using the equals operator, return true. + if (source == target) + return true; + + // Now, test the equality + bool equal = source.All(kvp => + { + bool exists = target.TryGetValue(kvp.Key, out string part); + bool partsEqual = kvp.Value == part; + return exists && partsEqual; + }); + return equal; + } + + private static bool VerifyDicts( + IDictionary source, + IDictionary target) + { + if (source == null || target == null) + return false; + + if (source.Count != target.Count) + return false; + return true; + } + } +} diff --git a/VisualCard/Parts/EmailInfo.cs b/VisualCard/Parts/EmailInfo.cs deleted file mode 100644 index f594ab4..0000000 --- a/VisualCard/Parts/EmailInfo.cs +++ /dev/null @@ -1,291 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.Mail; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact e-mail information - /// - [DebuggerDisplay("E-mail = {ContactEmailAddress}")] - public class EmailInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's email types - /// - public string[] ContactEmailTypes { get; } - /// - /// The contact's email address - /// - public string ContactEmailAddress { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(EmailInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(EmailInfo source, EmailInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.ContactEmailTypes.SequenceEqual(target.ContactEmailTypes) && - source.AltId == target.AltId && - source.ContactEmailAddress == target.ContactEmailAddress - ; - } - - /// - public override int GetHashCode() - { - int hashCode = 2091849342; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactEmailTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactEmailAddress); - return hashCode; - } - - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._emailSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactEmailAddress}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._emailSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactEmailAddress}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._emailSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactEmailAddress}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - 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 = ["HOME"]; - string _emailAddress = mail.Address; - EmailInfo _email = new(0, [], _emailTypes, _emailAddress); - return _email; - } - - internal static EmailInfo FromStringVcardTwoWithType(string value) - { - // Get the value - string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); - string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); - MailAddress mail; - if (splitMail.Length < 2) - throw new InvalidDataException("E-mail field must specify exactly two values (Type (optionally prepended with TYPE=), and a valid e-mail address)"); - - // Try to create mail address - try - { - mail = new MailAddress(splitMail[1]); - } - catch (ArgumentException aex) - { - throw new InvalidDataException("E-mail address is invalid", aex); - } - - // Populate the fields - string[] _emailTypes = VcardParserTools.GetTypes(splitMail, "HOME", false); - string _emailAddress = mail.Address; - EmailInfo _email = new(0, [], _emailTypes, _emailAddress); - 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 = ["HOME"]; - string _emailAddress = mail.Address; - EmailInfo _email = new(0, [], _emailTypes, _emailAddress); - return _email; - } - - internal static EmailInfo FromStringVcardThreeWithType(string value) - { - // Get the value - string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); - string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); - MailAddress mail; - if (splitMail.Length < 2) - throw new InvalidDataException("E-mail field must specify exactly two values (Type (must be prepended with TYPE=), and a valid e-mail address)"); - - // Try to create mail address - try - { - mail = new MailAddress(splitMail[1]); - } - catch (ArgumentException aex) - { - throw new InvalidDataException("E-mail address is invalid", aex); - } - - // Populate the fields - string[] _emailTypes = VcardParserTools.GetTypes(splitMail, "HOME", true); - string _emailAddress = mail.Address; - EmailInfo _email = new(0, [], _emailTypes, _emailAddress); - 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 = ["HOME"]; - string _emailAddress = mail.Address; - EmailInfo _email = new(altId, [], _emailTypes, _emailAddress); - return _email; - } - - internal static EmailInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); - string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); - MailAddress mail; - if (splitMail.Length < 2) - throw new InvalidDataException("E-mail field must specify exactly two values (Type (must be prepended with TYPE=), and a valid e-mail address)"); - - // Try to create mail address - try - { - mail = new MailAddress(splitMail[1]); - } - catch (ArgumentException aex) - { - throw new InvalidDataException("E-mail address is invalid", aex); - } - - // Populate the fields - string[] _emailTypes = VcardParserTools.GetTypes(splitMail, "HOME", true); - string _emailAddress = mail.Address; - EmailInfo _email = new(altId, [.. finalArgs], _emailTypes, _emailAddress); - return _email; - } - - internal static EmailInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static EmailInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal EmailInfo() { } - - internal EmailInfo(int altId, string[] altArguments, string[] contactEmailTypes, string contactEmailAddress) - { - AltId = altId; - AltArguments = altArguments; - ContactEmailTypes = contactEmailTypes; - ContactEmailAddress = contactEmailAddress; - } - } -} diff --git a/VisualCard/Parts/Enums/PartsArrayEnum.cs b/VisualCard/Parts/Enums/PartsArrayEnum.cs new file mode 100644 index 0000000..b898f71 --- /dev/null +++ b/VisualCard/Parts/Enums/PartsArrayEnum.cs @@ -0,0 +1,100 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +namespace VisualCard.Parts.Enums +{ + /// + /// Enumeration for available parts that are not strings but are arrays + /// + public enum PartsArrayEnum + { + /// + /// The contact's names + /// + Names, + /// + /// The contact's telephones + /// + Telephones, + /// + /// The contact's addresses + /// + Addresses, + /// + /// The contact's delivery address labels + /// + Labels, + /// + /// The contact's agents + /// + Agents, + /// + /// The contact's e-mails + /// + Mails, + /// + /// The contact's organizations + /// + Organizations, + /// + /// The contact's titles + /// + Titles, + /// + /// The contact's photos + /// + Photos, + /// + /// The contact's nicknames + /// + Nicknames, + /// + /// The contact's roles + /// + Roles, + /// + /// The contact's categories + /// + Categories, + /// + /// The contact's logos + /// + Logos, + /// + /// The contact's time zones + /// + TimeZone, + /// + /// The contact's geographical coordinates in (lat;long) + /// + Geo, + /// + /// The contact's sounds + /// + Sounds, + /// + /// The contact's IMPP information + /// + Impps, + /// + /// The contact's extended options (usually starts with X-SOMETHING:Value1;Value2...) + /// + NonstandardNames, + } +} diff --git a/VisualCard/Parts/Enums/PartsEnum.cs b/VisualCard/Parts/Enums/PartsEnum.cs new file mode 100644 index 0000000..cf84d5b --- /dev/null +++ b/VisualCard/Parts/Enums/PartsEnum.cs @@ -0,0 +1,36 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +namespace VisualCard.Parts.Enums +{ + /// + /// Enumeration for available parts that are not strings and can only hold one value + /// + public enum PartsEnum + { + /// + /// The card revision + /// + Revision, + /// + /// The contact's birthdate + /// + Birthdate, + } +} diff --git a/VisualCard/Parts/Enums/StringsEnum.cs b/VisualCard/Parts/Enums/StringsEnum.cs new file mode 100644 index 0000000..9d2f951 --- /dev/null +++ b/VisualCard/Parts/Enums/StringsEnum.cs @@ -0,0 +1,80 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +namespace VisualCard.Parts.Enums +{ + /// + /// Enumeration for available parts that are strings + /// + public enum StringsEnum + { + /// + /// The VCard kind (individual is the default) + /// + Kind, + /// + /// The contact's full name + /// + FullName, + /// + /// The contact's URL + /// + Url, + /// + /// The contact's notes + /// + Notes, + /// + /// The contact's mailing software + /// + Mailer, + /// + /// The contact's product ID + /// + ProductId, + /// + /// The contact's sort string + /// + SortString, + /// + /// The contact's card source + /// + Source, + /// + /// The contact's XML code + /// + Xml, + /// + /// The contact's free/busy indicator URL + /// + FreeBusyUrl, + /// + /// The contact's calendar URL + /// + CalendarUrl, + /// + /// The contact's calendar scheduling request URL + /// + CalendarSchedulingRequestUrl, + /// + /// The contact's access classification + /// + AccessClassification + } +} diff --git a/VisualCard/Parts/GeoInfo.cs b/VisualCard/Parts/GeoInfo.cs deleted file mode 100644 index c7837ad..0000000 --- a/VisualCard/Parts/GeoInfo.cs +++ /dev/null @@ -1,222 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact geographical information - /// - [DebuggerDisplay("Geography = {Geo}")] - public class GeoInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's geographical information types - /// - public string[] GeoTypes { get; } - /// - /// The contact's geographical information - /// - public string Geo { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(GeoInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(GeoInfo source, GeoInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.GeoTypes.SequenceEqual(target.GeoTypes) && - source.AltId == target.AltId && - source.Geo == target.Geo - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -772623698; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(GeoTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Geo); - return hashCode; - } - - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._geoSpecifier}:" + - $"{Geo}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._geoSpecifier}:" + - $"{Geo}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._geoSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + - $"{Geo}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static GeoInfo FromStringVcardTwo(string value) - { - string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); - string _geoStr = Regex.Unescape(geoValue); - GeoInfo _geo = new(0, [], [], _geoStr); - return _geo; - } - - internal static GeoInfo FromStringVcardTwoWithType(string value) - { - 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)"); - - // Get the types and the number - string[] _geoTypes = VcardParserTools.GetValues(splitGeo, "", VcardConstants._valueArgumentSpecifier); - string _geoNumber = Regex.Unescape(splitGeo[1]); - - // Add the fetched information - GeoInfo _geo = new(0, [], _geoTypes, _geoNumber); - return _geo; - } - - internal static GeoInfo FromStringVcardThree(string value) - { - // Get the value - string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); - - // Populate the fields - string[] _geoTypes = ["uri"]; - string _geoNumber = Regex.Unescape(geoValue); - GeoInfo _geo = new(0, [], _geoTypes, _geoNumber); - return _geo; - } - - internal static GeoInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Get the types and the number - string[] _geoTypes = VcardParserTools.GetValues(splitGeo, "", VcardConstants._valueArgumentSpecifier); - string _geoNumber = Regex.Unescape(splitGeo[1]); - - // Add the fetched information - GeoInfo _geo = new(0, [], _geoTypes, _geoNumber); - return _geo; - } - - internal static GeoInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); - - // Populate the fields - string[] _geoTypes = ["uri"]; - string _geoNumber = Regex.Unescape(geoValue); - GeoInfo _geo = new(altId, [], _geoTypes, _geoNumber); - return _geo; - } - - internal static GeoInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - 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)"); - - // Get the types and the number - string[] _geoTypes = VcardParserTools.GetValues(splitGeo, "", VcardConstants._valueArgumentSpecifier); - string _geoNumber = Regex.Unescape(splitGeo[1]); - - // Add the fetched information - GeoInfo _geo = new(altId, [.. finalArgs], _geoTypes, _geoNumber); - return _geo; - } - - internal static GeoInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static GeoInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal GeoInfo() { } - - internal GeoInfo(int altId, string[] altArguments, string[] geoTypes, string geo) - { - AltId = altId; - AltArguments = altArguments; - GeoTypes = geoTypes; - Geo = geo; - } - } -} diff --git a/VisualCard/Parts/Implementations/AddressInfo.cs b/VisualCard/Parts/Implementations/AddressInfo.cs new file mode 100644 index 0000000..7d035ad --- /dev/null +++ b/VisualCard/Parts/Implementations/AddressInfo.cs @@ -0,0 +1,241 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact address information + /// + [DebuggerDisplay("Address = {PostOfficeBox}, {ExtendedAddress}, {StreetAddress}, {Locality}, {Region}, {PostalCode}, {Country}")] + public class AddressInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's address types + /// + public string[] AddressTypes { get; } + /// + /// The contact's post office box + /// + public string PostOfficeBox { get; } + /// + /// The contact's extended address + /// + public string ExtendedAddress { get; } + /// + /// The contact's street address + /// + public string StreetAddress { get; } + /// + /// The contact's locality + /// + public string Locality { get; } + /// + /// The contact's region + /// + public string Region { get; } + /// + /// The contact's postal code + /// + public string PostalCode { get; } + /// + /// The contact's country + /// + public string Country { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new AddressInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new AddressInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._addressSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + + $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + + $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + + $"{StreetAddress}{VcardConstants._fieldDelimiter}" + + $"{Locality}{VcardConstants._fieldDelimiter}" + + $"{Region}{VcardConstants._fieldDelimiter}" + + $"{PostalCode}{VcardConstants._fieldDelimiter}" + + $"{Country}"; + } + else + { + return + $"{VcardConstants._addressSpecifier};" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + + $"{PostOfficeBox}{VcardConstants._fieldDelimiter}" + + $"{ExtendedAddress}{VcardConstants._fieldDelimiter}" + + $"{StreetAddress}{VcardConstants._fieldDelimiter}" + + $"{Locality}{VcardConstants._fieldDelimiter}" + + $"{Region}{VcardConstants._fieldDelimiter}" + + $"{PostalCode}{VcardConstants._fieldDelimiter}" + + $"{Country}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // 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 + return InstallInfo([], splitAddressValues, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Check the provided address + string[] splitAddressValues = splitAdr[1].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 + return InstallInfo(splitAdr, splitAddressValues, finalArgs, altId, cardVersion); + } + + private AddressInfo InstallInfo(string[] splitAdr, string[] splitAddressValues, int altId, Version cardVersion) => + InstallInfo(splitAdr, splitAddressValues, [], altId, cardVersion); + + private AddressInfo InstallInfo(string[] splitAdr, string[] splitAddressValues, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + bool specifierRequired = cardVersion.Major >= 3; + + // Populate the fields + string[] _addressTypes = splitAdr.Length == 0 ? ["HOME"] : VcardParserTools.GetTypes(splitAdr, "HOME", specifierRequired); + 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(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _addressTypes, _addressPOBox, _addressExtended, _addressStreet, _addressLocality, _addressRegion, _addressPostalCode, _addressCountry); + return _address; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(AddressInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(AddressInfo source, AddressInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AddressTypes.SequenceEqual(target.AddressTypes) && + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.PostOfficeBox == target.PostOfficeBox && + source.ExtendedAddress == target.ExtendedAddress && + source.StreetAddress == target.StreetAddress && + source.Locality == target.Locality && + source.Region == target.Region && + source.PostalCode == target.PostalCode && + source.Country == target.Country + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -1858114484; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AddressTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PostOfficeBox); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ExtendedAddress); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StreetAddress); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Locality); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Region); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PostalCode); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Country); + return hashCode; + } + + /// + public static bool operator ==(AddressInfo left, AddressInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(AddressInfo left, AddressInfo right) => + !(left == right); + + internal AddressInfo() { } + + internal AddressInfo(int altId, string[] altArguments, string[] addressTypes, string postOfficeBox, string extendedAddress, string streetAddress, string locality, string region, string postalCode, string country) + { + AltId = altId; + AltArguments = altArguments; + AddressTypes = addressTypes; + PostOfficeBox = postOfficeBox; + ExtendedAddress = extendedAddress; + StreetAddress = streetAddress; + Locality = locality; + Region = region; + PostalCode = postalCode; + Country = country; + } + } +} diff --git a/VisualCard/Parts/Implementations/AgentInfo.cs b/VisualCard/Parts/Implementations/AgentInfo.cs new file mode 100644 index 0000000..8629858 --- /dev/null +++ b/VisualCard/Parts/Implementations/AgentInfo.cs @@ -0,0 +1,183 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Textify.General; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact agent information + /// + [DebuggerDisplay("{AgentCards.Length} agents")] + public class AgentInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's agent instances + /// + public Card[] AgentCards { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new AgentInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new AgentInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + var agents = new StringBuilder(); + bool altIdSupported = cardVersion.Major >= 4; + + foreach (var a in AgentCards) + { + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + agents.Append( + $"{VcardConstants._agentSpecifier}" + + $"{(installAltId ? $"{VcardConstants._fieldDelimiter}{VcardConstants._altIdArgumentSpecifier}" + AltId : "")}" + + $"{VcardConstants._argumentDelimiter}" + + $"{string.Join("\\n", a.SaveToString().SplitNewLines())}" + ); + } + else + { + agents.Append( + $"{VcardConstants._agentSpecifier}{VcardConstants._argumentDelimiter}" + + $"{string.Join("\\n", a.SaveToString().SplitNewLines())}" + ); + } + } + return agents.ToString(); + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); + string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); + + // Check the provided agent + string[] splitAgentValues = splitAgent[0].Split(VcardConstants._fieldDelimiter); + if (splitAgentValues.Length < 1) + throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); + + // Populate the fields + return InstallInfo(agentValue, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1); + string[] splitAgent = agentValue.Split(VcardConstants._argumentDelimiter); + if (splitAgent.Length < 2) + throw new InvalidDataException("Agent field must specify exactly two values (Type (optionally prepended with TYPE=), and agent information)"); + + // Check the provided agent + string[] splitAgentValues = splitAgent[1].Split(VcardConstants._fieldDelimiter); + if (splitAgentValues.Length < 1) + throw new InvalidDataException("Agent information must specify exactly one value (agent vCard contents that have their lines delimited by \\n)"); + + // Populate the fields + return InstallInfo(agentValue, finalArgs, altId, cardVersion); + } + + private AgentInfo InstallInfo(string value, int altId, Version cardVersion) => + InstallInfo(value, [], altId, cardVersion); + + private AgentInfo InstallInfo(string value, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Populate the fields + string _agentVcard = Regex.Unescape(value).Replace("\\n", "\n"); + var _agentVcardParsers = CardTools.GetCardParsersFromString(_agentVcard); + var _agentVcardFinal = _agentVcardParsers.Select((parser) => parser.Parse()).ToArray(); + AgentInfo _agent = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _agentVcardFinal); + return _agent; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(AgentInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(AgentInfo source, AgentInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.AgentCards == target.AgentCards + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -1716393954; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AgentCards); + return hashCode; + } + + /// + public static bool operator ==(AgentInfo left, AgentInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(AgentInfo left, AgentInfo right) => + !(left == right); + + internal AgentInfo() { } + + internal AgentInfo(int altId, string[] altArguments, Card[] agentCard) + { + AltId = altId; + AltArguments = altArguments; + AgentCards = agentCard; + } + } +} diff --git a/VisualCard/Parts/Implementations/BirthDateInfo.cs b/VisualCard/Parts/Implementations/BirthDateInfo.cs new file mode 100644 index 0000000..d761951 --- /dev/null +++ b/VisualCard/Parts/Implementations/BirthDateInfo.cs @@ -0,0 +1,168 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact birth date info + /// + [DebuggerDisplay("Birth date = {BirthDate}")] + public class BirthDateInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's birth date + /// + public DateTime? BirthDate { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new BirthDateInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new BirthDateInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._birthSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + + $"{BirthDate}"; + } + else + { + return + $"{VcardConstants._birthSpecifier}:" + + $"{BirthDate}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string bdayValue = value.Substring(VcardConstants._birthSpecifier.Length + 1); + + // Populate the fields + return InstallInfo(bdayValue, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string bdayValue = value.Substring(value.IndexOf(VcardConstants._argumentDelimiter) + 1); + + // Populate the fields + return InstallInfo(bdayValue, finalArgs, altId, cardVersion); + } + + private BirthDateInfo InstallInfo(string value, int altId, Version cardVersion) => + InstallInfo(value, [], altId, cardVersion); + + private BirthDateInfo InstallInfo(string value, string[] finalArgs, int altId, Version cardVersion) + { + // Populate field + DateTime bday; + if (int.TryParse(value, out _) && value.Length == 8) + { + int birthNum = int.Parse(value); + 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(value); + + // Add the fetched information + bool altIdSupported = cardVersion.Major >= 4; + BirthDateInfo _time = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], bday); + return _time; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(BirthDateInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(BirthDateInfo source, BirthDateInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.BirthDate == target.BirthDate + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -480211805; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + BirthDate.GetHashCode(); + return hashCode; + } + + /// + public static bool operator ==(BirthDateInfo left, BirthDateInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(BirthDateInfo left, BirthDateInfo right) => + !(left == right); + + internal BirthDateInfo() { } + + internal BirthDateInfo(int altId, string[] altArguments, DateTime? birth) + { + AltId = altId; + AltArguments = altArguments; + BirthDate = birth; + } + } +} diff --git a/VisualCard/Parts/Implementations/CategoryInfo.cs b/VisualCard/Parts/Implementations/CategoryInfo.cs new file mode 100644 index 0000000..4865013 --- /dev/null +++ b/VisualCard/Parts/Implementations/CategoryInfo.cs @@ -0,0 +1,136 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact category info + /// + [DebuggerDisplay("Category = {Category}")] + public class CategoryInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's categories + /// + public string[] Category { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new CategoryInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new CategoryInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + return + $"{VcardConstants._categoriesSpecifier}:" + + $"{Category}"; + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string categoryValue = value.Substring(VcardConstants._categoriesSpecifier.Length + 1); + + // Populate the fields + return InstallInfo(categoryValue); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + private CategoryInfo InstallInfo(string value) + { + // Populate field + var categories = Regex.Unescape(value).Split(','); + + // Add the fetched information + CategoryInfo _time = new(0, [], categories); + return _time; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(CategoryInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(CategoryInfo source, CategoryInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.Category == target.Category + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1152977432; + hashCode = hashCode * -1521134295 + base.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Category); + return hashCode; + } + + /// + public static bool operator ==(CategoryInfo left, CategoryInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(CategoryInfo left, CategoryInfo right) => + !(left == right); + + internal CategoryInfo() { } + + internal CategoryInfo(int altId, string[] altArguments, string[] category) + { + AltId = altId; + AltArguments = altArguments; + Category = category; + } + } +} diff --git a/VisualCard/Parts/Implementations/EmailInfo.cs b/VisualCard/Parts/Implementations/EmailInfo.cs new file mode 100644 index 0000000..9773759 --- /dev/null +++ b/VisualCard/Parts/Implementations/EmailInfo.cs @@ -0,0 +1,182 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Mail; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact e-mail information + /// + [DebuggerDisplay("E-mail = {ContactEmailAddress}")] + public class EmailInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's email types + /// + public string[] ContactEmailTypes { get; } + /// + /// The contact's email address + /// + public string ContactEmailAddress { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new EmailInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new EmailInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._emailSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + + $"{ContactEmailAddress}"; + } + else + { + return + $"{VcardConstants._emailSpecifier};" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactEmailTypes)}{VcardConstants._argumentDelimiter}" + + $"{ContactEmailAddress}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); + string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); + + // Populate the fields + return InstallInfo(splitMail, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string mailValue = value.Substring(VcardConstants._emailSpecifier.Length + 1); + string[] splitMail = mailValue.Split(VcardConstants._argumentDelimiter); + if (splitMail.Length < 2) + throw new InvalidDataException("E-mail field must specify exactly two values (Type (must be prepended with TYPE=), and a valid e-mail address)"); + + // Populate the fields + return InstallInfo(splitMail, finalArgs, altId, cardVersion); + } + + private EmailInfo InstallInfo(string[] splitMail, int altId, Version cardVersion) => + InstallInfo(splitMail, [], altId, cardVersion); + + private EmailInfo InstallInfo(string[] splitMail, string[] finalArgs, int altId, Version cardVersion) + { + MailAddress mail; + bool altIdSupported = cardVersion.Major >= 4; + bool installType = splitMail.Length > 1; + bool specifierRequired = cardVersion.Major >= 3; + + // Try to create mail address + try + { + mail = new MailAddress(installType ? splitMail[1] : splitMail[0]); + } + catch (ArgumentException aex) + { + throw new InvalidDataException("E-mail address is invalid", aex); + } + + // Populate the fields + string[] _emailTypes = installType ? VcardParserTools.GetTypes(splitMail, "HOME", specifierRequired) : ["HOME"]; + string _emailAddress = mail.Address; + EmailInfo _address = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _emailTypes, _emailAddress); + return _address; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(EmailInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(EmailInfo source, EmailInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.ContactEmailTypes.SequenceEqual(target.ContactEmailTypes) && + source.AltId == target.AltId && + source.ContactEmailAddress == target.ContactEmailAddress + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 2091849342; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactEmailTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactEmailAddress); + return hashCode; + } + + /// + public static bool operator ==(EmailInfo left, EmailInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(EmailInfo left, EmailInfo right) => + !(left == right); + + internal EmailInfo() { } + + internal EmailInfo(int altId, string[] altArguments, string[] contactEmailTypes, string contactEmailAddress) + { + AltId = altId; + AltArguments = altArguments; + ContactEmailTypes = contactEmailTypes; + ContactEmailAddress = contactEmailAddress; + } + } +} diff --git a/VisualCard/Parts/Implementations/GeoInfo.cs b/VisualCard/Parts/Implementations/GeoInfo.cs new file mode 100644 index 0000000..582232b --- /dev/null +++ b/VisualCard/Parts/Implementations/GeoInfo.cs @@ -0,0 +1,171 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact geographical information + /// + [DebuggerDisplay("Geography = {Geo}")] + public class GeoInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's geographical information types + /// + public string[] GeoTypes { get; } + /// + /// The contact's geographical information + /// + public string Geo { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new GeoInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new GeoInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._geoSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + + $"{Geo}"; + } + else + { + return + $"{VcardConstants._geoSpecifier}:" + + $"{Geo}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string geoValue = value.Substring(VcardConstants._geoSpecifier.Length + 1); + string _geoStr = Regex.Unescape(geoValue); + + // Populate the fields + return InstallInfo([_geoStr], false, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Populate the fields + return InstallInfo(splitGeo, true, finalArgs, altId, cardVersion); + } + + private GeoInfo InstallInfo(string[] splitGeo, bool installType, int altId, Version cardVersion) => + InstallInfo(splitGeo, installType, [], altId, cardVersion); + + private GeoInfo InstallInfo(string[] splitGeo, bool installType, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + bool typesSupported = cardVersion.Major >= 3; + + string[] _geoTypes = typesSupported ? installType ? VcardParserTools.GetValues(splitGeo, "", VcardConstants._valueArgumentSpecifier) : ["uri"] : []; + string _geoStr = Regex.Unescape(typesSupported ? installType ? splitGeo[1] : splitGeo[0] : splitGeo[0]); + + // Populate the fields + GeoInfo _geo = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _geoTypes, _geoStr); + return _geo; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(GeoInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(GeoInfo source, GeoInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.GeoTypes.SequenceEqual(target.GeoTypes) && + source.AltId == target.AltId && + source.Geo == target.Geo + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -772623698; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(GeoTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Geo); + return hashCode; + } + + /// + public static bool operator ==(GeoInfo left, GeoInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(GeoInfo left, GeoInfo right) => + !(left == right); + + internal GeoInfo() { } + + internal GeoInfo(int altId, string[] altArguments, string[] geoTypes, string geo) + { + AltId = altId; + AltArguments = altArguments; + GeoTypes = geoTypes; + Geo = geo; + } + } +} diff --git a/VisualCard/Parts/Implementations/ImppInfo.cs b/VisualCard/Parts/Implementations/ImppInfo.cs new file mode 100644 index 0000000..aba5ec2 --- /dev/null +++ b/VisualCard/Parts/Implementations/ImppInfo.cs @@ -0,0 +1,178 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact IMPP info + /// + [DebuggerDisplay("IMPP info = {ContactIMPP}")] + public class ImppInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's IMPP information, such as SIP and XMPP + /// + public string ContactIMPP { get; } + /// + /// The contact's IMPP info types + /// + public string[] ImppTypes { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new ImppInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new ImppInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + bool installType = ImppTypes.Length > 0 && ImppTypes[0].ToUpper() != "HOME"; + return + $"{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}"; + } + else + { + bool installType = ImppTypes.Length > 0 && ImppTypes[0].ToUpper() != "HOME"; + return + $"{VcardConstants._imppSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ImppTypes)}{VcardConstants._argumentDelimiter}" : "")}" + + $"{ContactIMPP}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); + string[] _imppTypes = ["HOME"]; + + // Populate the fields + return InstallInfo(_imppTypes, imppValue, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + bool specifierRequired = cardVersion.Major >= 3; + + // Get the value + 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)"); + + // Install the values + string[] _imppTypes = VcardParserTools.GetTypes(splitImpp, "SIP", specifierRequired); + + // Populate the fields + return InstallInfo(_imppTypes, imppValue, finalArgs, altId, cardVersion); + } + + private ImppInfo InstallInfo(string[] types, string imppValue, int altId, Version cardVersion) => + InstallInfo(types, imppValue, [], altId, cardVersion); + + private ImppInfo InstallInfo(string[] types, string imppValue, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + string _impp = + imppValue.Contains(':') ? + Regex.Unescape(imppValue.Substring(imppValue.IndexOf(":") + 1)) : + Regex.Unescape(imppValue); + + ImppInfo _imppInstance = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _impp, types); + return _imppInstance; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(ImppInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(ImppInfo source, ImppInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.ImppTypes.SequenceEqual(target.ImppTypes) && + source.AltId == target.AltId && + source.ContactIMPP == target.ContactIMPP + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -700274766; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactIMPP); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ImppTypes); + return hashCode; + } + + /// + public static bool operator ==(ImppInfo left, ImppInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(ImppInfo left, ImppInfo right) => + !(left == right); + + internal ImppInfo() { } + + internal ImppInfo(int altId, string[] altArguments, string contactImpp, string[] imppTypes) + { + AltId = altId; + AltArguments = altArguments; + ContactIMPP = contactImpp; + ImppTypes = imppTypes; + } + } +} diff --git a/VisualCard/Parts/Implementations/LabelAddressInfo.cs b/VisualCard/Parts/Implementations/LabelAddressInfo.cs new file mode 100644 index 0000000..7ce5c32 --- /dev/null +++ b/VisualCard/Parts/Implementations/LabelAddressInfo.cs @@ -0,0 +1,182 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact address information + /// + [DebuggerDisplay("LabelAddress = {DeliveryLabel}")] + public class LabelAddressInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's address types + /// + public string[] AddressTypes { get; } + /// + /// The contact's delivery address label + /// + public string DeliveryLabel { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new LabelAddressInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new LabelAddressInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._labelSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + + $"{DeliveryLabel}"; + } + else + { + return + $"{VcardConstants._labelSpecifier};" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + + $"{DeliveryLabel}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); + string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); + + // Check the provided address + string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); + if (splitAddressValues.Length < 1) + throw new InvalidDataException("Label address information must specify exactly one value (address label)"); + + // Populate the fields + return InstallInfo(splitAdr, splitAddressValues, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); + string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); + if (splitAdr.Length < 2) + throw new InvalidDataException("Label address field must specify exactly two values (Type (optionally prepended with TYPE=), and address information)"); + + // Check the provided address + string[] splitAddressValues = splitAdr[1].Split(VcardConstants._fieldDelimiter); + if (splitAddressValues.Length < 1) + throw new InvalidDataException("Label address information must specify exactly one value (address label)"); + + // Populate the fields + return InstallInfo(splitAdr, splitAddressValues, finalArgs, altId, cardVersion); + } + + private LabelAddressInfo InstallInfo(string[] splitAdr, string[] splitAddressValues, int altId, Version cardVersion) => + InstallInfo(splitAdr, splitAddressValues, [], altId, cardVersion); + + private LabelAddressInfo InstallInfo(string[] splitAdr, string[] splitAddressValues, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + bool defaultType = splitAdr.Length < 2; + bool specifierRequired = cardVersion.Major >= 3; + + // Populate the fields + string[] _addressTypes = defaultType ? ["HOME"] : VcardParserTools.GetTypes(splitAdr, "HOME", specifierRequired); + string _addressLabel = Regex.Unescape(splitAddressValues[0]); + LabelAddressInfo _address = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _addressTypes, _addressLabel); + return _address; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(LabelAddressInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(LabelAddressInfo source, LabelAddressInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AddressTypes.SequenceEqual(target.AddressTypes) && + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.DeliveryLabel == target.DeliveryLabel + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1313918102; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AddressTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(DeliveryLabel); + return hashCode; + } + + /// + public static bool operator ==(LabelAddressInfo left, LabelAddressInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(LabelAddressInfo left, LabelAddressInfo right) => + !(left == right); + + internal LabelAddressInfo() { } + + internal LabelAddressInfo(int altId, string[] altArguments, string[] addressTypes, string label) + { + AltId = altId; + AltArguments = altArguments; + AddressTypes = addressTypes; + DeliveryLabel = label; + } + } +} diff --git a/VisualCard/Parts/Implementations/LogoInfo.cs b/VisualCard/Parts/Implementations/LogoInfo.cs new file mode 100644 index 0000000..415db0e --- /dev/null +++ b/VisualCard/Parts/Implementations/LogoInfo.cs @@ -0,0 +1,225 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact logo info + /// + [DebuggerDisplay("Logo, {Encoding}, {LogoType}, {ValueType}")] + public class LogoInfo : BaseCardPartInfo, IEquatable + { + /// + /// Value type + /// + public string ValueType { get; } + /// + /// Logo encoding type + /// + public string Encoding { get; } + /// + /// Logo type (JPEG, ...) + /// + public string LogoType { get; } + /// + /// Encoded logo + /// + public string LogoEncoded { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new LogoInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new LogoInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + if (ValueType == "uri" || ValueType == "url") + { + return + $"{VcardConstants._logoSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + + $"{LogoEncoded}"; + } + else + { + string logoArgsLine = + $"{VcardConstants._logoSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; + return logoArgsLine + BaseVcardParser.MakeStringBlock(LogoEncoded, logoArgsLine.Length); + } + } + else + { + if (ValueType == "uri" || ValueType == "url") + { + return + $"{VcardConstants._logoSpecifier};" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + + $"{LogoEncoded}"; + } + else + { + string logoArgsLine = + $"{VcardConstants._logoSpecifier};" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; + return logoArgsLine + BaseVcardParser.MakeStringBlock(LogoEncoded, logoArgsLine.Length); + } + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + FromStringVcardWithTypeInternal(value, [], altId, cardVersion, cardContentReader); + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Populate the fields + return InstallInfo(splitLogo, finalArgs, altId, cardVersion, cardContentReader); + } + + private LogoInfo InstallInfo(string[] splitLogo, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Check to see if the value is prepended by the VALUE= argument + string valueType = VcardParserTools.GetValuesString(splitLogo, "", VcardConstants._valueArgumentSpecifier).ToLower(); + bool isUrl = valueType == "url" || valueType == "uri"; + + // Check to see if the value is prepended by the ENCODING= argument + string logoEncoding = VcardParserTools.GetValuesString(splitLogo, "BASE64", VcardConstants._encodingArgumentSpecifier); + + // Check to see if the value is prepended with the TYPE= argument + string logoType = VcardParserTools.GetTypesString(splitLogo, "JPEG", false); + + // Now, get the encoded logo + StringBuilder encodedLogo = new(); + if (splitLogo.Length == 2) + encodedLogo.Append(splitLogo[1]); + + // Make sure to get all the blocks until we reach an empty line + if (!isUrl) + { + string lineToBeAppended = cardContentReader.ReadLine(); + while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) + { + encodedLogo.Append(lineToBeAppended.Trim()); + lineToBeAppended = cardContentReader.ReadLine(); + } + } + + // Populate the fields + LogoInfo _logo = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], valueType, logoEncoding, logoType, encodedLogo.ToString()); + return _logo; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(LogoInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(LogoInfo source, LogoInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.ValueType == target.ValueType && + source.Encoding == target.Encoding && + source.LogoType == target.LogoType && + source.LogoEncoded == target.LogoEncoded + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -1881924127; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ValueType); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Encoding); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(LogoType); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(LogoEncoded); + return hashCode; + } + + /// + public static bool operator ==(LogoInfo left, LogoInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(LogoInfo left, LogoInfo right) => + !(left == right); + + internal LogoInfo() { } + + internal LogoInfo(int altId, string[] altArguments, string valueType, string encoding, string logoType, string logoEncoded) + { + AltId = altId; + AltArguments = altArguments; + ValueType = valueType; + Encoding = encoding; + LogoType = logoType; + LogoEncoded = logoEncoded; + } + } +} diff --git a/VisualCard/Parts/Implementations/NameInfo.cs b/VisualCard/Parts/Implementations/NameInfo.cs new file mode 100644 index 0000000..ebb1f2d --- /dev/null +++ b/VisualCard/Parts/Implementations/NameInfo.cs @@ -0,0 +1,210 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Name information + /// + [DebuggerDisplay("FirstName = {ContactFirstName}, LastName = {ContactLastName}")] + public class NameInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's first name + /// + public string ContactFirstName { get; } + /// + /// The contact's last name + /// + public string ContactLastName { get; } + /// + /// The contact's alternative names + /// + public string[] AltNames { get; } + /// + /// The contact's prefixes + /// + public string[] Prefixes { get; } + /// + /// The contact's suffixes + /// + public string[] Suffixes { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new NameInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new NameInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + string altNamesStr = string.Join(VcardConstants._valueDelimiter.ToString(), AltNames); + string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); + string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); + return + $"{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}"; + } + else + { + string altNamesStr = string.Join(VcardConstants._valueDelimiter.ToString(), AltNames); + string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); + string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); + return + $"{VcardConstants._nameSpecifier}:" + + $"{ContactLastName}{VcardConstants._fieldDelimiter}" + + $"{ContactFirstName}{VcardConstants._fieldDelimiter}" + + $"{altNamesStr}{VcardConstants._fieldDelimiter}" + + $"{prefixesStr}{VcardConstants._fieldDelimiter}" + + $"{suffixesStr}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Check the line + 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)"); + + // Populate the fields + return InstallInfo(splitName, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Check the line + 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) + 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)"); + + // Populate the fields + return InstallInfo(splitName, finalArgs, altId, cardVersion); + } + + private NameInfo InstallInfo(string[] splitName, int altId, Version cardVersion) => + InstallInfo(splitName, [], altId, cardVersion); + + private NameInfo InstallInfo(string[] splitName, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Populate fields + string _lastName = Regex.Unescape(splitName[0]); + string _firstName = Regex.Unescape(splitName[1]); + string[] _altNames = splitName.Length >= 3 ? Regex.Unescape(splitName[2]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; + string[] _prefixes = splitName.Length >= 4 ? Regex.Unescape(splitName[3]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; + string[] _suffixes = splitName.Length >= 5 ? Regex.Unescape(splitName[4]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; + NameInfo _name = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _firstName, _lastName, _altNames, _prefixes, _suffixes); + return _name; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(NameInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(NameInfo source, NameInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltNames.SequenceEqual(target.AltNames) && + source.Prefixes.SequenceEqual(target.Prefixes) && + source.Suffixes.SequenceEqual(target.Suffixes) && + source.AltId == target.AltId && + source.ContactFirstName == target.ContactFirstName && + source.ContactLastName == target.ContactLastName + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 357851718; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactFirstName); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactLastName); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltNames); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Prefixes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Suffixes); + return hashCode; + } + + /// + public static bool operator ==(NameInfo left, NameInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(NameInfo left, NameInfo right) => + !(left == right); + + internal NameInfo() { } + + internal NameInfo(int altId, string[] altArguments, string contactFirstName, string contactLastName, string[] altNames, string[] prefixes, string[] suffixes) + { + AltId = altId; + AltArguments = altArguments; + ContactFirstName = contactFirstName; + ContactLastName = contactLastName; + AltNames = altNames; + Prefixes = prefixes; + Suffixes = suffixes; + } + } +} diff --git a/VisualCard/Parts/NicknameInfo.cs b/VisualCard/Parts/Implementations/NicknameInfo.cs similarity index 54% rename from VisualCard/Parts/NicknameInfo.cs rename to VisualCard/Parts/Implementations/NicknameInfo.cs index 1e71d84..535df6f 100644 --- a/VisualCard/Parts/NicknameInfo.cs +++ b/VisualCard/Parts/Implementations/NicknameInfo.cs @@ -25,22 +25,14 @@ using System.Text.RegularExpressions; using VisualCard.Parsers; -namespace VisualCard.Parts +namespace VisualCard.Parts.Implementations { /// /// Contact nickname info /// [DebuggerDisplay("Nickname = {ContactNickname}")] - public class NicknameInfo : IEquatable + public class NicknameInfo : BaseCardPartInfo, IEquatable { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } /// /// The contact's nickname /// @@ -50,6 +42,71 @@ public class NicknameInfo : IEquatable /// public string[] NicknameTypes { get; } + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new NicknameInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new NicknameInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._nicknameSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", NicknameTypes)}{VcardConstants._argumentDelimiter}" + + $"{ContactNickname}"; + } + else + { + return + $"{VcardConstants._nicknameSpecifier};" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", NicknameTypes)}{VcardConstants._argumentDelimiter}" + + $"{ContactNickname}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); + + // Populate the fields + return InstallInfo([nickValue], altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Populate the fields + return InstallInfo(splitNick, finalArgs, altId, cardVersion); + } + + private NicknameInfo InstallInfo(string[] splitNick, int altId, Version cardVersion) => + InstallInfo(splitNick, [], altId, cardVersion); + + private NicknameInfo InstallInfo(string[] splitNick, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + bool installType = splitNick.Length > 1; + bool specifierRequired = cardVersion.Major >= 3; + + // Populate the fields + string[] _nicknameTypes = installType ? VcardParserTools.GetTypes(splitNick, "WORK", specifierRequired) : ["HOME"]; + string _nick = Regex.Unescape(installType ? splitNick[1] : splitNick[0]); + NicknameInfo _nickInstance = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _nick, _nicknameTypes); + return _nickInstance; + } + /// public override bool Equals(object obj) => base.Equals(obj); @@ -94,92 +151,13 @@ public override int GetHashCode() return hashCode; } - internal string ToStringVcardTwo() - { - throw new NotImplementedException(); - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._nicknameSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", NicknameTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactNickname}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._nicknameSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", NicknameTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactNickname}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static NicknameInfo FromStringVcardThree(string value) - { - // Get the value - string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); - - // Populate the fields - string[] _nicknameTypes = ["HOME"]; - string _nick = Regex.Unescape(nickValue); - NicknameInfo _nickInstance = new(0, [], _nick, _nicknameTypes); - return _nickInstance; - } - - internal static NicknameInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Populate the fields - string[] _nicknameTypes = VcardParserTools.GetTypes(splitNick, "WORK", true); - string _nick = Regex.Unescape(splitNick[1]); - NicknameInfo _nickInstance = new(0, [], _nick, _nicknameTypes); - return _nickInstance; - } - - internal static NicknameInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string nickValue = value.Substring(VcardConstants._nicknameSpecifier.Length + 1); - - // Populate the fields - string[] _nicknameTypes = ["HOME"]; - string _nick = Regex.Unescape(nickValue); - NicknameInfo _nickInstance = new(altId, [], _nick, _nicknameTypes); - return _nickInstance; - } - - internal static NicknameInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - 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)"); - - // Populate the fields - string[] _nicknameTypes = VcardParserTools.GetTypes(splitNick, "WORK", true); - string _nick = Regex.Unescape(splitNick[1]); - NicknameInfo _nickInstance = new(altId, [.. finalArgs], _nick, _nicknameTypes); - return _nickInstance; - } - - internal static NicknameInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); + /// + public static bool operator ==(NicknameInfo left, NicknameInfo right) => + EqualityComparer.Default.Equals(left, right); - internal static NicknameInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); + /// + public static bool operator !=(NicknameInfo left, NicknameInfo right) => + !(left == right); internal NicknameInfo() { } diff --git a/VisualCard/Parts/Implementations/OrganizationInfo.cs b/VisualCard/Parts/Implementations/OrganizationInfo.cs new file mode 100644 index 0000000..40a3dfb --- /dev/null +++ b/VisualCard/Parts/Implementations/OrganizationInfo.cs @@ -0,0 +1,199 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact organization info + /// + [DebuggerDisplay("OrgName = {Name}, {Unit}, {Role}")] + public class OrganizationInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's organization types + /// + public string[] OrgTypes { get; } + /// + /// The contact's organization name + /// + public string Name { get; } + /// + /// The contact's organization unit + /// + public string Unit { get; } + /// + /// The contact's organization unit's role + /// + public string Role { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new OrganizationInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new OrganizationInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + bool installType = (installAltId || OrgTypes.Length > 0) && OrgTypes[0].ToUpper() != "WORK"; + return + $"{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}" + + $"{Unit}{VcardConstants._fieldDelimiter}" + + $"{Role}"; + } + else + { + bool installType = OrgTypes.Length > 0 && OrgTypes[0].ToUpper() != "WORK"; + return + $"{VcardConstants._orgSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", OrgTypes)}{VcardConstants._argumentDelimiter}" : "")}" + + $"{Name}{VcardConstants._fieldDelimiter}" + + $"{Unit}{VcardConstants._fieldDelimiter}" + + $"{Role}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); + string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); + + // Populate the fields + return InstallInfo(splitOrg, ["WORK"], altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + bool specifierRequired = cardVersion.Major >= 3; + + // Get the value + 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)"); + + // Check the provided organization + string[] splitOrganizationValues = splitOrg[1].Split(VcardConstants._fieldDelimiter); + if (splitOrganizationValues.Length < 3) + throw new InvalidDataException("Organization information must specify exactly three values (name, unit, and role)"); + + // Populate the fields + string[] _orgTypes = VcardParserTools.GetTypes(splitOrg, "WORK", specifierRequired); + return InstallInfo(splitOrganizationValues, _orgTypes, finalArgs, altId, cardVersion); + } + + private OrganizationInfo InstallInfo(string[] splitOrg, string[] _orgTypes, int altId, Version cardVersion) => + InstallInfo(splitOrg, _orgTypes, [], altId, cardVersion); + + private OrganizationInfo InstallInfo(string[] splitOrg, string[] _orgTypes, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Populate the fields + string _orgName = Regex.Unescape(splitOrg[0]); + string _orgUnit = Regex.Unescape(splitOrg.Length >= 2 ? splitOrg[1] : ""); + string _orgUnitRole = Regex.Unescape(splitOrg.Length >= 3 ? splitOrg[2] : ""); + OrganizationInfo _org = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _orgName, _orgUnit, _orgUnitRole, _orgTypes); + return _org; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(OrganizationInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(OrganizationInfo source, OrganizationInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.OrgTypes.SequenceEqual(target.OrgTypes) && + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.Name == target.Name && + source.Unit == target.Unit && + source.Role == target.Role + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 374840165; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(OrgTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Unit); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Role); + return hashCode; + } + + /// + public static bool operator ==(OrganizationInfo left, OrganizationInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(OrganizationInfo left, OrganizationInfo right) => + !(left == right); + + internal OrganizationInfo() { } + + internal OrganizationInfo(int altId, string[] altArguments, string name, string unit, string role, string[] orgTypes) + { + AltId = altId; + AltArguments = altArguments; + Name = name; + Unit = unit; + Role = role; + OrgTypes = orgTypes; + } + } +} diff --git a/VisualCard/Parts/Implementations/PhotoInfo.cs b/VisualCard/Parts/Implementations/PhotoInfo.cs new file mode 100644 index 0000000..eaa482d --- /dev/null +++ b/VisualCard/Parts/Implementations/PhotoInfo.cs @@ -0,0 +1,225 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact photo info + /// + [DebuggerDisplay("Photo, {Encoding}, {PhotoType}, {ValueType}")] + public class PhotoInfo : BaseCardPartInfo, IEquatable + { + /// + /// Value type + /// + public string ValueType { get; } + /// + /// Photo encoding type + /// + public string Encoding { get; } + /// + /// Photo type (JPEG, ...) + /// + public string PhotoType { get; } + /// + /// Encoded photo + /// + public string PhotoEncoded { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new PhotoInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new PhotoInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + if (ValueType == "uri" || ValueType == "url") + { + return + $"{VcardConstants._photoSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + + $"{PhotoEncoded}"; + } + else + { + string photoArgsLine = + $"{VcardConstants._photoSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; + return photoArgsLine + BaseVcardParser.MakeStringBlock(PhotoEncoded, photoArgsLine.Length); + } + } + else + { + if (ValueType == "uri" || ValueType == "url") + { + return + $"{VcardConstants._photoSpecifier};" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + + $"{PhotoEncoded}"; + } + else + { + string photoArgsLine = + $"{VcardConstants._photoSpecifier};" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; + return photoArgsLine + BaseVcardParser.MakeStringBlock(PhotoEncoded, photoArgsLine.Length); + } + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + FromStringVcardWithTypeInternal(value, [], altId, cardVersion, cardContentReader); + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Populate the fields + return InstallInfo(splitPhoto, finalArgs, altId, cardVersion, cardContentReader); + } + + private PhotoInfo InstallInfo(string[] splitPhoto, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Check to see if the value is prepended by the VALUE= argument + string valueType = VcardParserTools.GetValuesString(splitPhoto, "", VcardConstants._valueArgumentSpecifier).ToLower(); + bool isUrl = valueType == "url" || valueType == "uri"; + + // Check to see if the value is prepended by the ENCODING= argument + string photoEncoding = VcardParserTools.GetValuesString(splitPhoto, "BASE64", VcardConstants._encodingArgumentSpecifier); + + // Check to see if the value is prepended with the TYPE= argument + string photoType = VcardParserTools.GetTypesString(splitPhoto, "JPEG", false); + + // Now, get the encoded photo + StringBuilder encodedPhoto = new(); + if (splitPhoto.Length == 2) + encodedPhoto.Append(splitPhoto[1]); + + // Make sure to get all the blocks until we reach an empty line + if (!isUrl) + { + string lineToBeAppended = cardContentReader.ReadLine(); + while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) + { + encodedPhoto.Append(lineToBeAppended.Trim()); + lineToBeAppended = cardContentReader.ReadLine(); + } + } + + // Populate the fields + PhotoInfo _photo = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], valueType, photoEncoding, photoType, encodedPhoto.ToString()); + return _photo; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(PhotoInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(PhotoInfo source, PhotoInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.ValueType == target.ValueType && + source.Encoding == target.Encoding && + source.PhotoType == target.PhotoType && + source.PhotoEncoded == target.PhotoEncoded + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -1042689907; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ValueType); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Encoding); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PhotoType); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PhotoEncoded); + return hashCode; + } + + /// + public static bool operator ==(PhotoInfo left, PhotoInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(PhotoInfo left, PhotoInfo right) => + !(left == right); + + internal PhotoInfo() { } + + internal PhotoInfo(int altId, string[] altArguments, string valueType, string encoding, string photoType, string photoEncoded) + { + AltId = altId; + AltArguments = altArguments; + ValueType = valueType; + Encoding = encoding; + PhotoType = photoType; + PhotoEncoded = photoEncoded; + } + } +} diff --git a/VisualCard/Parts/Implementations/RevisionInfo.cs b/VisualCard/Parts/Implementations/RevisionInfo.cs new file mode 100644 index 0000000..948a404 --- /dev/null +++ b/VisualCard/Parts/Implementations/RevisionInfo.cs @@ -0,0 +1,140 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Card revision info + /// + [DebuggerDisplay("Revision = {Revision}")] + public class RevisionInfo : BaseCardPartInfo, IEquatable + { + /// + /// The card's revision + /// + public DateTime? Revision { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new RevisionInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new RevisionInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) => + $"{VcardConstants._revSpecifier}:{Revision:yyyy-MM-dd HH:mm:ss}"; + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string revValue = value.Substring(VcardConstants._revSpecifier.Length + 1); + + // Populate the fields + return InstallInfo(revValue, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string revValue = value.Substring(VcardConstants._revSpecifier.Length + 1); + + // Populate the fields + return InstallInfo(revValue, finalArgs, altId, cardVersion); + } + + private RevisionInfo InstallInfo(string value, int altId, Version cardVersion) => + InstallInfo(value, [], altId, cardVersion); + + private RevisionInfo InstallInfo(string value, string[] finalArgs, int altId, Version cardVersion) + { + // Populate field + DateTime rev = DateTime.Parse(value); + + // Add the fetched information + bool altIdSupported = cardVersion.Major >= 4; + RevisionInfo _time = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], rev); + return _time; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(RevisionInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(RevisionInfo source, RevisionInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.Revision == target.Revision + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -480211805; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + Revision.GetHashCode(); + return hashCode; + } + + /// + public static bool operator ==(RevisionInfo left, RevisionInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(RevisionInfo left, RevisionInfo right) => + !(left == right); + + internal RevisionInfo() { } + + internal RevisionInfo(int altId, string[] altArguments, DateTime? birth) + { + AltId = altId; + AltArguments = altArguments; + Revision = birth; + } + } +} diff --git a/VisualCard/Parts/RoleInfo.cs b/VisualCard/Parts/Implementations/RoleInfo.cs similarity index 56% rename from VisualCard/Parts/RoleInfo.cs rename to VisualCard/Parts/Implementations/RoleInfo.cs index 22b86e0..94376f8 100644 --- a/VisualCard/Parts/RoleInfo.cs +++ b/VisualCard/Parts/Implementations/RoleInfo.cs @@ -20,30 +20,79 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using VisualCard.Parsers; -namespace VisualCard.Parts +namespace VisualCard.Parts.Implementations { /// /// Contact role info /// [DebuggerDisplay("Role = {ContactRole}")] - public class RoleInfo : IEquatable + public class RoleInfo : BaseCardPartInfo, IEquatable { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } /// /// The contact's role /// public string ContactRole { get; } + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new RoleInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new RoleInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._roleSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + + $"{ContactRole}"; + } + else + { + return + $"{VcardConstants._roleSpecifier};" + + $"{ContactRole}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); + + // Populate the fields + return InstallInfo(roleValue, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); + + // Populate the fields + return InstallInfo(roleValue, finalArgs, altId, cardVersion); + } + + private RoleInfo InstallInfo(string roleValue, int altId, Version cardVersion) => + InstallInfo(roleValue, [], altId, cardVersion); + + private RoleInfo InstallInfo(string roleValue, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Populate the fields + RoleInfo _telephone = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], roleValue); + return _telephone; + } + /// public override bool Equals(object obj) => base.Equals(obj); @@ -86,78 +135,13 @@ public override int GetHashCode() return hashCode; } - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._roleSpecifier};" + - $"{ContactRole}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._roleSpecifier};" + - $"{ContactRole}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._roleSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + - $"{ContactRole}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static RoleInfo FromStringVcardTwo(string value) - { - // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); - - // Populate the fields - RoleInfo _role = new(0, [], roleValue); - return _role; - } - - internal static RoleInfo FromStringVcardThree(string value) - { - // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); - - // Populate the fields - RoleInfo _role = new(0, [], roleValue); - return _role; - } - - internal static RoleInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); - - // Populate the fields - RoleInfo _role = new(altId, [], roleValue); - return _role; - } - - internal static RoleInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - string roleValue = value.Substring(VcardConstants._roleSpecifier.Length + 1); - - // Populate the fields - RoleInfo _role = new(altId, [.. finalArgs], roleValue); - return _role; - } - - internal static RoleInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); + /// + public static bool operator ==(RoleInfo left, RoleInfo right) => + EqualityComparer.Default.Equals(left, right); - internal static RoleInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); + /// + public static bool operator !=(RoleInfo left, RoleInfo right) => + !(left == right); internal RoleInfo() { } diff --git a/VisualCard/Parts/Implementations/SoundInfo.cs b/VisualCard/Parts/Implementations/SoundInfo.cs new file mode 100644 index 0000000..1846c4f --- /dev/null +++ b/VisualCard/Parts/Implementations/SoundInfo.cs @@ -0,0 +1,225 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact sound info + /// + [DebuggerDisplay("Sound, {Encoding}, {SoundType}, {ValueType}")] + public class SoundInfo : BaseCardPartInfo, IEquatable + { + /// + /// Value type + /// + public string ValueType { get; } + /// + /// Sound encoding type + /// + public string Encoding { get; } + /// + /// Sound type (MP3, ...) + /// + public string SoundType { get; } + /// + /// Encoded sound + /// + public string SoundEncoded { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new SoundInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new SoundInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + if (ValueType == "uri" || ValueType == "url") + { + return + $"{VcardConstants._soundSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + + $"{SoundEncoded}"; + } + else + { + string soundArgsLine = + $"{VcardConstants._soundSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; + return soundArgsLine + BaseVcardParser.MakeStringBlock(SoundEncoded, soundArgsLine.Length); + } + } + else + { + if (ValueType == "uri" || ValueType == "url") + { + return + $"{VcardConstants._soundSpecifier};" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + + $"{SoundEncoded}"; + } + else + { + string soundArgsLine = + $"{VcardConstants._soundSpecifier};" + + $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + + $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; + return soundArgsLine + BaseVcardParser.MakeStringBlock(SoundEncoded, soundArgsLine.Length); + } + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + FromStringVcardWithTypeInternal(value, [], altId, cardVersion, cardContentReader); + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Populate the fields + return InstallInfo(splitSound, finalArgs, altId, cardVersion, cardContentReader); + } + + private SoundInfo InstallInfo(string[] splitSound, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Check to see if the value is prepended by the VALUE= argument + string valueType = VcardParserTools.GetValuesString(splitSound, "", VcardConstants._valueArgumentSpecifier).ToLower(); + bool isUrl = valueType == "url" || valueType == "uri"; + + // Check to see if the value is prepended by the ENCODING= argument + string soundEncoding = VcardParserTools.GetValuesString(splitSound, "BASE64", VcardConstants._encodingArgumentSpecifier); + + // Check to see if the value is prepended with the TYPE= argument + string soundType = VcardParserTools.GetTypesString(splitSound, "WAVE", false); + + // Now, get the encoded sound + StringBuilder encodedSound = new(); + if (splitSound.Length == 2) + encodedSound.Append(splitSound[1]); + + // Make sure to get all the blocks until we reach an empty line + if (!isUrl) + { + string lineToBeAppended = cardContentReader.ReadLine(); + while (!string.IsNullOrWhiteSpace(lineToBeAppended)) + { + encodedSound.Append(lineToBeAppended); + lineToBeAppended = cardContentReader.ReadLine()?.Trim(); + } + } + + // Populate the fields + SoundInfo _sound = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], valueType, soundEncoding, soundType, encodedSound.ToString()); + return _sound; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(SoundInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(SoundInfo source, SoundInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.ValueType == target.ValueType && + source.Encoding == target.Encoding && + source.SoundType == target.SoundType && + source.SoundEncoded == target.SoundEncoded + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 21154477; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ValueType); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Encoding); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SoundType); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SoundEncoded); + return hashCode; + } + + /// + public static bool operator ==(SoundInfo left, SoundInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(SoundInfo left, SoundInfo right) => + !(left == right); + + internal SoundInfo() { } + + internal SoundInfo(int altId, string[] altArguments, string valueType, string encoding, string soundType, string soundEncoded) + { + AltId = altId; + AltArguments = altArguments; + ValueType = valueType; + Encoding = encoding; + SoundType = soundType; + SoundEncoded = soundEncoded; + } + } +} diff --git a/VisualCard/Parts/Implementations/TelephoneInfo.cs b/VisualCard/Parts/Implementations/TelephoneInfo.cs new file mode 100644 index 0000000..6c29426 --- /dev/null +++ b/VisualCard/Parts/Implementations/TelephoneInfo.cs @@ -0,0 +1,171 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact telephone number information + /// + [DebuggerDisplay("Telephone = {ContactPhoneNumber}")] + public class TelephoneInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's phone types + /// + public string[] ContactPhoneTypes { get; } + /// + /// The contact's phone number + /// + public string ContactPhoneNumber { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new TelephoneInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new TelephoneInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._telephoneSpecifier};" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + + $"{ContactPhoneNumber}"; + } + else + { + return + $"{VcardConstants._telephoneSpecifier};" + + $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + + $"{ContactPhoneNumber}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); + + // Populate the fields + return InstallInfo([telValue], altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + 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)"); + + // Populate the fields + return InstallInfo(splitTel, finalArgs, altId, cardVersion); + } + + private TelephoneInfo InstallInfo(string[] splitTel, int altId, Version cardVersion) => + InstallInfo(splitTel, [], altId, cardVersion); + + private TelephoneInfo InstallInfo(string[] splitTel, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + bool installType = splitTel.Length > 1; + bool specifierRequired = cardVersion.Major >= 3; + + // Populate the fields + string[] _telephoneTypes = installType ? VcardParserTools.GetTypes(splitTel, "CELL", specifierRequired) : ["CELL"]; + string _telephoneNumber = Regex.Unescape(installType ? splitTel[1] : splitTel[0]); + TelephoneInfo _telephone = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _telephoneTypes, _telephoneNumber); + return _telephone; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(TelephoneInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(TelephoneInfo source, TelephoneInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.ContactPhoneTypes.SequenceEqual(target.ContactPhoneTypes) && + source.AltArguments.SequenceEqual(target.AltArguments) && + source.AltId == target.AltId && + source.ContactPhoneNumber == target.ContactPhoneNumber + ; + } + + /// + public override int GetHashCode() + { + int hashCode = -986063477; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactPhoneTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactPhoneNumber); + return hashCode; + } + + /// + public static bool operator ==(TelephoneInfo left, TelephoneInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(TelephoneInfo left, TelephoneInfo right) => + !(left == right); + + internal TelephoneInfo() { } + + internal TelephoneInfo(int altId, string[] altArguments, string[] contactPhoneTypes, string contactPhoneNumber) + { + AltId = altId; + AltArguments = altArguments; + ContactPhoneTypes = contactPhoneTypes; + ContactPhoneNumber = contactPhoneNumber; + } + } +} diff --git a/VisualCard/Parts/Implementations/TimeDateZoneInfo.cs b/VisualCard/Parts/Implementations/TimeDateZoneInfo.cs new file mode 100644 index 0000000..68140b2 --- /dev/null +++ b/VisualCard/Parts/Implementations/TimeDateZoneInfo.cs @@ -0,0 +1,171 @@ +// +// VisualCard Copyright (C) 2021-2024 Aptivi +// +// This file is part of VisualCard +// +// VisualCard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VisualCard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using VisualCard.Parsers; + +namespace VisualCard.Parts.Implementations +{ + /// + /// Contact time zone info + /// + [DebuggerDisplay("Time zone = {TimeZone}")] + public class TimeDateZoneInfo : BaseCardPartInfo, IEquatable + { + /// + /// The contact's time zone types + /// + public string[] TimeZoneTypes { get; } + /// + /// The contact's time zone + /// + public string TimeZone { get; } + + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new TimeDateZoneInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new TimeDateZoneInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{VcardConstants._timeZoneSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + + $"{TimeZone}"; + } + else + { + return + $"{VcardConstants._timeZoneSpecifier}:" + + $"{TimeZone}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); + + // Populate the fields + return InstallInfo([tzValue], false, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Check the line + 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)"); + + // Populate the fields + return InstallInfo(splitTz, true, finalArgs, altId, cardVersion); + } + + private TimeDateZoneInfo InstallInfo(string[] splitTz, bool installType, int altId, Version cardVersion) => + InstallInfo(splitTz, installType, [], altId, cardVersion); + + private TimeDateZoneInfo InstallInfo(string[] splitTz, bool installType, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + bool typesSupported = cardVersion.Major >= 3; + + // Get the types and the number + string[] _geoTypes = typesSupported ? installType ? VcardParserTools.GetValues(splitTz, "", VcardConstants._valueArgumentSpecifier) : ["uri-offset"] : []; + string _geoStr = Regex.Unescape(typesSupported ? installType ? splitTz[1] : splitTz[0] : splitTz[0]); + + // Add the fetched information + TimeDateZoneInfo _timeZone = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _geoTypes, _geoStr); + return _timeZone; + } + + /// + public override bool Equals(object obj) => + base.Equals(obj); + + /// + /// Checks to see if both the parts are equal + /// + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(TimeDateZoneInfo other) => + Equals(this, other); + + /// + /// Checks to see if both the parts are equal + /// + /// The source instance to check to see if they equal + /// The target instance to check to see if they equal + /// True if all the part elements are equal. Otherwise, false. + public bool Equals(TimeDateZoneInfo source, TimeDateZoneInfo target) + { + // We can't perform this operation on null. + if (source is null) + return false; + + // Check all the properties + return + source.AltArguments.SequenceEqual(target.AltArguments) && + source.TimeZoneTypes.SequenceEqual(target.TimeZoneTypes) && + source.AltId == target.AltId && + source.TimeZone == target.TimeZone + ; + } + + /// + public override int GetHashCode() + { + int hashCode = 1304261678; + hashCode = hashCode * -1521134295 + AltId.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(TimeZoneTypes); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(TimeZone); + return hashCode; + } + + /// + public static bool operator ==(TimeDateZoneInfo left, TimeDateZoneInfo right) => + EqualityComparer.Default.Equals(left, right); + + /// + public static bool operator !=(TimeDateZoneInfo left, TimeDateZoneInfo right) => + !(left == right); + + internal TimeDateZoneInfo() { } + + internal TimeDateZoneInfo(int altId, string[] altArguments, string[] timeZoneTypes, string timeZone) + { + AltId = altId; + AltArguments = altArguments; + TimeZoneTypes = timeZoneTypes; + TimeZone = timeZone; + } + } +} diff --git a/VisualCard/Parts/TitleInfo.cs b/VisualCard/Parts/Implementations/TitleInfo.cs similarity index 54% rename from VisualCard/Parts/TitleInfo.cs rename to VisualCard/Parts/Implementations/TitleInfo.cs index 2462d5b..c717b51 100644 --- a/VisualCard/Parts/TitleInfo.cs +++ b/VisualCard/Parts/Implementations/TitleInfo.cs @@ -20,31 +20,80 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using VisualCard.Parsers; -namespace VisualCard.Parts +namespace VisualCard.Parts.Implementations { /// /// Contact job title info /// [DebuggerDisplay("Job title = {ContactTitle}")] - public class TitleInfo : IEquatable + public class TitleInfo : BaseCardPartInfo, IEquatable { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } /// /// The contact's title /// public string ContactTitle { get; } + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new TitleInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new TitleInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + return + $"{(installAltId ? $"{VcardConstants._titleSpecifier};" : $"{VcardConstants._titleSpecifier}:")}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + + $"{ContactTitle}"; + } + else + { + return + $"{VcardConstants._titleSpecifier}:" + + $"{ContactTitle}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); + + // Populate the fields + return InstallInfo([titleValue], altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); + string[] splitTitleParts = titleValue.Split(VcardConstants._argumentDelimiter); + + // Populate the fields + return InstallInfo(splitTitleParts, finalArgs, altId, cardVersion); + } + + private TitleInfo InstallInfo(string[] splitTitleParts, int altId, Version cardVersion) => + InstallInfo(splitTitleParts, [], altId, cardVersion); + + private TitleInfo InstallInfo(string[] splitTitleParts, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + string _title = Regex.Unescape(splitTitleParts.Length > 1 ? splitTitleParts[1] : splitTitleParts[0]); + TitleInfo _titleInfo = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _title); + return _titleInfo; + } + /// public override bool Equals(object obj) => base.Equals(obj); @@ -87,83 +136,13 @@ public override int GetHashCode() return hashCode; } - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._titleSpecifier}:" + - $"{ContactTitle}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._titleSpecifier}:" + - $"{ContactTitle}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{(installAltId ? $"{VcardConstants._titleSpecifier};" : $"{VcardConstants._titleSpecifier}:")}" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + - $"{ContactTitle}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static TitleInfo FromStringVcardTwo(string value) - { - // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); - - // Populate field - string _title = Regex.Unescape(titleValue); - TitleInfo title = new(0, [], _title); - return title; - } - - internal static TitleInfo FromStringVcardThree(string value) - { - // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); - - // Populate field - string _title = Regex.Unescape(titleValue); - TitleInfo title = new(0, [], _title); - return title; - } - - internal static TitleInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); - - // Populate field - string _title = Regex.Unescape(titleValue); - TitleInfo title = new(altId, [], _title); - return title; - } - - internal static TitleInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - string titleValue = value.Substring(VcardConstants._titleSpecifier.Length + 1); - string[] splitTitleParts = titleValue.Split(VcardConstants._argumentDelimiter); - - // Populate field - string _title = Regex.Unescape(splitTitleParts[1]); - TitleInfo title = new(altId, [.. finalArgs], _title); - return title; - } - - internal static TitleInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); + /// + public static bool operator ==(TitleInfo left, TitleInfo right) => + EqualityComparer.Default.Equals(left, right); - internal static TitleInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); + /// + public static bool operator !=(TitleInfo left, TitleInfo right) => + !(left == right); internal TitleInfo() { } diff --git a/VisualCard/Parts/XNameInfo.cs b/VisualCard/Parts/Implementations/XNameInfo.cs similarity index 54% rename from VisualCard/Parts/XNameInfo.cs rename to VisualCard/Parts/Implementations/XNameInfo.cs index 6f95913..d9e41cb 100644 --- a/VisualCard/Parts/XNameInfo.cs +++ b/VisualCard/Parts/Implementations/XNameInfo.cs @@ -20,25 +20,18 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using VisualCard.Parsers; -namespace VisualCard.Parts +namespace VisualCard.Parts.Implementations { /// /// Contact non-standard field entry information /// [DebuggerDisplay("Non-standard = {XKeyName}")] - public class XNameInfo : IEquatable + public class XNameInfo : BaseCardPartInfo, IEquatable { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } /// /// X- key name /// @@ -52,6 +45,78 @@ public class XNameInfo : IEquatable /// public string[] XValues { get; } + internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) => + new XNameInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader); + + internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) => + new XNameInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader); + + internal override string ToStringVcardInternal(Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + if (altIdSupported) + { + bool installAltId = AltId >= 0 && AltArguments.Length > 0; + bool installType = installAltId && XKeyTypes.Length > 0; + return + $"{VcardConstants._xSpecifier}" + + $"{XKeyName}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + + $"{(XKeyTypes.Length > 0 ? string.Join(VcardConstants._fieldDelimiter.ToString(), XKeyTypes) + VcardConstants._argumentDelimiter : "")}" + + $"{string.Join(VcardConstants._fieldDelimiter.ToString(), XValues)}"; + } + else + { + return + $"{VcardConstants._xSpecifier}" + + $"{XKeyName}{(XKeyTypes.Length > 0 ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + + $"{(XKeyTypes.Length > 0 ? string.Join(VcardConstants._fieldDelimiter.ToString(), XKeyTypes) + VcardConstants._argumentDelimiter : "")}" + + $"{string.Join(VcardConstants._fieldDelimiter.ToString(), XValues)}"; + } + } + + internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string xValue = value.Substring(VcardConstants._xSpecifier.Length); + string[] splitX = xValue.Split(VcardConstants._argumentDelimiter); + + // Populate the fields + return InstallInfo(splitX, altId, cardVersion); + } + + internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) + { + // Get the value + string xValue = value.Substring(VcardConstants._xSpecifier.Length); + string[] splitX = xValue.Split(VcardConstants._argumentDelimiter); + + // Populate the fields + return InstallInfo(splitX, finalArgs, altId, cardVersion); + } + + private XNameInfo InstallInfo(string[] splitX, int altId, Version cardVersion) => + InstallInfo(splitX, [], altId, cardVersion); + + private XNameInfo InstallInfo(string[] splitX, string[] finalArgs, int altId, Version cardVersion) + { + bool altIdSupported = cardVersion.Major >= 4; + + // Populate the name + string _xName = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? + splitX[0].Substring(0, splitX[0].IndexOf(VcardConstants._fieldDelimiter)) : + splitX[0]; + + // Populate the fields + string[] _xTypes = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? + splitX[0].Substring(splitX[0].IndexOf(VcardConstants._fieldDelimiter) + 1) + .Split(VcardConstants._fieldDelimiter) : + []; + string[] _xValues = splitX[1].Split(VcardConstants._fieldDelimiter); + XNameInfo _x = new(altIdSupported ? altId : 0, altIdSupported ? finalArgs : [], _xName, _xValues, _xTypes); + return _x; + } + /// public override bool Equals(object obj) => base.Equals(obj); @@ -98,99 +163,13 @@ public override int GetHashCode() return hashCode; } - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._xSpecifier}" + - $"{XKeyName}{(XKeyTypes.Length > 0 ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(XKeyTypes.Length > 0 ? string.Join(VcardConstants._fieldDelimiter.ToString(), XKeyTypes) + VcardConstants._argumentDelimiter : "")}" + - $"{string.Join(VcardConstants._fieldDelimiter.ToString(), XValues)}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._xSpecifier}" + - $"{XKeyName}{(XKeyTypes.Length > 0 ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(XKeyTypes.Length > 0 ? string.Join(VcardConstants._fieldDelimiter.ToString(), XKeyTypes) + VcardConstants._argumentDelimiter : "")}" + - $"{string.Join(VcardConstants._fieldDelimiter.ToString(), XValues)}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - bool installType = installAltId && XKeyTypes.Length > 0; - return - $"{VcardConstants._xSpecifier}" + - $"{XKeyName}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(XKeyTypes.Length > 0 ? string.Join(VcardConstants._fieldDelimiter.ToString(), XKeyTypes) + VcardConstants._argumentDelimiter : "")}" + - $"{string.Join(VcardConstants._fieldDelimiter.ToString(), XValues)}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static XNameInfo FromStringVcardTwo(string value) - { - string xValue = value.Substring(VcardConstants._xSpecifier.Length); - string[] splitX = xValue.Split(VcardConstants._argumentDelimiter); - string _xName = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? - splitX[0].Substring(0, splitX[0].IndexOf(VcardConstants._fieldDelimiter)) : - splitX[0]; - string[] _xTypes = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? - splitX[0].Substring(splitX[0].IndexOf(VcardConstants._fieldDelimiter) + 1) - .Split(VcardConstants._fieldDelimiter) : - []; - string[] _xValues = splitX[1].Split(VcardConstants._fieldDelimiter); - XNameInfo _x = new(0, [], _xName, _xValues, _xTypes); - return _x; - } - - internal static XNameInfo FromStringVcardThree(string value) - { - // Get the value - string xValue = value.Substring(VcardConstants._xSpecifier.Length); - string[] splitX = xValue.Split(VcardConstants._argumentDelimiter); - - // Populate the name - string _xName = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? - splitX[0].Substring(0, splitX[0].IndexOf(VcardConstants._fieldDelimiter)) : - splitX[0]; - - // Populate the fields - string[] _xTypes = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? - splitX[0].Substring(splitX[0].IndexOf(VcardConstants._fieldDelimiter) + 1) - .Split(VcardConstants._fieldDelimiter) : - []; - string[] _xValues = splitX[1].Split(VcardConstants._fieldDelimiter); - XNameInfo _x = new(0, [], _xName, _xValues, _xTypes); - return _x; - } - - internal static XNameInfo FromStringVcardFour(string value, List finalArgs, int altId) - { - // Get the value - string xValue = value.Substring(VcardConstants._xSpecifier.Length); - string[] splitX = xValue.Split(VcardConstants._argumentDelimiter); - - // Populate the name - string _xName = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? - splitX[0].Substring(0, splitX[0].IndexOf(VcardConstants._fieldDelimiter)) : - splitX[0]; - - // Populate the fields - string[] _xTypes = splitX[0].Contains(VcardConstants._fieldDelimiter.ToString()) ? - splitX[0].Substring(splitX[0].IndexOf(VcardConstants._fieldDelimiter) + 1) - .Split(VcardConstants._fieldDelimiter) : - []; - string[] _xValues = splitX[1].Split(VcardConstants._fieldDelimiter); - XNameInfo _x = new(altId, [.. finalArgs], _xName, _xValues, _xTypes); - return _x; - } + /// + public static bool operator ==(XNameInfo left, XNameInfo right) => + EqualityComparer.Default.Equals(left, right); - internal static XNameInfo FromStringVcardFive(string value, List finalArgs, int altId) => - FromStringVcardFour(value, finalArgs, altId); + /// + public static bool operator !=(XNameInfo left, XNameInfo right) => + !(left == right); internal XNameInfo() { } diff --git a/VisualCard/Parts/ImppInfo.cs b/VisualCard/Parts/ImppInfo.cs deleted file mode 100644 index 4b966d8..0000000 --- a/VisualCard/Parts/ImppInfo.cs +++ /dev/null @@ -1,222 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact IMPP info - /// - [DebuggerDisplay("IMPP info = {ContactIMPP}")] - public class ImppInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's IMPP information, such as SIP and XMPP - /// - public string ContactIMPP { get; } - /// - /// The contact's IMPP info types - /// - public string[] ImppTypes { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(ImppInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(ImppInfo source, ImppInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.ImppTypes.SequenceEqual(target.ImppTypes) && - source.AltId == target.AltId && - source.ContactIMPP == target.ContactIMPP - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -700274766; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactIMPP); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ImppTypes); - return hashCode; - } - - internal string ToStringVcardTwo() - { - bool installType = ImppTypes.Length > 0 && ImppTypes[0].ToUpper() != "HOME"; - return - $"{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._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 - $"{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 string ToStringVcardFive() => - ToStringVcardFour(); - - internal static ImppInfo FromStringVcardTwo(string value) - { - string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); - string[] _imppTypes = ["HOME"]; - string _impp = Regex.Unescape(imppValue); - ImppInfo _imppInstance = new(0, [], _impp, _imppTypes); - return _imppInstance; - } - - internal static ImppInfo FromStringVcardTwoWithType(string value) - { - 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)"); - - // Install the values - string[] _imppTypes = VcardParserTools.GetTypes(splitImpp, "SIP", false); - string _impp = Regex.Unescape(imppValue.Substring(imppValue.IndexOf(":") + 1)); - ImppInfo _imppInstance = new(0, [], _impp, _imppTypes); - return _imppInstance; - } - - internal static ImppInfo FromStringVcardThree(string value) - { - // Get the value - string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); - - // Populate the fields - string[] _imppTypes = ["HOME"]; - string _impp = Regex.Unescape(imppValue); - ImppInfo _imppInstance = new(0, [], _impp, _imppTypes); - return _imppInstance; - } - - internal static ImppInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Install the values - string[] _imppTypes = VcardParserTools.GetTypes(splitImpp, "SIP", true); - string _impp = Regex.Unescape(imppValue.Substring(imppValue.IndexOf(":") + 1)); - ImppInfo _imppInstance = new(0, [], _impp, _imppTypes); - return _imppInstance; - } - - internal static ImppInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string imppValue = value.Substring(VcardConstants._imppSpecifier.Length + 1); - - // Populate the fields - string[] _imppTypes = ["HOME"]; - string _impp = Regex.Unescape(imppValue); - ImppInfo _imppInstance = new(altId, [], _impp, _imppTypes); - return _imppInstance; - } - - internal static ImppInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - 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)"); - - // Install the values - string[] _imppTypes = VcardParserTools.GetTypes(splitImpp, "SIP", true); - string _impp = Regex.Unescape(imppValue.Substring(imppValue.IndexOf(":") + 1)); - ImppInfo _imppInstance = new(altId, [.. finalArgs], _impp, _imppTypes); - return _imppInstance; - } - - internal static ImppInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static ImppInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal ImppInfo() { } - - internal ImppInfo(int altId, string[] altArguments, string contactImpp, string[] imppTypes) - { - AltId = altId; - AltArguments = altArguments; - ContactIMPP = contactImpp; - ImppTypes = imppTypes; - } - } -} diff --git a/VisualCard/Parts/LabelAddressInfo.cs b/VisualCard/Parts/LabelAddressInfo.cs deleted file mode 100644 index 86930af..0000000 --- a/VisualCard/Parts/LabelAddressInfo.cs +++ /dev/null @@ -1,247 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact address information - /// - [DebuggerDisplay("LabelAddress = {DeliveryLabel}")] - public class LabelAddressInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's address types - /// - public string[] AddressTypes { get; } - /// - /// The contact's delivery address label - /// - public string DeliveryLabel { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(LabelAddressInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(LabelAddressInfo source, LabelAddressInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AddressTypes.SequenceEqual(target.AddressTypes) && - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.DeliveryLabel == target.DeliveryLabel - ; - } - - /// - public override int GetHashCode() - { - int hashCode = 1313918102; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AddressTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(DeliveryLabel); - return hashCode; - } - - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._labelSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + - $"{DeliveryLabel}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._labelSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + - $"{DeliveryLabel}"; - } - - internal string ToStringVcardFive() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._labelSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", AddressTypes)}{VcardConstants._argumentDelimiter}" + - $"{DeliveryLabel}"; - } - - internal static LabelAddressInfo FromStringVcardTwo(string value) - { - // Get the value - string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); - string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); - - // Check the provided address - string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); - if (splitAddressValues.Length < 1) - throw new InvalidDataException("Label address information must specify exactly one value (address label)"); - - // Populate the fields - string[] _addressTypes = ["HOME"]; - string _addressLabel = Regex.Unescape(splitAddressValues[0]); - LabelAddressInfo _address = new(0, [], _addressTypes, _addressLabel); - return _address; - } - - internal static LabelAddressInfo FromStringVcardTwoWithType(string value) - { - // Get the value - string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); - string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); - if (splitAdr.Length < 2) - throw new InvalidDataException("Label address field must specify exactly two values (Type (optionally prepended with TYPE=), and address information)"); - - // Check the provided address - string[] splitAddressValues = splitAdr[1].Split(VcardConstants._fieldDelimiter); - if (splitAddressValues.Length < 1) - throw new InvalidDataException("Label address information must specify exactly one value (address label)"); - - // Populate the fields - string[] _addressTypes = VcardParserTools.GetTypes(splitAdr, "HOME", false); - string _addressLabel = Regex.Unescape(splitAddressValues[0]); - LabelAddressInfo _address = new(0, [], _addressTypes, _addressLabel); - return _address; - } - - internal static LabelAddressInfo FromStringVcardThree(string value) - { - // Get the value - string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); - string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); - - // Check the provided address - string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); - if (splitAddressValues.Length < 1) - throw new InvalidDataException("Label address information must specify exactly one value (address label)"); - - // Populate the fields - string[] _addressTypes = ["HOME"]; - string _addressLabel = Regex.Unescape(splitAddressValues[0]); - LabelAddressInfo _address = new(0, [], _addressTypes, _addressLabel); - return _address; - } - - internal static LabelAddressInfo FromStringVcardThreeWithType(string value) - { - // Get the value - string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); - string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); - if (splitAdr.Length < 2) - throw new InvalidDataException("Label address field must specify exactly two values (Type (must be prepended with TYPE=), and address information)"); - - // Check the provided address - string[] splitAddressValues = splitAdr[1].Split(VcardConstants._fieldDelimiter); - if (splitAddressValues.Length < 1) - throw new InvalidDataException("Label address information must specify exactly one value (address label)"); - - // Populate the fields - string[] _addressTypes = VcardParserTools.GetTypes(splitAdr, "HOME", true); - string _addressLabel = Regex.Unescape(splitAddressValues[0]); - LabelAddressInfo _address = new(0, [], _addressTypes, _addressLabel); - return _address; - } - - internal static LabelAddressInfo FromStringVcardFive(string value, int altId) - { - // Get the value - string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); - string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); - - // Check the provided address - string[] splitAddressValues = splitAdr[0].Split(VcardConstants._fieldDelimiter); - if (splitAddressValues.Length < 1) - throw new InvalidDataException("Label address information must specify exactly one value (address label)"); - - // Populate the fields - string[] _addressTypes = ["HOME"]; - string _addressLabel = Regex.Unescape(splitAddressValues[0]); - LabelAddressInfo _address = new(altId, [], _addressTypes, _addressLabel); - return _address; - } - - internal static LabelAddressInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) - { - // Get the value - string adrValue = value.Substring(VcardConstants._labelSpecifier.Length + 1); - string[] splitAdr = adrValue.Split(VcardConstants._argumentDelimiter); - if (splitAdr.Length < 2) - throw new InvalidDataException("Label address field must specify exactly two values (Type (must be prepended with TYPE=), and address information)"); - - // Check the provided address - string[] splitAddressValues = splitAdr[1].Split(VcardConstants._fieldDelimiter); - if (splitAddressValues.Length < 1) - throw new InvalidDataException("Label address information must specify exactly one value (address label)"); - - // Populate the fields - string[] _addressTypes = VcardParserTools.GetTypes(splitAdr, "HOME", true); - string _addressLabel = Regex.Unescape(splitAddressValues[0]); - LabelAddressInfo _address = new(altId, [.. finalArgs], _addressTypes, _addressLabel); - return _address; - } - - internal LabelAddressInfo() { } - - internal LabelAddressInfo(int altId, string[] altArguments, string[] addressTypes, string label) - { - AltId = altId; - AltArguments = altArguments; - AddressTypes = addressTypes; - DeliveryLabel = label; - } - } -} diff --git a/VisualCard/Parts/LogoInfo.cs b/VisualCard/Parts/LogoInfo.cs deleted file mode 100644 index fbaf5b7..0000000 --- a/VisualCard/Parts/LogoInfo.cs +++ /dev/null @@ -1,309 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact logo info - /// - [DebuggerDisplay("Logo, {Encoding}, {LogoType}, {ValueType}")] - public class LogoInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// Value type - /// - public string ValueType { get; } - /// - /// Logo encoding type - /// - public string Encoding { get; } - /// - /// Logo type (JPEG, ...) - /// - public string LogoType { get; } - /// - /// Encoded logo - /// - public string LogoEncoded { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(LogoInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(LogoInfo source, LogoInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.ValueType == target.ValueType && - source.Encoding == target.Encoding && - source.LogoType == target.LogoType && - source.LogoEncoded == target.LogoEncoded - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -1881924127; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ValueType); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Encoding); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(LogoType); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(LogoEncoded); - return hashCode; - } - - internal string ToStringVcardTwo() - { - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._logoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{LogoEncoded}"; - } - else - { - string logoArgsLine = - $"{VcardConstants._logoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; - return logoArgsLine + BaseVcardParser.MakeStringBlock(LogoEncoded, logoArgsLine.Length); - } - } - - internal string ToStringVcardThree() - { - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._logoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{LogoEncoded}"; - } - else - { - string logoArgsLine = - $"{VcardConstants._logoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; - return logoArgsLine + BaseVcardParser.MakeStringBlock(LogoEncoded, logoArgsLine.Length); - } - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._logoSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{LogoEncoded}"; - } - else - { - string logoArgsLine = - $"{VcardConstants._logoSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{LogoType}{VcardConstants._argumentDelimiter}"; - return logoArgsLine + BaseVcardParser.MakeStringBlock(LogoEncoded, logoArgsLine.Length); - } - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static LogoInfo FromStringVcardTwoWithType(string value, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitLogo, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string logoEncoding = VcardParserTools.GetValuesString(splitLogo, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string logoType = VcardParserTools.GetTypesString(splitLogo, "JPEG", false); - - // Now, get the encoded logo - StringBuilder encodedLogo = new(); - if (splitLogo.Length == 2) - encodedLogo.Append(splitLogo[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) - { - encodedLogo.Append(lineToBeAppended.Trim()); - lineToBeAppended = cardContentReader.ReadLine(); - } - } - - // Populate the fields - LogoInfo _logo = new(0, [], valueType, logoEncoding, logoType, encodedLogo.ToString()); - return _logo; - } - - internal static LogoInfo FromStringVcardThreeWithType(string value, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitLogo, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string logoEncoding = VcardParserTools.GetValuesString(splitLogo, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string logoType = VcardParserTools.GetTypesString(splitLogo, "JPEG", true); - - // Now, get the encoded logo - StringBuilder encodedLogo = new(); - if (splitLogo.Length == 2) - encodedLogo.Append(splitLogo[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) - { - encodedLogo.Append(lineToBeAppended.Trim()); - lineToBeAppended = cardContentReader.ReadLine(); - } - } - - // Populate the fields - LogoInfo _logo = new(0, [], valueType, logoEncoding, logoType, encodedLogo.ToString()); - return _logo; - } - - internal static LogoInfo FromStringVcardFourWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitLogo, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string logoEncoding = VcardParserTools.GetValuesString(splitLogo, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string logoType = VcardParserTools.GetTypesString(splitLogo, "JPEG", true); - - // Now, get the encoded logo - StringBuilder encodedLogo = new(); - if (splitLogo.Length == 2) - encodedLogo.Append(splitLogo[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) - { - encodedLogo.Append(lineToBeAppended.Trim()); - lineToBeAppended = cardContentReader.ReadLine(); - } - } - - // Populate the fields - LogoInfo _logo = new(altId, [.. finalArgs], valueType, logoEncoding, logoType, encodedLogo.ToString()); - return _logo; - } - - internal static LogoInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) => - FromStringVcardFourWithType(value, finalArgs, altId, cardContentReader); - - internal LogoInfo() { } - - internal LogoInfo(int altId, string[] altArguments, string valueType, string encoding, string logoType, string logoEncoded) - { - AltId = altId; - AltArguments = altArguments; - ValueType = valueType; - Encoding = encoding; - LogoType = logoType; - LogoEncoded = logoEncoded; - } - } -} diff --git a/VisualCard/Parts/NameInfo.cs b/VisualCard/Parts/NameInfo.cs deleted file mode 100644 index e2b4d8d..0000000 --- a/VisualCard/Parts/NameInfo.cs +++ /dev/null @@ -1,265 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Name information - /// - [DebuggerDisplay("FirstName = {ContactFirstName}, LastName = {ContactLastName}")] - public class NameInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's first name - /// - public string ContactFirstName { get; } - /// - /// The contact's last name - /// - public string ContactLastName { get; } - /// - /// The contact's alternative names - /// - public string[] AltNames { get; } - /// - /// The contact's prefixes - /// - public string[] Prefixes { get; } - /// - /// The contact's suffixes - /// - public string[] Suffixes { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(NameInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(NameInfo source, NameInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltNames.SequenceEqual(target.AltNames) && - source.Prefixes.SequenceEqual(target.Prefixes) && - source.Suffixes.SequenceEqual(target.Suffixes) && - source.AltId == target.AltId && - source.ContactFirstName == target.ContactFirstName && - source.ContactLastName == target.ContactLastName - ; - } - - /// - public override int GetHashCode() - { - int hashCode = 357851718; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactFirstName); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactLastName); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltNames); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Prefixes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Suffixes); - return hashCode; - } - - internal string ToStringVcardTwo() - { - string altNamesStr = string.Join(VcardConstants._valueDelimiter.ToString(), AltNames); - string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); - string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); - return - $"{VcardConstants._nameSpecifier}:" + - $"{ContactLastName}{VcardConstants._fieldDelimiter}" + - $"{ContactFirstName}{VcardConstants._fieldDelimiter}" + - $"{altNamesStr}{VcardConstants._fieldDelimiter}" + - $"{prefixesStr}{VcardConstants._fieldDelimiter}" + - $"{suffixesStr}"; - } - - internal string ToStringVcardThree() - { - string altNamesStr = string.Join(VcardConstants._valueDelimiter.ToString(), AltNames); - string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); - string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); - return - $"{VcardConstants._nameSpecifier}:" + - $"{ContactLastName}{VcardConstants._fieldDelimiter}" + - $"{ContactFirstName}{VcardConstants._fieldDelimiter}" + - $"{altNamesStr}{VcardConstants._fieldDelimiter}" + - $"{prefixesStr}{VcardConstants._fieldDelimiter}" + - $"{suffixesStr}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - string altNamesStr = string.Join(VcardConstants._valueDelimiter.ToString(), AltNames); - string prefixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Prefixes); - string suffixesStr = string.Join(VcardConstants._valueDelimiter.ToString(), Suffixes); - return - $"{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}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static NameInfo FromStringVcardTwo(string value) - { - // Check the line - 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)"); - - // Populate fields - string _lastName = Regex.Unescape(splitName[0]); - string _firstName = Regex.Unescape(splitName[1]); - string[] _altNames = splitName.Length >= 3 ? Regex.Unescape(splitName[2]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _prefixes = splitName.Length >= 4 ? Regex.Unescape(splitName[3]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _suffixes = splitName.Length >= 5 ? Regex.Unescape(splitName[4]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - NameInfo _name = new(0, [], _firstName, _lastName, _altNames, _prefixes, _suffixes); - return _name; - } - - internal static NameInfo FromStringVcardThree(string value) - { - // Check the line - 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)"); - - // Populate fields - string _lastName = Regex.Unescape(splitName[0]); - string _firstName = Regex.Unescape(splitName[1]); - string[] _altNames = splitName.Length >= 3 ? Regex.Unescape(splitName[2]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _prefixes = splitName.Length >= 4 ? Regex.Unescape(splitName[3]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _suffixes = splitName.Length >= 5 ? Regex.Unescape(splitName[4]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - NameInfo _name = new(0, [], _firstName, _lastName, _altNames, _prefixes, _suffixes); - return _name; - } - - internal static NameInfo FromStringVcardFour(string[] splitValues, bool idReservedForName) - { - // Check the line - if (splitValues.Length < 2) - throw new InvalidDataException("Name field must specify exactly five values (Last name, first name, alt names, prefixes, and suffixes)"); - - // Check to see if there are any names with altid - if (idReservedForName) - throw new InvalidDataException("Attempted to overwrite name under the main ID."); - - // Populate fields - string _lastName = Regex.Unescape(splitValues[0]); - string _firstName = Regex.Unescape(splitValues[1]); - string[] _altNames = splitValues.Length >= 3 ? Regex.Unescape(splitValues[2]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _prefixes = splitValues.Length >= 4 ? Regex.Unescape(splitValues[3]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _suffixes = splitValues.Length >= 5 ? Regex.Unescape(splitValues[4]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - NameInfo _name = new(0, [], _firstName, _lastName, _altNames, _prefixes, _suffixes); - return _name; - } - - internal static NameInfo FromStringVcardFourWithType(string value, string[] splitArgs, List finalArgs, int altId, List _names, bool idReservedForName) - { - // Check the line - 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) - throw new InvalidDataException("Name field must specify exactly five values (Last name, first name, alt names, prefixes, and suffixes)"); - - // Check the ALTID - if (!splitArgs[0].StartsWith(VcardConstants._altIdArgumentSpecifier)) - throw new InvalidDataException("ALTID must come exactly first"); - - // ALTID: N: has cardinality of *1 - if (idReservedForName && _names.Count > 0 && _names[0].AltId != altId) - throw new InvalidDataException("ALTID may not be different from all the alternative argument names"); - - // Populate fields - string _lastName = Regex.Unescape(splitName[0]); - string _firstName = Regex.Unescape(splitName[1]); - string[] _altNames = splitName.Length >= 3 ? Regex.Unescape(splitName[2]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _prefixes = splitName.Length >= 4 ? Regex.Unescape(splitName[3]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - string[] _suffixes = splitName.Length >= 5 ? Regex.Unescape(splitName[4]).Split(new char[] { VcardConstants._valueDelimiter }, StringSplitOptions.RemoveEmptyEntries) : []; - NameInfo _name = new(altId, [.. finalArgs], _firstName, _lastName, _altNames, _prefixes, _suffixes); - return _name; - } - - internal static NameInfo FromStringVcardFive(string[] splitValues, bool idReservedForName) => - FromStringVcardFour(splitValues, idReservedForName); - - internal static NameInfo FromStringVcardFiveWithType(string value, string[] splitArgs, List finalArgs, int altId, List _names, bool idReservedForName) => - FromStringVcardFourWithType(value, splitArgs, finalArgs, altId, _names, idReservedForName); - - internal NameInfo() { } - - internal NameInfo(int altId, string[] altArguments, string contactFirstName, string contactLastName, string[] altNames, string[] prefixes, string[] suffixes) - { - AltId = altId; - AltArguments = altArguments; - ContactFirstName = contactFirstName; - ContactLastName = contactLastName; - AltNames = altNames; - Prefixes = prefixes; - Suffixes = suffixes; - } - } -} diff --git a/VisualCard/Parts/OrganizationInfo.cs b/VisualCard/Parts/OrganizationInfo.cs deleted file mode 100644 index 0aac5e8..0000000 --- a/VisualCard/Parts/OrganizationInfo.cs +++ /dev/null @@ -1,276 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact organization info - /// - [DebuggerDisplay("OrgName = {Name}, {Unit}, {Role}")] - public class OrganizationInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's organization types - /// - public string[] OrgTypes { get; } - /// - /// The contact's organization name - /// - public string Name { get; } - /// - /// The contact's organization unit - /// - public string Unit { get; } - /// - /// The contact's organization unit's role - /// - public string Role { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(OrganizationInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(OrganizationInfo source, OrganizationInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.OrgTypes.SequenceEqual(target.OrgTypes) && - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.Name == target.Name && - source.Unit == target.Unit && - source.Role == target.Role - ; - } - - /// - public override int GetHashCode() - { - int hashCode = 374840165; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(OrgTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Unit); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Role); - return hashCode; - } - - internal string ToStringVcardTwo() - { - bool installType = OrgTypes.Length > 0 && OrgTypes[0].ToUpper() != "WORK"; - return - $"{VcardConstants._orgSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", OrgTypes)}{VcardConstants._argumentDelimiter}" : "")}" + - $"{Name}{VcardConstants._fieldDelimiter}" + - $"{Unit}{VcardConstants._fieldDelimiter}" + - $"{Role}"; - } - - internal string ToStringVcardThree() - { - bool installType = OrgTypes.Length > 0 && OrgTypes[0].ToUpper() != "WORK"; - return - $"{VcardConstants._orgSpecifier}{(installType ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(installType ? $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", OrgTypes)}{VcardConstants._argumentDelimiter}" : "")}" + - $"{Name}{VcardConstants._fieldDelimiter}" + - $"{Unit}{VcardConstants._fieldDelimiter}" + - $"{Role}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - bool installType = (installAltId || OrgTypes.Length > 0) && OrgTypes[0].ToUpper() != "WORK"; - return - $"{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}" + - $"{Unit}{VcardConstants._fieldDelimiter}" + - $"{Role}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static OrganizationInfo FromStringVcardTwo(string value) - { - // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); - string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); - - // Populate the fields - string[] splitTypes = ["WORK"]; - string _orgName = Regex.Unescape(splitOrg[0]); - string _orgUnit = Regex.Unescape(splitOrg.Length >= 2 ? splitOrg[1] : ""); - string _orgUnitRole = Regex.Unescape(splitOrg.Length >= 3 ? splitOrg[2] : ""); - OrganizationInfo _org = new(0, [], _orgName, _orgUnit, _orgUnitRole, splitTypes); - return _org; - } - - internal static OrganizationInfo FromStringVcardTwoWithType(string value) - { - // Get the value - 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)"); - - // Check the provided organization - string[] splitOrganizationValues = splitOrg[1].Split(VcardConstants._fieldDelimiter); - if (splitOrganizationValues.Length < 3) - throw new InvalidDataException("Organization information must specify exactly three values (name, unit, and role)"); - - // Populate the fields - string _orgName = Regex.Unescape(splitOrganizationValues[0]); - string _orgUnit = Regex.Unescape(splitOrganizationValues.Length >= 2 ? splitOrganizationValues[1] : ""); - string _orgUnitRole = Regex.Unescape(splitOrganizationValues.Length >= 3 ? splitOrganizationValues[2] : ""); - string[] _orgTypes = VcardParserTools.GetTypes(splitOrg, "WORK", false); - OrganizationInfo _org = new(0, [], _orgName, _orgUnit, _orgUnitRole, _orgTypes); - return _org; - } - - internal static OrganizationInfo FromStringVcardThree(string value) - { - // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); - string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); - - // Populate the fields - string[] splitTypes = ["WORK"]; - string _orgName = Regex.Unescape(splitOrg[0]); - string _orgUnit = Regex.Unescape(splitOrg.Length >= 2 ? splitOrg[1] : ""); - string _orgUnitRole = Regex.Unescape(splitOrg.Length >= 3 ? splitOrg[2] : ""); - OrganizationInfo _org = new(0, [], _orgName, _orgUnit, _orgUnitRole, splitTypes); - return _org; - } - - internal static OrganizationInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Check the provided organization - string[] splitOrganizationValues = splitOrg[1].Split(VcardConstants._fieldDelimiter); - if (splitOrganizationValues.Length < 3) - throw new InvalidDataException("Organization information must specify exactly three values (name, unit, and role)"); - - // Populate the fields - string _orgName = Regex.Unescape(splitOrganizationValues[0]); - string _orgUnit = Regex.Unescape(splitOrganizationValues.Length >= 2 ? splitOrganizationValues[1] : ""); - string _orgUnitRole = Regex.Unescape(splitOrganizationValues.Length >= 3 ? splitOrganizationValues[2] : ""); - string[] _orgTypes = VcardParserTools.GetTypes(splitOrg, "WORK", true); - OrganizationInfo _org = new(0, [], _orgName, _orgUnit, _orgUnitRole, _orgTypes); - return _org; - } - - internal static OrganizationInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string orgValue = value.Substring(VcardConstants._orgSpecifier.Length + 1); - string[] splitOrg = orgValue.Split(VcardConstants._fieldDelimiter); - - // Populate the fields - string[] splitTypes = ["WORK"]; - string _orgName = Regex.Unescape(splitOrg[0]); - string _orgUnit = Regex.Unescape(splitOrg.Length >= 2 ? splitOrg[1] : ""); - string _orgUnitRole = Regex.Unescape(splitOrg.Length >= 3 ? splitOrg[2] : ""); - OrganizationInfo _org = new(altId, [], _orgName, _orgUnit, _orgUnitRole, splitTypes); - return _org; - } - - internal static OrganizationInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - 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)"); - - // Check the provided organization - string[] splitOrganizationValues = splitOrg[1].Split(VcardConstants._fieldDelimiter); - if (splitOrganizationValues.Length < 3) - throw new InvalidDataException("Organization information must specify exactly three values (name, unit, and role)"); - - // Populate the fields - string _orgName = Regex.Unescape(splitOrganizationValues[0]); - string _orgUnit = Regex.Unescape(splitOrganizationValues.Length >= 2 ? splitOrganizationValues[1] : ""); - string _orgUnitRole = Regex.Unescape(splitOrganizationValues.Length >= 3 ? splitOrganizationValues[2] : ""); - string[] _orgTypes = VcardParserTools.GetTypes(splitOrg, "WORK", true); - OrganizationInfo _org = new(altId, [.. finalArgs], _orgName, _orgUnit, _orgUnitRole, _orgTypes); - return _org; - } - - internal static OrganizationInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static OrganizationInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal OrganizationInfo() { } - - internal OrganizationInfo(int altId, string[] altArguments, string name, string unit, string role, string[] orgTypes) - { - AltId = altId; - AltArguments = altArguments; - Name = name; - Unit = unit; - Role = role; - OrgTypes = orgTypes; - } - } -} diff --git a/VisualCard/Parts/PhotoInfo.cs b/VisualCard/Parts/PhotoInfo.cs deleted file mode 100644 index 724fd6a..0000000 --- a/VisualCard/Parts/PhotoInfo.cs +++ /dev/null @@ -1,309 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact photo info - /// - [DebuggerDisplay("Photo, {Encoding}, {PhotoType}, {ValueType}")] - public class PhotoInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// Value type - /// - public string ValueType { get; } - /// - /// Photo encoding type - /// - public string Encoding { get; } - /// - /// Photo type (JPEG, ...) - /// - public string PhotoType { get; } - /// - /// Encoded photo - /// - public string PhotoEncoded { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(PhotoInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(PhotoInfo source, PhotoInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.ValueType == target.ValueType && - source.Encoding == target.Encoding && - source.PhotoType == target.PhotoType && - source.PhotoEncoded == target.PhotoEncoded - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -1042689907; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ValueType); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Encoding); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PhotoType); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PhotoEncoded); - return hashCode; - } - - internal string ToStringVcardTwo() - { - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._photoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{PhotoEncoded}"; - } - else - { - string photoArgsLine = - $"{VcardConstants._photoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; - return photoArgsLine + BaseVcardParser.MakeStringBlock(PhotoEncoded, photoArgsLine.Length); - } - } - - internal string ToStringVcardThree() - { - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._photoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{PhotoEncoded}"; - } - else - { - string photoArgsLine = - $"{VcardConstants._photoSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; - return photoArgsLine + BaseVcardParser.MakeStringBlock(PhotoEncoded, photoArgsLine.Length); - } - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._photoSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{PhotoEncoded}"; - } - else - { - string photoArgsLine = - $"{VcardConstants._photoSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{PhotoType}{VcardConstants._argumentDelimiter}"; - return photoArgsLine + BaseVcardParser.MakeStringBlock(PhotoEncoded, photoArgsLine.Length); - } - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static PhotoInfo FromStringVcardTwoWithType(string value, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitPhoto, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string photoEncoding = VcardParserTools.GetValuesString(splitPhoto, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string photoType = VcardParserTools.GetTypesString(splitPhoto, "JPEG", false); - - // Now, get the encoded photo - StringBuilder encodedPhoto = new(); - if (splitPhoto.Length == 2) - encodedPhoto.Append(splitPhoto[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) - { - encodedPhoto.Append(lineToBeAppended.Trim()); - lineToBeAppended = cardContentReader.ReadLine(); - } - } - - // Populate the fields - PhotoInfo _photo = new(0, [], valueType, photoEncoding, photoType, encodedPhoto.ToString()); - return _photo; - } - - internal static PhotoInfo FromStringVcardThreeWithType(string value, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitPhoto, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string photoEncoding = VcardParserTools.GetValuesString(splitPhoto, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string photoType = VcardParserTools.GetTypesString(splitPhoto, "JPEG", true); - - // Now, get the encoded photo - StringBuilder encodedPhoto = new(); - if (splitPhoto.Length == 2) - encodedPhoto.Append(splitPhoto[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) - { - encodedPhoto.Append(lineToBeAppended.Trim()); - lineToBeAppended = cardContentReader.ReadLine(); - } - } - - // Populate the fields - PhotoInfo _photo = new(0, [], valueType, photoEncoding, photoType, encodedPhoto.ToString()); - return _photo; - } - - internal static PhotoInfo FromStringVcardFourWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitPhoto, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string photoEncoding = VcardParserTools.GetValuesString(splitPhoto, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string photoType = VcardParserTools.GetTypesString(splitPhoto, "JPEG", true); - - // Now, get the encoded photo - StringBuilder encodedPhoto = new(); - if (splitPhoto.Length == 2) - encodedPhoto.Append(splitPhoto[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended) && lineToBeAppended.StartsWith(" ")) - { - encodedPhoto.Append(lineToBeAppended.Trim()); - lineToBeAppended = cardContentReader.ReadLine(); - } - } - - // Populate the fields - PhotoInfo _photo = new(altId, [.. finalArgs], valueType, photoEncoding, photoType, encodedPhoto.ToString()); - return _photo; - } - - internal static PhotoInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) => - FromStringVcardFourWithType(value, finalArgs, altId, cardContentReader); - - internal PhotoInfo() { } - - internal PhotoInfo(int altId, string[] altArguments, string valueType, string encoding, string photoType, string photoEncoded) - { - AltId = altId; - AltArguments = altArguments; - ValueType = valueType; - Encoding = encoding; - PhotoType = photoType; - PhotoEncoded = photoEncoded; - } - } -} diff --git a/VisualCard/Parts/SoundInfo.cs b/VisualCard/Parts/SoundInfo.cs deleted file mode 100644 index 5aa0844..0000000 --- a/VisualCard/Parts/SoundInfo.cs +++ /dev/null @@ -1,309 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact sound info - /// - [DebuggerDisplay("Sound, {Encoding}, {SoundType}, {ValueType}")] - public class SoundInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// Value type - /// - public string ValueType { get; } - /// - /// Sound encoding type - /// - public string Encoding { get; } - /// - /// Sound type (MP3, ...) - /// - public string SoundType { get; } - /// - /// Encoded sound - /// - public string SoundEncoded { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(SoundInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(SoundInfo source, SoundInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.ValueType == target.ValueType && - source.Encoding == target.Encoding && - source.SoundType == target.SoundType && - source.SoundEncoded == target.SoundEncoded - ; - } - - /// - public override int GetHashCode() - { - int hashCode = 21154477; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ValueType); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Encoding); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SoundType); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SoundEncoded); - return hashCode; - } - - internal string ToStringVcardTwo() - { - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._soundSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{SoundEncoded}"; - } - else - { - string soundArgsLine = - $"{VcardConstants._soundSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; - return soundArgsLine + BaseVcardParser.MakeStringBlock(SoundEncoded, soundArgsLine.Length); - } - } - - internal string ToStringVcardThree() - { - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._soundSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{SoundEncoded}"; - } - else - { - string soundArgsLine = - $"{VcardConstants._soundSpecifier};" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; - return soundArgsLine + BaseVcardParser.MakeStringBlock(SoundEncoded, soundArgsLine.Length); - } - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - if (ValueType == "uri" || ValueType == "url") - { - return - $"{VcardConstants._soundSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._argumentDelimiter}" + - $"{SoundEncoded}"; - } - else - { - string soundArgsLine = - $"{VcardConstants._soundSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._valueArgumentSpecifier}{ValueType}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._encodingArgumentSpecifier}{Encoding}{VcardConstants._fieldDelimiter}" + - $"{VcardConstants._typeArgumentSpecifier}{SoundType}{VcardConstants._argumentDelimiter}"; - return soundArgsLine + BaseVcardParser.MakeStringBlock(SoundEncoded, soundArgsLine.Length); - } - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static SoundInfo FromStringVcardTwoWithType(string value, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitSound, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string soundEncoding = VcardParserTools.GetValuesString(splitSound, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string soundType = VcardParserTools.GetTypesString(splitSound, "WAVE", false); - - // Now, get the encoded sound - StringBuilder encodedSound = new(); - if (splitSound.Length == 2) - encodedSound.Append(splitSound[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended)) - { - encodedSound.Append(lineToBeAppended); - lineToBeAppended = cardContentReader.ReadLine()?.Trim(); - } - } - - // Populate the fields - SoundInfo _sound = new(0, [], valueType, soundEncoding, soundType, encodedSound.ToString()); - return _sound; - } - - internal static SoundInfo FromStringVcardThreeWithType(string value, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitSound, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string soundEncoding = VcardParserTools.GetValuesString(splitSound, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string soundType = VcardParserTools.GetTypesString(splitSound, "WAVE", true); - - // Now, get the encoded sound - StringBuilder encodedSound = new(); - if (splitSound.Length == 2) - encodedSound.Append(splitSound[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended)) - { - encodedSound.Append(lineToBeAppended); - lineToBeAppended = cardContentReader.ReadLine()?.Trim(); - } - } - - // Populate the fields - SoundInfo _sound = new(0, [], valueType, soundEncoding, soundType, encodedSound.ToString()); - return _sound; - } - - internal static SoundInfo FromStringVcardFourWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) - { - // Get the value - 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)"); - - // Check to see if the value is prepended by the VALUE= argument - string valueType = VcardParserTools.GetValuesString(splitSound, "", VcardConstants._valueArgumentSpecifier).ToLower(); - bool isUrl = valueType == "url" || valueType == "uri"; - - // Check to see if the value is prepended by the ENCODING= argument - string soundEncoding = VcardParserTools.GetValuesString(splitSound, "BASE64", VcardConstants._encodingArgumentSpecifier); - - // Check to see if the value is prepended with the TYPE= argument - string soundType = VcardParserTools.GetTypesString(splitSound, "WAVE", true); - - // Now, get the encoded sound - StringBuilder encodedSound = new(); - if (splitSound.Length == 2) - encodedSound.Append(splitSound[1]); - - // Make sure to get all the blocks until we reach an empty line - if (!isUrl) - { - string lineToBeAppended = cardContentReader.ReadLine(); - while (!string.IsNullOrWhiteSpace(lineToBeAppended)) - { - encodedSound.Append(lineToBeAppended); - lineToBeAppended = cardContentReader.ReadLine()?.Trim(); - } - } - - // Populate the fields - SoundInfo _sound = new(altId, [.. finalArgs], valueType, soundEncoding, soundType, encodedSound.ToString()); - return _sound; - } - - internal static SoundInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId, StreamReader cardContentReader) => - FromStringVcardFourWithType(value, finalArgs, altId, cardContentReader); - - internal SoundInfo() { } - - internal SoundInfo(int altId, string[] altArguments, string valueType, string encoding, string soundType, string soundEncoded) - { - AltId = altId; - AltArguments = altArguments; - ValueType = valueType; - Encoding = encoding; - SoundType = soundType; - SoundEncoded = soundEncoded; - } - } -} diff --git a/VisualCard/Parts/TelephoneInfo.cs b/VisualCard/Parts/TelephoneInfo.cs deleted file mode 100644 index 0deac8b..0000000 --- a/VisualCard/Parts/TelephoneInfo.cs +++ /dev/null @@ -1,223 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact telephone number information - /// - [DebuggerDisplay("Telephone = {ContactPhoneNumber}")] - public class TelephoneInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's phone types - /// - public string[] ContactPhoneTypes { get; } - /// - /// The contact's phone number - /// - public string ContactPhoneNumber { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(TelephoneInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(TelephoneInfo source, TelephoneInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.ContactPhoneTypes.SequenceEqual(target.ContactPhoneTypes) && - source.AltArguments.SequenceEqual(target.AltArguments) && - source.AltId == target.AltId && - source.ContactPhoneNumber == target.ContactPhoneNumber - ; - } - - /// - public override int GetHashCode() - { - int hashCode = -986063477; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactPhoneTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ContactPhoneNumber); - return hashCode; - } - - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._telephoneSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactPhoneNumber}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._telephoneSpecifier};" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactPhoneNumber}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._telephoneSpecifier};" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{VcardConstants._typeArgumentSpecifier}{string.Join(",", ContactPhoneTypes)}{VcardConstants._argumentDelimiter}" + - $"{ContactPhoneNumber}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static TelephoneInfo FromStringVcardTwo(string value) - { - // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); - - // Populate the fields - string[] _telephoneTypes = ["CELL"]; - string _telephoneNumber = Regex.Unescape(telValue); - TelephoneInfo _telephone = new(0, [], _telephoneTypes, _telephoneNumber); - return _telephone; - } - - internal static TelephoneInfo FromStringVcardTwoWithType(string value) - { - // Get the value - 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)"); - - // Populate the fields - string[] _telephoneTypes = VcardParserTools.GetTypes(splitTel, "CELL", false); - string _telephoneNumber = Regex.Unescape(splitTel[1]); - TelephoneInfo _telephone = new(0, [], _telephoneTypes, _telephoneNumber); - return _telephone; - } - - internal static TelephoneInfo FromStringVcardThree(string value) - { - // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); - - // Populate the fields - string[] _telephoneTypes = ["CELL"]; - string _telephoneNumber = Regex.Unescape(telValue); - TelephoneInfo _telephone = new(0, [], _telephoneTypes, _telephoneNumber); - return _telephone; - } - - internal static TelephoneInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Populate the fields - string[] _telephoneTypes = VcardParserTools.GetTypes(splitTel, "CELL", true); - string _telephoneNumber = Regex.Unescape(splitTel[1]); - TelephoneInfo _telephone = new(0, [], _telephoneTypes, _telephoneNumber); - return _telephone; - } - - internal static TelephoneInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string telValue = value.Substring(VcardConstants._telephoneSpecifier.Length + 1); - - // Populate the fields - string[] _telephoneTypes = ["CELL"]; - string _telephoneNumber = Regex.Unescape(telValue); - TelephoneInfo _telephone = new(altId, [], _telephoneTypes, _telephoneNumber); - return _telephone; - } - - internal static TelephoneInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - 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)"); - - // Populate the fields - string[] _telephoneTypes = VcardParserTools.GetTypes(splitTel, "CELL", true); - string _telephoneNumber = Regex.Unescape(splitTel[1]); - TelephoneInfo _telephone = new(altId, [.. finalArgs], _telephoneTypes, _telephoneNumber); - return _telephone; - } - - internal static TelephoneInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static TelephoneInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal TelephoneInfo() { } - - internal TelephoneInfo(int altId, string[] altArguments, string[] contactPhoneTypes, string contactPhoneNumber) - { - AltId = altId; - AltArguments = altArguments; - ContactPhoneTypes = contactPhoneTypes; - ContactPhoneNumber = contactPhoneNumber; - } - } -} diff --git a/VisualCard/Parts/TimeZoneInfo.cs b/VisualCard/Parts/TimeZoneInfo.cs deleted file mode 100644 index 9063ca1..0000000 --- a/VisualCard/Parts/TimeZoneInfo.cs +++ /dev/null @@ -1,222 +0,0 @@ -// -// VisualCard Copyright (C) 2021-2024 Aptivi -// -// This file is part of VisualCard -// -// VisualCard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VisualCard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY, without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using VisualCard.Parsers; - -namespace VisualCard.Parts -{ - /// - /// Contact time zone info - /// - [DebuggerDisplay("Time zone = {TimeZone}")] - public class TimeZoneInfo : IEquatable - { - /// - /// Alternative ID. Zero if unspecified. - /// - public int AltId { get; } - /// - /// Arguments that follow the AltId - /// - public string[] AltArguments { get; } - /// - /// The contact's time zone types - /// - public string[] TimeZoneTypes { get; } - /// - /// The contact's time zone - /// - public string TimeZone { get; } - - /// - public override bool Equals(object obj) => - base.Equals(obj); - - /// - /// Checks to see if both the parts are equal - /// - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(TimeZoneInfo other) => - Equals(this, other); - - /// - /// Checks to see if both the parts are equal - /// - /// The source instance to check to see if they equal - /// The target instance to check to see if they equal - /// True if all the part elements are equal. Otherwise, false. - public bool Equals(TimeZoneInfo source, TimeZoneInfo target) - { - // We can't perform this operation on null. - if (source is null) - return false; - - // Check all the properties - return - source.AltArguments.SequenceEqual(target.AltArguments) && - source.TimeZoneTypes.SequenceEqual(target.TimeZoneTypes) && - source.AltId == target.AltId && - source.TimeZone == target.TimeZone - ; - } - - /// - public override int GetHashCode() - { - int hashCode = 1304261678; - hashCode = hashCode * -1521134295 + AltId.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AltArguments); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(TimeZoneTypes); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(TimeZone); - return hashCode; - } - - internal string ToStringVcardTwo() - { - return - $"{VcardConstants._timeZoneSpecifier}:" + - $"{TimeZone}"; - } - - internal string ToStringVcardThree() - { - return - $"{VcardConstants._timeZoneSpecifier}:" + - $"{TimeZone}"; - } - - internal string ToStringVcardFour() - { - bool installAltId = AltId >= 0 && AltArguments.Length > 0; - return - $"{VcardConstants._timeZoneSpecifier}{(installAltId ? VcardConstants._fieldDelimiter : VcardConstants._argumentDelimiter)}" + - $"{(installAltId ? VcardConstants._altIdArgumentSpecifier + AltId + VcardConstants._fieldDelimiter : "")}" + - $"{(installAltId ? string.Join(VcardConstants._fieldDelimiter.ToString(), AltArguments) + VcardConstants._argumentDelimiter : "")}" + - $"{TimeZone}"; - } - - internal string ToStringVcardFive() => - ToStringVcardFour(); - - internal static TimeZoneInfo FromStringVcardTwo(string value) - { - string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); - string _timeZoneStr = Regex.Unescape(tzValue); - TimeZoneInfo _timeZone = new(0, [], [], _timeZoneStr); - return _timeZone; - } - - internal static TimeZoneInfo FromStringVcardTwoWithType(string value) - { - 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)"); - - // Get the types and the number - string[] _timeZoneTypes = VcardParserTools.GetValues(splitTz, "", VcardConstants._valueArgumentSpecifier); - string _timeZoneNumber = Regex.Unescape(splitTz[1]); - - // Add the fetched information - TimeZoneInfo _timeZone = new(0, [], _timeZoneTypes, _timeZoneNumber); - return _timeZone; - } - - internal static TimeZoneInfo FromStringVcardThree(string value) - { - // Get the value - string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); - - // Populate the fields - string[] _timeZoneTypes = ["uri-offset"]; - string _timeZoneNumber = Regex.Unescape(tzValue); - TimeZoneInfo _timeZone = new(0, [], _timeZoneTypes, _timeZoneNumber); - return _timeZone; - } - - internal static TimeZoneInfo FromStringVcardThreeWithType(string value) - { - // Get the value - 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)"); - - // Get the types and the number - string[] _timeZoneTypes = VcardParserTools.GetValues(splitTz, "", VcardConstants._valueArgumentSpecifier); - string _timeZoneNumber = Regex.Unescape(splitTz[1]); - - // Add the fetched information - TimeZoneInfo _timeZone = new(0, [], _timeZoneTypes, _timeZoneNumber); - return _timeZone; - } - - internal static TimeZoneInfo FromStringVcardFour(string value, int altId) - { - // Get the value - string tzValue = value.Substring(VcardConstants._timeZoneSpecifier.Length + 1); - - // Populate the fields - string[] _timeZoneTypes = ["uri-offset"]; - string _timeZoneNumber = Regex.Unescape(tzValue); - TimeZoneInfo _timeZone = new(altId, [], _timeZoneTypes, _timeZoneNumber); - return _timeZone; - } - - internal static TimeZoneInfo FromStringVcardFourWithType(string value, List finalArgs, int altId) - { - // Get the value - 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)"); - - // Get the types and the number - string[] _timeZoneTypes = VcardParserTools.GetValues(splitTz, "", VcardConstants._valueArgumentSpecifier); - string _timeZoneNumber = Regex.Unescape(splitTz[1]); - - // Add the fetched information - TimeZoneInfo _timeZone = new(altId, [.. finalArgs], _timeZoneTypes, _timeZoneNumber); - return _timeZone; - } - - internal static TimeZoneInfo FromStringVcardFive(string value, int altId) => - FromStringVcardFour(value, altId); - - internal static TimeZoneInfo FromStringVcardFiveWithType(string value, List finalArgs, int altId) => - FromStringVcardFourWithType(value, finalArgs, altId); - - internal TimeZoneInfo() { } - - internal TimeZoneInfo(int altId, string[] altArguments, string[] timeZoneTypes, string timeZone) - { - AltId = altId; - AltArguments = altArguments; - TimeZoneTypes = timeZoneTypes; - TimeZone = timeZone; - } - } -}