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; - } - } -}