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