11 changed files with 304 additions and 110 deletions
@ -0,0 +1,205 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using static System.Char; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
internal struct StringTokenizer : IDisposable |
|||
{ |
|||
private const char DefaultSeparatorChar = ','; |
|||
|
|||
private readonly string _s; |
|||
private readonly int _length; |
|||
private readonly char _separator; |
|||
private readonly string _exceptionMessage; |
|||
private readonly IFormatProvider _formatProvider; |
|||
private int _index; |
|||
private int _tokenIndex; |
|||
private int _tokenLength; |
|||
|
|||
public StringTokenizer(string s, IFormatProvider formatProvider, string exceptionMessage = null) |
|||
: this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage) |
|||
{ |
|||
_formatProvider = formatProvider; |
|||
} |
|||
|
|||
public StringTokenizer(string s, char separator = DefaultSeparatorChar, string exceptionMessage = null) |
|||
{ |
|||
_s = s ?? throw new ArgumentNullException(nameof(s)); |
|||
_length = s?.Length ?? 0; |
|||
_separator = separator; |
|||
_exceptionMessage = exceptionMessage; |
|||
_formatProvider = CultureInfo.InvariantCulture; |
|||
_index = 0; |
|||
_tokenIndex = -1; |
|||
_tokenLength = 0; |
|||
|
|||
while (_index < _length && IsWhiteSpace(_s, _index)) |
|||
{ |
|||
_index++; |
|||
} |
|||
} |
|||
|
|||
public string CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (_index != _length) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
} |
|||
|
|||
public bool NextInt32(out Int32 result, char? separator = null) |
|||
{ |
|||
var success = NextString(out var stringResult, separator); |
|||
result = success ? int.Parse(stringResult, _formatProvider) : 0; |
|||
return success; |
|||
} |
|||
|
|||
public int NextInt32Required(char? separator = null) |
|||
{ |
|||
if (!NextInt32(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public bool NextDouble(out double result, char? separator = null) |
|||
{ |
|||
var success = NextString(out var stringResult, separator); |
|||
result = success ? double.Parse(stringResult, _formatProvider) : 0; |
|||
return success; |
|||
} |
|||
|
|||
public double NextDoubleRequired(char? separator = null) |
|||
{ |
|||
if (!NextDouble(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public bool NextString(out string result, char? separator = null) |
|||
{ |
|||
var success = NextToken(separator ?? _separator); |
|||
result = CurrentToken; |
|||
return success; |
|||
} |
|||
|
|||
public string NextStringRequired(char? separator = null) |
|||
{ |
|||
if (!NextString(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private bool NextToken(char separator) |
|||
{ |
|||
_tokenIndex = -1; |
|||
|
|||
if (_index >= _length) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var c = _s[_index]; |
|||
|
|||
var index = _index; |
|||
var length = 0; |
|||
|
|||
while (_index < _length) |
|||
{ |
|||
c = _s[_index]; |
|||
|
|||
if (IsWhiteSpace(c) || c == separator) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
_index++; |
|||
length++; |
|||
} |
|||
|
|||
SkipToNextToken(separator); |
|||
|
|||
_tokenIndex = index; |
|||
_tokenLength = length; |
|||
|
|||
if (_tokenLength < 1) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private void SkipToNextToken(char separator) |
|||
{ |
|||
if (_index < _length) |
|||
{ |
|||
var c = _s[_index]; |
|||
|
|||
if (c != separator && !IsWhiteSpace(c)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
var length = 0; |
|||
|
|||
while (_index < _length) |
|||
{ |
|||
c = _s[_index]; |
|||
|
|||
if (c == separator) |
|||
{ |
|||
length++; |
|||
_index++; |
|||
|
|||
if (length > 1) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (!IsWhiteSpace(c)) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
_index++; |
|||
} |
|||
} |
|||
|
|||
if (length > 0 && _index >= _length) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private FormatException GetFormatException() => |
|||
_exceptionMessage != null ? new FormatException(_exceptionMessage) : new FormatException(); |
|||
|
|||
private static char GetSeparatorFromFormatProvider(IFormatProvider provider) |
|||
{ |
|||
var c = DefaultSeparatorChar; |
|||
|
|||
var formatInfo = NumberFormatInfo.GetInstance(provider); |
|||
if (formatInfo.NumberDecimalSeparator.Length > 0 && c == formatInfo.NumberDecimalSeparator[0]) |
|||
{ |
|||
c = ';'; |
|||
} |
|||
|
|||
return c; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue