Made the UCD data access code available from an interface instead of directly relying on HttpClient.

This commit is contained in:
GoldenCrystal
2014-11-01 14:43:50 +01:00
parent f3e2ff53c5
commit f13b0d2523
7 changed files with 163 additions and 64 deletions
+24
View File
@@ -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>
+34
View File
@@ -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);
}
}
}
+14
View File
@@ -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);
}
}
+59 -63
View File
@@ -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" />