-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented support of delimiter options #2
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright (c) Arjen Post and contributors. See LICENSE in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace PartialResponse.Core | ||
{ | ||
/// <summary> | ||
/// Represents delimiter options for parser | ||
/// </summary> | ||
public class DelimiterOptions | ||
{ | ||
private static DelimiterOptions defaultOptions; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="DelimiterOptions"/> class. | ||
/// </summary> | ||
/// <param name="fieldsDelimiters">Characters representing field delimiters.</param> | ||
/// <param name="nestedFieldDelimiters">Characters representing nested field delimiters.</param> | ||
/// <param name="fieldGroupStartDelimiters">Characters representing field group start delimiters.</param> | ||
/// <param name="fieldGroupEndDelimiters">Characters representing field group end delimiters.</param> | ||
public DelimiterOptions(char[] fieldsDelimiters, char[] nestedFieldDelimiters, char[] fieldGroupStartDelimiters, char[] fieldGroupEndDelimiters) | ||
{ | ||
var map = new Dictionary<char, TokenType>(); | ||
|
||
foreach (var c in fieldsDelimiters ?? throw new ArgumentNullException(nameof(fieldsDelimiters))) | ||
{ | ||
map.Add(c, TokenType.FieldsDelimiter); | ||
} | ||
|
||
foreach (var c in nestedFieldDelimiters ?? throw new ArgumentNullException(nameof(nestedFieldDelimiters))) | ||
{ | ||
map.Add(c, TokenType.NestedFieldDelimiter); | ||
} | ||
|
||
foreach (var c in fieldGroupStartDelimiters ?? throw new ArgumentNullException(nameof(fieldGroupStartDelimiters))) | ||
{ | ||
map.Add(c, TokenType.FieldGroupStartDelimiter); | ||
} | ||
|
||
foreach (var c in fieldGroupEndDelimiters ?? throw new ArgumentNullException(nameof(fieldGroupEndDelimiters))) | ||
{ | ||
map.Add(c, TokenType.FieldGroupEndDelimiter); | ||
} | ||
|
||
this.DelimiterToTokenTypeMap = map; | ||
|
||
this.FieldsDelimiters = fieldsDelimiters; | ||
this.NestedFieldDelimiters = nestedFieldDelimiters; | ||
this.FieldGroupStartDelimiters = fieldGroupStartDelimiters; | ||
this.FieldGroupEndDelimiters = fieldGroupEndDelimiters; | ||
} | ||
|
||
/// <summary> | ||
/// Gets default options for parser. Default options are: | ||
/// field delimiters - ',' | ||
/// nested field delimiters - '/' | ||
/// field group start delimiters - '(' | ||
/// field group end delimiters - ')' | ||
/// </summary> | ||
public static DelimiterOptions DefaultOptions | ||
{ | ||
get | ||
{ | ||
return defaultOptions | ||
?? (defaultOptions = new DelimiterOptions(new[] { ',' }, new[] { '/' }, new[] { '(' }, new[] { ')' })); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets delimiter to token type map. | ||
/// </summary> | ||
public IReadOnlyDictionary<char, TokenType> DelimiterToTokenTypeMap { get; } | ||
|
||
/// <summary> | ||
/// Gets fields delimiters. | ||
/// </summary> | ||
public char[] FieldsDelimiters { get; } | ||
|
||
/// <summary> | ||
/// Gets nested field delimiters. | ||
/// </summary> | ||
public char[] NestedFieldDelimiters { get; } | ||
|
||
/// <summary> | ||
/// Gets nested field group start delimiters. | ||
/// </summary> | ||
public char[] FieldGroupStartDelimiters { get; } | ||
|
||
/// <summary> | ||
/// Gets nested field group end delimiters. | ||
/// </summary> | ||
public char[] FieldGroupEndDelimiters { get; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
// Copyright (c) Arjen Post. See LICENSE in the project root for license information. | ||
// Copyright (c) Arjen Post and contributors. See LICENSE in the project root for license information. | ||
|
||
using System; | ||
using System.Linq; | ||
|
||
namespace PartialResponse.Core | ||
{ | ||
|
@@ -16,15 +17,20 @@ public struct Field | |
/// <summary> | ||
/// Initializes a new instance of the <see cref="Field"/> structure. | ||
/// </summary> | ||
/// <param name="value">The value of the field.</param> | ||
public Field(string value) | ||
/// <param name="parts">The value of the field.</param> | ||
public Field(params string[] parts) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was the constructor changed? As a result Parser new uses a Stack<List> but Field expects an array, so ToArray is called. Less string manipulation, but is this more efficient compared to the string There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason was to move responsibility of splitting field parts to parser to not to make a dependency on the delimiter options. But looking at it now it made me realize that it will probably have to have it and Wildcard should be also part of the delimiter options. |
||
{ | ||
if (value == null) | ||
if (parts == null) | ||
{ | ||
throw new ArgumentNullException(nameof(parts)); | ||
} | ||
|
||
if (parts.Length == 0 || parts.Any(string.IsNullOrEmpty)) | ||
{ | ||
throw new ArgumentNullException(nameof(value)); | ||
throw new ArgumentException("Parts cannot be empty or null", nameof(parts)); | ||
} | ||
|
||
this.Parts = value.Split('/'); | ||
this.Parts = parts; | ||
} | ||
|
||
/// <summary> | ||
|
@@ -77,14 +83,5 @@ public bool Matches(string[] parts, bool ignoreCase) | |
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Returns a string that represents the current object. | ||
/// </summary> | ||
/// <returns>A string that represents the current object.</returns> | ||
public override string ToString() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the ToString method removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, unwillingness to make a dependency on delimiter options. But according to comment above I'll change that. This leads to the question, if there are more than one field part delimiter which one should be used, first one? Or better to keep a reference to original field string? What do you think? |
||
{ | ||
return string.Join("/", this.Parts); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to use dictionaries and not a single char per type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually makes a lot of sense but then there will be too many changes for parser. I've tried that way and found that it becomes very complicated to make a hand-crafted parser. So probably better go baby steps and implement version allowing one to use single character delimiters (most of the delimiters are indeed are a single character). And then work on enhancing it further.