Skip to content

Commit

Permalink
add - Implemented global line folding
Browse files Browse the repository at this point in the history
---

You can now globally line fold using either a space or a tab character in your vCard file. VisualCard makes sure that folding is handled appropriately before parsing anything.

Line 264 in vcard-40-rfc6350.txt.

---

Type: add
Breaking: False
Doc Required: False
Part: 1/1
  • Loading branch information
AptiviCEO committed Mar 30, 2024
1 parent 48b2f8d commit 16501a4
Show file tree
Hide file tree
Showing 32 changed files with 472 additions and 239 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ end_of_line = crlf
insert_final_newline = true
indent_style = space
indent_size = 2

[*.vcf]
insert_final_newline = true
indent_style = tab
4 changes: 4 additions & 0 deletions VisualCard.ShowContacts/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
"vCard 5.0 test (with agents)": {
"commandName": "Project",
"commandLineArgs": "TestFiles/fourVCard5Agents.vcf"
},
"vCard - Picture test": {
"commandName": "Project",
"commandLineArgs": "TestFiles/picture.vcf"
}
}
}
10 changes: 8 additions & 2 deletions VisualCard.ShowContacts/TestFiles/fourVCard4.vcf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ IMPP;TYPE=HOME:aim:IM
IMPP;TYPE=HOME:msn:Windows LIVE
IMPP;TYPE=HOME:ymsgr:Yahoo
N:Navasquillo;Neville;Neville\,Nevile;Mr.;Jr.
N;ALTID=0;LANGUAGE=de:NAVASQUILLO;Neville;Neville\,Nevile;Mr.;Jr.
N;ALTID=0;LANGUAGE=de:
NAVASQUILLO;
Neville;
Neville\,Nevile;
Mr.;
Jr.
NOTE:Notes
ORG:Organization
TEL;TYPE=work:098-765-4321
Expand All @@ -24,7 +29,8 @@ TEL;TYPE=voice:078-494-6434
TEL;TYPE=home:348-404-8404
TITLE:Title
GENDER:M
X-ANDROID-CUSTOM:vnd.android.cursor.item/nickname;NVL.N;1;;;;;;;;;;;;;
X-ANDROID-CUSTOM:vnd.android.cursor.item/nickname;NVL.N;1
;;;;;;;;;;;;;
END:VCARD

BEGIN:VCARD
Expand Down
10 changes: 9 additions & 1 deletion VisualCard.ShowContacts/TestFiles/fourVCard5Agents.vcf
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ IMPP:aim:john.s
N:Sanders;John;;;
NOTE:Note test for VisualCard
ORG:Acme Co.
AGENT:BEGIN:VCARD\nVERSION:5.0\nFN:Joe Friday\nN:Friday;Joe;;;\nTEL:+1-919-555-7878\nTITLE:Area Administrator\, Assistant\nEMAIL\;TYPE=INTERNET:[email protected]\nEND:VCARD\n
AGENT:
BEGIN:VCARD\n
VERSION:5.0\n
FN:Joe Friday\n
N:Friday;Joe;;;\n
TEL:+1-919-555-7878\n
TITLE:Area Administrator\, Assistant\n
EMAIL\;TYPE=INTERNET:[email protected]\n
END:VCARD\n
TEL;TYPE=cell:495-522-3560
TITLE:Product Manager
X-ANDROID-CUSTOM:vnd.android.cursor.item/nickname;JS;1;;;;;;;;;;;;;
Expand Down
248 changes: 248 additions & 0 deletions VisualCard.ShowContacts/TestFiles/picture.vcf

Large diffs are not rendered by default.

18 changes: 1 addition & 17 deletions VisualCard.ShowContacts/VisualCard.ShowContacts.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,7 @@
</ItemGroup>

<ItemGroup>
<Folder Include="TestFiles\" />
</ItemGroup>

<ItemGroup>
<None Update="TestFiles\four.vcf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestFiles\fourVCard3.vcf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestFiles\fourVCard4.vcf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestFiles\fourVCard5.vcf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestFiles\fourVCard5Agents.vcf">
<None Update="TestFiles\*.vcf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Expand Down
13 changes: 11 additions & 2 deletions VisualCard/CardTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using VisualCard.Parsers;
using System;
using VisualCard.Parts;
using Textify.General;

