Restructured the projects, and put all the data construction logic in a separate assembly.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Unicode;
|
||||
|
||||
namespace System.Unicode.Builder
|
||||
{
|
||||
public 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 System.Unicode.Builder
|
||||
{
|
||||
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 System.Unicode.Builder
|
||||
{
|
||||
public interface IUcdSource
|
||||
{
|
||||
Task<Stream> OpenDataFileAsync(string fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Unicode.Builder
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("UnicodeInformation.Builder")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("UnicodeInformation.Builder")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("8dfdee6c-4f0d-4de1-b346-574cb56d2b8b")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Unicode.Builder
|
||||
{
|
||||
public sealed class UnicodeCharacterDataBuilder
|
||||
{
|
||||
private readonly UnicodeCharacterRange codePointRange;
|
||||
private string name;
|
||||
private UnicodeCategory category = UnicodeCategory.OtherNotAssigned;
|
||||
private CanonicalCombiningClass canonicalCombiningClass;
|
||||
private BidirectionalClass bidirectionalClass;
|
||||
private string decompositionType;
|
||||
private UnicodeNumericType numericType;
|
||||
private UnicodeRationalNumber numericValue;
|
||||
private bool bidirectionalMirrored;
|
||||
private string oldName;
|
||||
private string simpleUpperCaseMapping;
|
||||
private string simpleLowerCaseMapping;
|
||||
private string simpleTitleCaseMapping;
|
||||
private ContributoryProperties contributoryProperties;
|
||||
|
||||
private List<int> relatedCodePoints = new List<int>();
|
||||
|
||||
public UnicodeCharacterRange CodePointRange { get { return codePointRange; } }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
|
||||
public UnicodeCategory Category
|
||||
{
|
||||
get { return category; }
|
||||
set
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(UnicodeCategory), value))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
category = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CanonicalCombiningClass CanonicalCombiningClass
|
||||
{
|
||||
get { return canonicalCombiningClass; }
|
||||
set { canonicalCombiningClass = value; } // Even values not defined in the enum are allowed here.
|
||||
}
|
||||
|
||||
public BidirectionalClass BidirectionalClass
|
||||
{
|
||||
get { return bidirectionalClass; }
|
||||
set { bidirectionalClass = value; }
|
||||
}
|
||||
|
||||
public string DecompositionType
|
||||
{
|
||||
get { return decompositionType; }
|
||||
set { decompositionType = value; }
|
||||
}
|
||||
|
||||
public UnicodeNumericType NumericType
|
||||
{
|
||||
get { return numericType; }
|
||||
set { numericType = value; }
|
||||
}
|
||||
|
||||
public UnicodeRationalNumber NumericValue
|
||||
{
|
||||
get { return numericValue; }
|
||||
set { numericValue = value; }
|
||||
}
|
||||
|
||||
public string OldName
|
||||
{
|
||||
get { return oldName; }
|
||||
set { oldName = value; }
|
||||
}
|
||||
|
||||
public bool BidirectionalMirrored
|
||||
{
|
||||
get { return bidirectionalMirrored; }
|
||||
set { bidirectionalMirrored = value; }
|
||||
}
|
||||
|
||||
public string SimpleUpperCaseMapping
|
||||
{
|
||||
get { return simpleUpperCaseMapping; }
|
||||
set { simpleUpperCaseMapping = value; }
|
||||
}
|
||||
|
||||
public string SimpleLowerCaseMapping
|
||||
{
|
||||
get { return simpleLowerCaseMapping; }
|
||||
set { simpleLowerCaseMapping = value; }
|
||||
}
|
||||
|
||||
public string SimpleTitleCaseMapping
|
||||
{
|
||||
get { return simpleTitleCaseMapping; }
|
||||
set { simpleTitleCaseMapping = value; }
|
||||
}
|
||||
|
||||
public ContributoryProperties ContributoryProperties
|
||||
{
|
||||
get { return contributoryProperties; }
|
||||
set { contributoryProperties = value; }
|
||||
}
|
||||
|
||||
public ICollection<int> RelatedCodePoints { get { return relatedCodePoints; } }
|
||||
|
||||
public UnicodeCharacterDataBuilder(int codePoint)
|
||||
: this(new UnicodeCharacterRange(codePoint))
|
||||
{
|
||||
}
|
||||
|
||||
public UnicodeCharacterDataBuilder(UnicodeCharacterRange codePointRange)
|
||||
{
|
||||
this.codePointRange = codePointRange;
|
||||
this.category = UnicodeCategory.OtherNotAssigned;
|
||||
}
|
||||
|
||||
public UnicodeCharacterData ToCharacterData()
|
||||
{
|
||||
return new UnicodeCharacterData
|
||||
(
|
||||
codePointRange,
|
||||
Name,
|
||||
category,
|
||||
canonicalCombiningClass,
|
||||
bidirectionalClass,
|
||||
decompositionType,
|
||||
numericType,
|
||||
numericValue,
|
||||
bidirectionalMirrored,
|
||||
oldName,
|
||||
simpleUpperCaseMapping,
|
||||
simpleLowerCaseMapping,
|
||||
simpleTitleCaseMapping,
|
||||
contributoryProperties,
|
||||
relatedCodePoints.Count > 0 ? relatedCodePoints.ToArray() : null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Unicode.Builder
|
||||
{
|
||||
public class UnicodeDataBuilder
|
||||
{
|
||||
private readonly Version unicodeVersion;
|
||||
private UnicodeCharacterDataBuilder[] characterDataBuilders = new UnicodeCharacterDataBuilder[10000];
|
||||
private int characterCount;
|
||||
|
||||
public UnicodeDataBuilder(Version unicodeVersion)
|
||||
{
|
||||
this.unicodeVersion = unicodeVersion;
|
||||
}
|
||||
|
||||
private int FindCodePoint(int codePoint)
|
||||
{
|
||||
if (characterCount == 0) return -1;
|
||||
|
||||
int minIndex = 0;
|
||||
int maxIndex = characterCount - 1;
|
||||
|
||||
do
|
||||
{
|
||||
int index = (minIndex + maxIndex) >> 1;
|
||||
|
||||
int Δ = characterDataBuilders[index].CodePointRange.CompareCodePoint(codePoint);
|
||||
|
||||
if (Δ == 0) return index;
|
||||
else if (Δ < 0) maxIndex = index - 1;
|
||||
else minIndex = index + 1;
|
||||
} while (minIndex <= maxIndex);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int FindInsertionPoint(int startCodePoint, int endCodePoint)
|
||||
{
|
||||
int minIndex;
|
||||
int maxIndex;
|
||||
|
||||
if (characterCount == 0 || characterDataBuilders[maxIndex = characterCount - 1].CodePointRange.LastCodePoint < startCodePoint) return characterCount;
|
||||
else if (endCodePoint < characterDataBuilders[minIndex = 0].CodePointRange.FirstCodePoint) return 0;
|
||||
else if (characterCount == 1) return -1;
|
||||
|
||||
do
|
||||
{
|
||||
int index = (minIndex + maxIndex) >> 1;
|
||||
|
||||
int Δ = characterDataBuilders[index].CodePointRange.CompareCodePoint(startCodePoint);
|
||||
|
||||
if (Δ == 0) return -1;
|
||||
else if (Δ < 0) maxIndex = index;
|
||||
else minIndex = index;
|
||||
} while (maxIndex - minIndex > 1);
|
||||
|
||||
if (characterDataBuilders[maxIndex].CodePointRange.FirstCodePoint < endCodePoint) return -1;
|
||||
else return maxIndex;
|
||||
}
|
||||
|
||||
public void Insert(UnicodeCharacterDataBuilder data)
|
||||
{
|
||||
int insertionPoint = FindInsertionPoint(data.CodePointRange.FirstCodePoint, data.CodePointRange.LastCodePoint);
|
||||
|
||||
if (insertionPoint < 0) throw new InvalidOperationException("The specified range overlaps with pre-existing ranges.");
|
||||
|
||||
if (insertionPoint >= characterDataBuilders.Length)
|
||||
{
|
||||
Array.Resize(ref characterDataBuilders, characterDataBuilders.Length << 1);
|
||||
}
|
||||
|
||||
if (insertionPoint < characterCount)
|
||||
{
|
||||
Array.Copy(characterDataBuilders, insertionPoint, characterDataBuilders, insertionPoint + 1, characterCount - insertionPoint);
|
||||
}
|
||||
|
||||
characterDataBuilders[insertionPoint] = data;
|
||||
++characterCount;
|
||||
}
|
||||
|
||||
public UnicodeCharacterDataBuilder Get(int codePoint)
|
||||
{
|
||||
int index = FindCodePoint(codePoint);
|
||||
|
||||
return index >= 0 ? characterDataBuilders[index] : null;
|
||||
}
|
||||
|
||||
public void SetProperties(ContributoryProperties property, UnicodeCharacterRange codePointRange)
|
||||
{
|
||||
int firstIndex = FindCodePoint(codePointRange.FirstCodePoint);
|
||||
int lastIndex = FindCodePoint(codePointRange.LastCodePoint);
|
||||
|
||||
if (firstIndex < 0 && lastIndex < 0)
|
||||
{
|
||||
Insert(new UnicodeCharacterDataBuilder(codePointRange) { ContributoryProperties = property });
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstIndex < 0
|
||||
|| lastIndex < 0
|
||||
|| characterDataBuilders[firstIndex].CodePointRange.FirstCodePoint < codePointRange.FirstCodePoint
|
||||
|| characterDataBuilders[lastIndex].CodePointRange.LastCodePoint > codePointRange.LastCodePoint)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
int i = firstIndex;
|
||||
|
||||
while (true)
|
||||
{
|
||||
characterDataBuilders[i].ContributoryProperties |= property;
|
||||
|
||||
if (i == lastIndex) break;
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
public UnicodeData ToUnicodeData()
|
||||
{
|
||||
var finalData = new UnicodeCharacterData[characterCount];
|
||||
|
||||
for (int i = 0; i < finalData.Length; ++i)
|
||||
finalData[i] = characterDataBuilders[i].ToCharacterData();
|
||||
|
||||
return new UnicodeData(unicodeVersion, finalData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Unicode.Builder
|
||||
{
|
||||
public class UnicodeDataFileReader : IDisposable
|
||||
{
|
||||
private struct AsciiCharBuffer
|
||||
{
|
||||
private readonly UnicodeDataFileReader reader;
|
||||
private int length;
|
||||
|
||||
public AsciiCharBuffer(UnicodeDataFileReader reader)
|
||||
{
|
||||
this.reader = reader;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
public int Length { get { return length; } }
|
||||
|
||||
private void EnsureExtraCapacity(int count)
|
||||
{
|
||||
if (count < 0) throw new ArgumentOutOfRangeException("requiredExtraCapacity");
|
||||
if (reader.charBuffer.Length < checked(length + count))
|
||||
Array.Resize(ref reader.charBuffer, Math.Max(length + count, reader.charBuffer.Length << 1));
|
||||
}
|
||||
|
||||
public void Append(byte[] value, int startIndex, int count)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException("value");
|
||||
if (startIndex >= value.Length) throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (checked(count += startIndex) > value.Length) throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
EnsureExtraCapacity(value.Length);
|
||||
|
||||
var buffer = reader.charBuffer;
|
||||
|
||||
for (int i = startIndex; i < count; ++i)
|
||||
{
|
||||
byte b = value[i];
|
||||
|
||||
if (b > 127) throw new InvalidDataException(string.Format("Unexpected character value: {0}.", b));
|
||||
|
||||
buffer[length++] = (char)b;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return length > 0 ? new string(reader.charBuffer, 0, length) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Stream stream;
|
||||
private readonly byte[] byteBuffer;
|
||||
private char[] charBuffer;
|
||||
private int index;
|
||||
private int length;
|
||||
private bool hasField = false;
|
||||
private readonly bool leaveOpen;
|
||||
|
||||
public UnicodeDataFileReader(Stream stream)
|
||||
: this(stream, false)
|
||||
{
|
||||
}
|
||||
|
||||
public UnicodeDataFileReader(Stream stream, bool leaveOpen)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.byteBuffer = new byte[8192];
|
||||
this.charBuffer = new char[100];
|
||||
this.leaveOpen = leaveOpen;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!leaveOpen) stream.Dispose();
|
||||
}
|
||||
|
||||
private bool RefillBuffer()
|
||||
{
|
||||
// Evilish line of code. 😈
|
||||
return (length = stream.Read(byteBuffer, 0, byteBuffer.Length)) != (index = 0);
|
||||
}
|
||||
|
||||
private static bool IsNewLineOrComment(byte b)
|
||||
{
|
||||
return b == '\n' || b == '#';
|
||||
}
|
||||
|
||||
/// <summary>Moves the stream to the next valid data row.</summary>
|
||||
/// <returns><see langword="true"/> if data is available; <see langword="false"/> otherwise.</returns>
|
||||
public bool MoveToNextLine()
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
if (RefillBuffer())
|
||||
{
|
||||
if (!IsNewLineOrComment(byteBuffer[index]))
|
||||
{
|
||||
hasField = true;
|
||||
goto Completed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
while (index < length)
|
||||
{
|
||||
if (byteBuffer[index++] == '\n')
|
||||
{
|
||||
if (index < length && !IsNewLineOrComment(byteBuffer[index]))
|
||||
{
|
||||
hasField = true;
|
||||
goto Completed;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (RefillBuffer());
|
||||
|
||||
hasField = false;
|
||||
Completed: ;
|
||||
return hasField;
|
||||
}
|
||||
|
||||
/// <summary>Reads the next data field.</summary>
|
||||
/// <remarks>This method will return <see langword="null"/> on end of line.</remarks>
|
||||
/// <returns>The text value of the read field, if available; <see langword="null"/> otherwise.</returns>
|
||||
public string ReadField()
|
||||
{
|
||||
if (length == 0) throw new InvalidOperationException();
|
||||
|
||||
if (!hasField) return null;
|
||||
else if (index >= length) RefillBuffer();
|
||||
|
||||
// If the current character is a new line or a comment, we are at the end of a line.
|
||||
if (IsNewLineOrComment(byteBuffer[index]))
|
||||
{
|
||||
if (hasField)
|
||||
{
|
||||
hasField = false;
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var buffer = new AsciiCharBuffer(this);
|
||||
int startOffset;
|
||||
int endOffset;
|
||||
|
||||
do
|
||||
{
|
||||
startOffset = index;
|
||||
endOffset = -1;
|
||||
|
||||
while (index < length)
|
||||
{
|
||||
byte b = byteBuffer[index];
|
||||
|
||||
if (IsNewLineOrComment(b)) // NB: Do not advance to the next byte when end of line has been reached.
|
||||
{
|
||||
endOffset = index;
|
||||
hasField = false;
|
||||
break;
|
||||
}
|
||||
else if(b == ';')
|
||||
{
|
||||
endOffset = index++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
if (endOffset >= 0)
|
||||
{
|
||||
buffer.Append(byteBuffer, startOffset, endOffset - startOffset);
|
||||
break;
|
||||
}
|
||||
else if (index > startOffset)
|
||||
{
|
||||
buffer.Append(byteBuffer, startOffset, index - startOffset);
|
||||
}
|
||||
} while (RefillBuffer());
|
||||
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Skips the next data field.</summary>
|
||||
/// <remarks>This method will return <see langword="false"/> on end of line.</remarks>
|
||||
/// <returns><see langword="true"/> if a field was skipped; <see langword="false"/> otherwise.</returns>
|
||||
public bool SkipField()
|
||||
{
|
||||
if (length == 0) throw new InvalidOperationException();
|
||||
|
||||
if (!hasField) return false;
|
||||
else if (index >= length) RefillBuffer();
|
||||
|
||||
// If the current character is a new line or a comment, we are at the end of a line.
|
||||
if (IsNewLineOrComment(byteBuffer[index]))
|
||||
{
|
||||
hasField = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
while (index < length)
|
||||
{
|
||||
byte b = byteBuffer[index];
|
||||
|
||||
if (IsNewLineOrComment(b)) // NB: Do not advance to the next byte when end of line has been reached.
|
||||
{
|
||||
hasField = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++index;
|
||||
|
||||
if (b == ';')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (RefillBuffer());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Unicode.Builder
|
||||
{
|
||||
public class UnicodeDataManager
|
||||
{
|
||||
public const string UnicodeDataFileName = "UnicodeData.txt";
|
||||
public const string PropListFileName = "PropList.txt";
|
||||
|
||||
private static string ParseSimpleCaseMapping(string mapping)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mapping)) return null;
|
||||
|
||||
return char.ConvertFromUtf32(int.Parse(mapping, NumberStyles.HexNumber));
|
||||
}
|
||||
|
||||
private static string NullIfEmpty(string s)
|
||||
{
|
||||
return string.IsNullOrEmpty(s) ? null : s;
|
||||
}
|
||||
|
||||
public static async Task<UnicodeData> DownloadAndBuildDataAsync(IUcdSource ucdSource)
|
||||
{
|
||||
var builder = new UnicodeDataBuilder(new Version(7, 0));
|
||||
|
||||
using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(UnicodeDataFileName).ConfigureAwait(false)))
|
||||
{
|
||||
int rangeStartCodePoint = -1;
|
||||
|
||||
while (reader.MoveToNextLine())
|
||||
{
|
||||
var codePoint = new UnicodeCharacterRange(int.Parse(reader.ReadField(), NumberStyles.HexNumber));
|
||||
|
||||
string name = reader.ReadField();
|
||||
|
||||
if (!string.IsNullOrEmpty(name) && name[0] == '<' && name[name.Length - 1] == '>')
|
||||
{
|
||||
if (name.EndsWith(", First>", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (rangeStartCodePoint >= 0) throw new InvalidDataException("Invalid range data in UnicodeData.txt.");
|
||||
|
||||
rangeStartCodePoint = codePoint.FirstCodePoint;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (name.EndsWith(", Last>", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (rangeStartCodePoint < 0) throw new InvalidDataException("Invalid range data in UnicodeData.txt.");
|
||||
|
||||
codePoint = new UnicodeCharacterRange(rangeStartCodePoint, codePoint.LastCodePoint);
|
||||
|
||||
name = name.Substring(1, name.Length - 8);
|
||||
|
||||
rangeStartCodePoint = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = name.Substring(1, name.Length - 2);
|
||||
|
||||
if (codePoint.IsSingleCodePoint) name = name + "-" + codePoint.ToString();
|
||||
}
|
||||
}
|
||||
else if (rangeStartCodePoint >= 0)
|
||||
{
|
||||
throw new InvalidDataException("Invalid range data in UnicodeData.txt.");
|
||||
}
|
||||
|
||||
// NB: Fields 10 and 11 are deemed obsolete. Field 11 should always be empty, and will be ignored here.
|
||||
var characterData = new UnicodeCharacterDataBuilder(codePoint)
|
||||
{
|
||||
Name = NullIfEmpty(name),
|
||||
Category = UnicodeCategoryInfo.FromShortName(reader.ReadField()).Category,
|
||||
CanonicalCombiningClass = (CanonicalCombiningClass)byte.Parse(reader.ReadField()),
|
||||
};
|
||||
|
||||
BidirectionalClass bidirectionalClass;
|
||||
if (EnumHelper<BidirectionalClass>.TryGetNamedValue(reader.ReadField(), out bidirectionalClass))
|
||||
{
|
||||
characterData.BidirectionalClass = bidirectionalClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException(string.Format("Missing Bidi_Class property for code point(s) {0}.", codePoint));
|
||||
}
|
||||
|
||||
characterData.DecompositionType = NullIfEmpty(reader.ReadField());
|
||||
|
||||
string numericDecimalField = NullIfEmpty(reader.ReadField());
|
||||
string numericDigitField = NullIfEmpty(reader.ReadField());
|
||||
string numericNumericField = NullIfEmpty(reader.ReadField());
|
||||
|
||||
characterData.BidirectionalMirrored = reader.ReadField() == "Y";
|
||||
characterData.OldName = NullIfEmpty(reader.ReadField());
|
||||
reader.SkipField();
|
||||
characterData.SimpleUpperCaseMapping = ParseSimpleCaseMapping(reader.ReadField());
|
||||
characterData.SimpleLowerCaseMapping = ParseSimpleCaseMapping(reader.ReadField());
|
||||
characterData.SimpleTitleCaseMapping = ParseSimpleCaseMapping(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 (numericNumericField != null)
|
||||
{
|
||||
characterData.NumericValue = UnicodeRationalNumber.Parse(numericNumericField);
|
||||
|
||||
if (numericDigitField != null)
|
||||
{
|
||||
if (numericDigitField != numericNumericField)
|
||||
{
|
||||
throw new InvalidDataException("Invalid value for field 7 of code point " + characterData.CodePointRange.ToString() + ".");
|
||||
}
|
||||
|
||||
if (numericDecimalField != null)
|
||||
{
|
||||
if (numericDecimalField != numericDigitField)
|
||||
{
|
||||
throw new InvalidDataException("Invalid value for field 6 of code point " + characterData.CodePointRange.ToString() + ".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
characterData.NumericType = UnicodeNumericType.Digit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
characterData.NumericType = UnicodeNumericType.Numeric;
|
||||
}
|
||||
}
|
||||
|
||||
builder.Insert(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))
|
||||
{
|
||||
builder.SetProperties(property, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToUnicodeData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8DFDEE6C-4F0D-4DE1-B346-574CB56D2B8B}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>System.Unicode.Builder</RootNamespace>
|
||||
<AssemblyName>UnicodeInformation.Builder</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\System.Unicode.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileUcdSource.cs" />
|
||||
<Compile Include="HttpUcdSource.cs" />
|
||||
<Compile Include="IUcdSource.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UnicodeCharacterDataBuilder.cs" />
|
||||
<Compile Include="UnicodeDataBuilder.cs" />
|
||||
<Compile Include="UnicodeDataFileReader.cs" />
|
||||
<Compile Include="UnicodeDataManager.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\System.Unicode.snk">
|
||||
<Link>System.Unicode.snk</Link>
|
||||
</None>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UnicodeInformation\UnicodeInformation.csproj">
|
||||
<Project>{cb722958-a1c4-4121-804b-7d5a671491b1}</Project>
|
||||
<Name>UnicodeInformation</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
Reference in New Issue
Block a user