Made the UCD data access code available from an interface instead of directly relying on HttpClient.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnicodeInformation.Tests
|
||||
{
|
||||
internal sealed class FileUcdSource : IUcdSource
|
||||
{
|
||||
private readonly string baseDirectory;
|
||||
|
||||
public FileUcdSource(string baseDirectory)
|
||||
{
|
||||
this.baseDirectory = Path.GetFullPath(baseDirectory);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenDataFileAsync(string fileName)
|
||||
{
|
||||
return Task.FromResult<Stream>(File.OpenRead(Path.Combine(baseDirectory, fileName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,42 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace UnicodeInformation.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class UnicodeDataManagerTests
|
||||
{
|
||||
private const string UcdDirectoryName = "UCD";
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var directoryName = Path.GetFullPath(UcdDirectoryName);
|
||||
|
||||
if (!Directory.Exists(directoryName))
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
var fileName = Path.Combine(directoryName, "UCD.zip");
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
new WebClient().DownloadFile("http://www.unicode.org/Public/UCD/latest/ucd/UCD.zip", fileName);
|
||||
ZipFile.ExtractToDirectory(fileName, directoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task DownloadAndBuildDataAsync()
|
||||
{
|
||||
await UnicodeDataManager.DownloadAndBuildDataAsync();
|
||||
var source = new FileUcdSource(UcdDirectoryName);
|
||||
|
||||
await UnicodeDataManager.DownloadAndBuildDataAsync(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Net.Http.Extensions">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Extensions.dll</HintPath>
|
||||
@@ -58,6 +60,7 @@
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileUcdSource.cs" />
|
||||
<Compile Include="UnicodeDataManagerTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnicodeInformation
|
||||
{
|
||||
public class HttpUcdSource : IUcdSource
|
||||
{
|
||||
public static readonly Uri UnicodeCharacterDataUri = new Uri("http://www.unicode.org/Public/UCD/latest/ucd/", UriKind.Absolute);
|
||||
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly Uri baseUri;
|
||||
|
||||
public HttpUcdSource()
|
||||
: this(UnicodeCharacterDataUri)
|
||||
{
|
||||
}
|
||||
|
||||
public HttpUcdSource(Uri baseUri)
|
||||
{
|
||||
this.httpClient = new HttpClient();
|
||||
this.baseUri = baseUri;
|
||||
}
|
||||
|
||||
public Task<Stream> OpenDataFileAsync(string fileName)
|
||||
{
|
||||
return httpClient.GetStreamAsync(baseUri + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnicodeInformation
|
||||
{
|
||||
public interface IUcdSource
|
||||
{
|
||||
Task<Stream> OpenDataFileAsync(string fileName);
|
||||
}
|
||||
}
|
||||
@@ -10,92 +10,88 @@ namespace UnicodeInformation
|
||||
{
|
||||
public class UnicodeDataManager
|
||||
{
|
||||
public static readonly Uri UnicodeCharacterDataUri = new Uri("http://www.unicode.org/Public/UCD/latest/ucd/", UriKind.Absolute);
|
||||
public const string UnicodeDataFileName = "UnicodeData.txt";
|
||||
public const string PropListFileName = "PropList.txt";
|
||||
|
||||
public static async Task<UnicodeData> DownloadAndBuildDataAsync()
|
||||
public static async Task<UnicodeData> DownloadAndBuildDataAsync(IUcdSource ucdSource)
|
||||
{
|
||||
var characterDataBuilders = new List<UnicodeCharacterDataBuilder>();
|
||||
|
||||
using (var httpClient = new HttpClient())
|
||||
using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(UnicodeDataFileName).ConfigureAwait(false)))
|
||||
{
|
||||
using (var reader = new UnicodeDataFileReader(await httpClient.GetStreamAsync(UnicodeCharacterDataUri + UnicodeDataFileName).ConfigureAwait(false)))
|
||||
while (reader.MoveToNextLine())
|
||||
{
|
||||
while (reader.MoveToNextLine())
|
||||
// NB: Fields 10 and 11 are deemed obsolete. Field 11 should always be empty, and will be ignored here.
|
||||
var characterData = new UnicodeCharacterDataBuilder(int.Parse(reader.ReadField(), NumberStyles.HexNumber))
|
||||
{
|
||||
// NB: Fields 10 and 11 are deemed obsolete. Field 11 should always be empty, and will be ignored here.
|
||||
var characterData = new UnicodeCharacterDataBuilder(int.Parse(reader.ReadField(), NumberStyles.HexNumber))
|
||||
Name = reader.ReadField(),
|
||||
Category = UnicodeCategoryInfo.FromShortName(reader.ReadField()).Category,
|
||||
CanonicalCombiningClass = (CanonicalCombiningClass)byte.Parse(reader.ReadField()),
|
||||
BidirectionalClass = reader.ReadField(),
|
||||
DecompositionType = reader.ReadField()
|
||||
};
|
||||
|
||||
string numericDecimalField = reader.ReadField();
|
||||
string numericDigitField = reader.ReadField();
|
||||
string numericNumericField = reader.ReadField();
|
||||
|
||||
characterData.BidirectionalMirrored = reader.ReadField() == "Y";
|
||||
characterData.OldName = reader.ReadField();
|
||||
reader.SkipField();
|
||||
characterData.SimpleUpperCaseMapping = reader.ReadField();
|
||||
characterData.SimpleLowerCaseMapping = reader.ReadField();
|
||||
characterData.SimpleTitleCaseMapping = reader.ReadField();
|
||||
|
||||
// Handle Numeric_Type & Numeric_Value:
|
||||
// If field 6 is set, fields 7 and 8 should have the same value, and Numeric_Type is Decimal.
|
||||
// If field 6 is not set but field 7 is set, field 8 should be set and have the same value. Then, the type is Digit.
|
||||
// If field 6 and 7 are not set, but field 8 is set, then Numeric_Type is Numeric.
|
||||
if (!string.IsNullOrEmpty(numericNumericField))
|
||||
{
|
||||
characterData.NumericValue = UnicodeRationalNumber.Parse(numericNumericField);
|
||||
|
||||
if (!string.IsNullOrEmpty(numericDigitField))
|
||||
{
|
||||
Name = reader.ReadField(),
|
||||
Category = UnicodeCategoryInfo.FromShortName(reader.ReadField()).Category,
|
||||
CanonicalCombiningClass = (CanonicalCombiningClass)byte.Parse(reader.ReadField()),
|
||||
BidirectionalClass = reader.ReadField(),
|
||||
DecompositionType = reader.ReadField()
|
||||
};
|
||||
|
||||
string numericDecimalField = reader.ReadField();
|
||||
string numericDigitField = reader.ReadField();
|
||||
string numericNumericField = reader.ReadField();
|
||||
|
||||
characterData.BidirectionalMirrored = reader.ReadField() == "Y";
|
||||
characterData.OldName = reader.ReadField();
|
||||
reader.SkipField();
|
||||
characterData.SimpleUpperCaseMapping = reader.ReadField();
|
||||
characterData.SimpleLowerCaseMapping = reader.ReadField();
|
||||
characterData.SimpleTitleCaseMapping = reader.ReadField();
|
||||
|
||||
// Handle Numeric_Type & Numeric_Value:
|
||||
// If field 6 is set, fields 7 and 8 should have the same value, and Numeric_Type is Decimal.
|
||||
// If field 6 is not set but field 7 is set, field 8 should be set and have the same value. Then, the type is Digit.
|
||||
// If field 6 and 7 are not set, but field 8 is set, then Numeric_Type is Numeric.
|
||||
if (!string.IsNullOrEmpty(numericNumericField))
|
||||
{
|
||||
characterData.NumericValue = UnicodeRationalNumber.Parse(numericNumericField);
|
||||
|
||||
if (!string.IsNullOrEmpty(numericDigitField))
|
||||
if (numericDigitField != numericNumericField)
|
||||
{
|
||||
if (numericDigitField != numericNumericField)
|
||||
{
|
||||
throw new InvalidDataException("Invalid value for field 7 of character U+" + characterData.CodePoint.ToString("X4") + ".");
|
||||
}
|
||||
throw new InvalidDataException("Invalid value for field 7 of character U+" + characterData.CodePoint.ToString("X4") + ".");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(numericDecimalField))
|
||||
if (!string.IsNullOrEmpty(numericDecimalField))
|
||||
{
|
||||
if (numericDecimalField != numericDigitField)
|
||||
{
|
||||
if (numericDecimalField != numericDigitField)
|
||||
{
|
||||
throw new InvalidDataException("Invalid value for field 6 of character U+" + characterData.CodePoint.ToString("X4") + ".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
characterData.NumericType = UnicodeNumericType.Digit;
|
||||
throw new InvalidDataException("Invalid value for field 6 of character U+" + characterData.CodePoint.ToString("X4") + ".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
characterData.NumericType = UnicodeNumericType.Numeric;
|
||||
characterData.NumericType = UnicodeNumericType.Digit;
|
||||
}
|
||||
}
|
||||
|
||||
characterDataBuilders.Add(characterData);
|
||||
}
|
||||
}
|
||||
/*
|
||||
using (var reader = new UnicodeDataFileReader(await httpClient.GetStreamAsync(UnicodeCharacterDataUri + PropListFileName).ConfigureAwait(false)))
|
||||
{
|
||||
while (reader.MoveToNextLine())
|
||||
{
|
||||
ContributoryProperties property;
|
||||
|
||||
var range = UnicodeCharacterRange.Parse(reader.ReadField().TrimEnd());
|
||||
if (EnumHelper<ContributoryProperties>.TryGetNamedValue(reader.ReadField().Trim(), out property))
|
||||
else
|
||||
{
|
||||
characterData.NumericType = UnicodeNumericType.Numeric;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
characterDataBuilders.Add(characterData);
|
||||
}
|
||||
}
|
||||
/*
|
||||
using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(PropListFileName).ConfigureAwait(false)))
|
||||
{
|
||||
while (reader.MoveToNextLine())
|
||||
{
|
||||
ContributoryProperties property;
|
||||
|
||||
var range = UnicodeCharacterRange.Parse(reader.ReadField().TrimEnd());
|
||||
if (EnumHelper<ContributoryProperties>.TryGetNamedValue(reader.ReadField().Trim(), out property))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
var finalData = new UnicodeCharacterData[characterDataBuilders.Count];
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
<Compile Include="CodePointEnumerable.cs" />
|
||||
<Compile Include="CodePointEnumerator.cs" />
|
||||
<Compile Include="EnumHelper.cs" />
|
||||
<Compile Include="HttpUcdSource.cs" />
|
||||
<Compile Include="IUcdSource.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="UnicodeCategoryExtensions.cs" />
|
||||
|
||||
Reference in New Issue
Block a user