namespace VisualCard
{
Expand Down Expand Up @@ -79,6 +80,8 @@ public static Card[] GetCards(StreamReader stream)
CardLine = stream.ReadLine();
while (!stream.EndOfStream)
{
bool append = false;

// Skip empty lines
if (string.IsNullOrEmpty(CardLine))
{
Expand All @@ -90,7 +93,9 @@ public static Card[] GetCards(StreamReader stream)
else if (CardLine != VcardConstants._beginText &&
!CardLine.StartsWith(VcardConstants._versionSpecifier) &&
CardLine != VcardConstants._endText)
CardContent.AppendLine(CardLine);
append = true;
if (append)
CardContent.Append(CardLine);

// All VCards must begin with BEGIN:VCARD
if (CardLine != VcardConstants._beginText && !BeginSpotted)
Expand Down Expand Up @@ -126,14 +131,18 @@ public static Card[] GetCards(StreamReader stream)
EndSpotted = true;

// Make a new parser instance
VcardParser CardParser = new(CardContent.ToString(), CardVersion);
string content = CardContent.ToString();
string[] contentLines = content.SplitNewLines();
VcardParser CardParser = new(contentLines, CardVersion);
FinalParsers.Add(CardParser);

// Clear the content in case we want to make a second contact
CardContent.Clear();
BeginSpotted = false;
CardLine = stream.ReadLine();
}
else if (append)
CardContent.AppendLine();
}

// Close the stream to avoid stuck file handle
Expand Down
4 changes: 4 additions & 0 deletions VisualCard/Parsers/VcardConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ internal static class VcardConstants
internal const string _endText = "END:VCARD";
internal const string _versionSpecifier = "VERSION";

// Misc vCard constants
internal const string _spaceBreak = " ";
internal const string _tabBreak = "\x0009";

// Available in vCard 2.1, 3.0, 4.0, and 5.0
internal const char _fieldDelimiter = ';';
internal const char _valueDelimiter = ',';
Expand Down
55 changes: 36 additions & 19 deletions VisualCard/Parsers/VcardParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Textify.General;
using VisualCard.Exceptions;
using VisualCard.Parts;
using VisualCard.Parts.Enums;
Expand All @@ -39,44 +40,60 @@ namespace VisualCard.Parsers
internal class VcardParser
{
private readonly Version cardVersion = new();
private readonly string cardContent = "";
private readonly string[] cardContent = [];

/// <summary>
/// VCard card content
/// </summary>
public virtual string CardContent =>
public string[] CardContent =>
cardContent;
/// <summary>
/// VCard card version
/// </summary>
public virtual Version CardVersion =>
public Version CardVersion =>
cardVersion;

/// <summary>
/// Parses a VCard contact
/// </summary>
/// <returns>A strongly-typed <see cref="Card"/> instance holding information about the card</returns>
public virtual Card Parse()
public Card Parse()
{
// Check the content to ensure that we really have data
if (string.IsNullOrEmpty(CardContent))
if (CardContent.Length == 0)
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(CardVersion);

// Iterate through all the lines
int lineNumber = 0;
while (!CardContentReader.EndOfStream)
bool constructing = false;
StringBuilder valueBuilder = new();
for (int i = 0; i < CardContent.Length; i++)
{
// Get line
string _value = CardContentReader.ReadLine();
lineNumber += 1;
string _value = CardContent[i];
int lineNumber = i + 1;
if (string.IsNullOrEmpty(_value))
continue;

// First, check to see if we need to construct blocks
string secondLine = i + 1 < CardContent.Length ? CardContent[i + 1] : "";
bool firstConstructedLine = !_value.StartsWith(VcardConstants._spaceBreak) && !_value.StartsWith(VcardConstants._tabBreak);
constructing = secondLine.StartsWithAnyOf([VcardConstants._spaceBreak, VcardConstants._tabBreak]);
secondLine = secondLine.Length > 1 ? secondLine.Substring(1) : "";
if (constructing)
{
if (firstConstructedLine)
valueBuilder.Append(_value);
valueBuilder.Append(secondLine);
continue;
}
else if (!firstConstructedLine)
{
_value = valueBuilder.ToString();
valueBuilder.Clear();
}

// Variables
string value = _value.Substring(_value.IndexOf(VcardConstants._argumentDelimiter) + 1);
Expand Down Expand Up @@ -186,8 +203,8 @@ public virtual Card Parse()
// Now, get the part info
var partInfo =
isWithType ?
fromStringMethod.Invoke(null, [_value, finalArgs.ToArray(), altId, CardVersion, CardContentReader]) :
fromStringMethod.Invoke(null, [_value, altId, CardVersion, CardContentReader]);
fromStringMethod.Invoke(null, [_value, finalArgs.ToArray(), altId, CardVersion]) :
fromStringMethod.Invoke(null, [_value, altId, CardVersion]);
card.SetPart(partsType, (BaseCardPartInfo)partInfo);
}
break;
Expand All @@ -205,8 +222,8 @@ public virtual Card Parse()
// Now, get the part info
var partInfo =
isWithType ?
fromStringMethod.Invoke(null, [_value, finalArgs.ToArray(), altId, CardVersion, CardContentReader]) :
fromStringMethod.Invoke(null, [_value, altId, CardVersion, CardContentReader]);
fromStringMethod.Invoke(null, [_value, finalArgs.ToArray(), altId, CardVersion]) :
fromStringMethod.Invoke(null, [_value, altId, CardVersion]);
card.AddPartToArray(partsArrayType, (BaseCardPartInfo)partInfo);
}
break;
Expand Down Expand Up @@ -266,7 +283,7 @@ internal void ValidateCard(Card card)
throw new InvalidDataException($"The following keys [{string.Join(", ", expectedFields)}] are required. Got [{string.Join(", ", actualFields)}].");
}

internal VcardParser(string cardContent, Version cardVersion)
internal VcardParser(string[] cardContent, Version cardVersion)
{
this.cardContent = cardContent;
this.cardVersion = cardVersion;
Expand Down
4 changes: 2 additions & 2 deletions VisualCard/Parts/BaseCardPartInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ public override int GetHashCode()
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 FromStringVcardInternal(string value, int altId, Version cardVersion);

internal abstract BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader);
internal abstract BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion);

internal abstract string ToStringVcardInternal(Version cardVersion);
}
Expand Down
12 changes: 6 additions & 6 deletions VisualCard/Parts/Implementations/AddressInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ public class AddressInfo : BaseCardPartInfo, IEquatable<AddressInfo>
/// </summary>
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 FromStringVcardStatic(string value, int altId, Version cardVersion) =>
new AddressInfo().FromStringVcardInternal(value, altId, cardVersion);

internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) =>
new AddressInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader);
internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion) =>
new AddressInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion);

internal override string ToStringVcardInternal(Version cardVersion)
{
Expand Down Expand Up @@ -105,7 +105,7 @@ internal override string ToStringVcardInternal(Version cardVersion)
}
}

internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader)
internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion)
{
// Get the value
string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1);
Expand All @@ -120,7 +120,7 @@ internal override BaseCardPartInfo FromStringVcardInternal(string value, int alt
return InstallInfo([], splitAddressValues, altId, cardVersion);
}

internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader)
internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion)
{
// Get the value
string adrValue = value.Substring(VcardConstants._addressSpecifier.Length + 1);
Expand Down
12 changes: 6 additions & 6 deletions VisualCard/Parts/Implementations/AgentInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public class AgentInfo : BaseCardPartInfo, IEquatable<AgentInfo>
/// </summary>
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 FromStringVcardStatic(string value, int altId, Version cardVersion) =>
new AgentInfo().FromStringVcardInternal(value, altId, cardVersion);

internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) =>
new AgentInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader);
internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion) =>
new AgentInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion);

internal override string ToStringVcardInternal(Version cardVersion)
{
Expand Down Expand Up @@ -74,7 +74,7 @@ internal override string ToStringVcardInternal(Version cardVersion)
return agents.ToString();
}

internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader)
internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion)
{
// Get the value
string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1);
Expand All @@ -89,7 +89,7 @@ internal override BaseCardPartInfo FromStringVcardInternal(string value, int alt
return InstallInfo(agentValue, altId, cardVersion);
}

internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader)
internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion)
{
// Get the value
string agentValue = value.Substring(VcardConstants._agentSpecifier.Length + 1);
Expand Down
14 changes: 7 additions & 7 deletions VisualCard/Parts/Implementations/AnniversaryInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ public class AnniversaryInfo : BaseCardPartInfo, IEquatable<AnniversaryInfo>
/// </summary>
public DateTime? Anniversary { get; }

internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion, StreamReader cardContentReader) =>
new AnniversaryInfo().FromStringVcardInternal(value, altId, cardVersion, cardContentReader);
internal static BaseCardPartInfo FromStringVcardStatic(string value, int altId, Version cardVersion) =>
new AnniversaryInfo().FromStringVcardInternal(value, altId, cardVersion);

internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) =>
new AnniversaryInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion, cardContentReader);
internal static BaseCardPartInfo FromStringVcardWithTypeStatic(string value, string[] finalArgs, int altId, Version cardVersion) =>
new AnniversaryInfo().FromStringVcardWithTypeInternal(value, finalArgs, altId, cardVersion);

internal override string ToStringVcardInternal(Version cardVersion) =>
$"{VcardConstants._anniversarySpecifier}:{Anniversary:yyyyMMdd}";

internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion, StreamReader cardContentReader)
internal override BaseCardPartInfo FromStringVcardInternal(string value, int altId, Version cardVersion)
{
// Get the value
string anniversaryValue = value.Substring(VcardConstants._anniversarySpecifier.Length + 1);
Expand All @@ -55,8 +55,8 @@ internal override BaseCardPartInfo FromStringVcardInternal(string value, int alt
return InstallInfo(anniversaryValue);
}

internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion, StreamReader cardContentReader) =>
FromStringVcardInternal(value, altId, cardVersion, cardContentReader);
internal override BaseCardPartInfo FromStringVcardWithTypeInternal(string value, string[] finalArgs, int altId, Version cardVersion) =>
FromStringVcardInternal(value, altId, cardVersion);

private AnniversaryInfo InstallInfo(string value)
{
Expand Down
Loading

0 comments on commit 16501a4

Please sign in to comment.