diff --git a/UnicodeCharacterInspector/App.xaml b/UnicodeCharacterInspector/App.xaml index 6063d01..5bbd14d 100644 --- a/UnicodeCharacterInspector/App.xaml +++ b/UnicodeCharacterInspector/App.xaml @@ -6,5 +6,6 @@ + diff --git a/UnicodeCharacterInspector/CharacterInfoViewModel.cs b/UnicodeCharacterInspector/CharacterInfoViewModel.cs index 9947d2e..7b9e67a 100644 --- a/UnicodeCharacterInspector/CharacterInfoViewModel.cs +++ b/UnicodeCharacterInspector/CharacterInfoViewModel.cs @@ -49,19 +49,30 @@ namespace UnicodeCharacterInspector } NotifyPropertyChanged(); - NotifyPropertyChanged("CodePoint"); - NotifyPropertyChanged("Name"); - NotifyPropertyChanged("OldName"); - NotifyPropertyChanged("Category"); - NotifyPropertyChanged("Block"); - NotifyPropertyChanged("CanonicalCombiningClass"); - NotifyPropertyChanged("BidirectionalClass"); - NotifyPropertyChanged("DecompositionType"); - NotifyPropertyChanged("DecompositionMapping"); - NotifyPropertyChanged("NumericType"); - NotifyPropertyChanged("NumericValue"); - NotifyPropertyChanged("ContributoryProperties"); - NotifyPropertyChanged("CoreProperties"); + NotifyPropertyChanged(nameof(CodePoint)); + NotifyPropertyChanged(nameof(Name)); + NotifyPropertyChanged(nameof(OldName)); + NotifyPropertyChanged(nameof(Definition)); + NotifyPropertyChanged(nameof(Category)); + NotifyPropertyChanged(nameof(Block)); + NotifyPropertyChanged(nameof(CanonicalCombiningClass)); + NotifyPropertyChanged(nameof(BidirectionalClass)); + NotifyPropertyChanged(nameof(DecompositionType)); + NotifyPropertyChanged(nameof(DecompositionMapping)); + NotifyPropertyChanged(nameof(NumericType)); + NotifyPropertyChanged(nameof(UnihanNumericType)); + NotifyPropertyChanged(nameof(NumericValue)); + NotifyPropertyChanged(nameof(ContributoryProperties)); + NotifyPropertyChanged(nameof(CoreProperties)); + NotifyPropertyChanged(nameof(MandarinReading)); + NotifyPropertyChanged(nameof(CantoneseReading)); + NotifyPropertyChanged(nameof(JapaneseKunReading)); + NotifyPropertyChanged(nameof(JapaneseOnReading)); + NotifyPropertyChanged(nameof(KoreanReading)); + NotifyPropertyChanged(nameof(HangulReading)); + NotifyPropertyChanged(nameof(VietnameseReading)); + NotifyPropertyChanged(nameof(SimplifiedVariant)); + NotifyPropertyChanged(nameof(TraditionalVariant)); } } } @@ -81,6 +92,11 @@ namespace UnicodeCharacterInspector get { return character != null ? characterInfo.OldName : null; } } + public string Definition + { + get { return character != null ? characterInfo.Definition : null; } + } + public UnicodeCategory? Category { get { return character != null ? characterInfo.Category : null as UnicodeCategory?; } @@ -116,6 +132,11 @@ namespace UnicodeCharacterInspector get { return character != null ? characterInfo.NumericType : null as UnicodeNumericType?; } } + public UnihanNumericType? UnihanNumericType + { + get { return character != null ? characterInfo.UnihanNumericType : null as UnihanNumericType?; } + } + public UnicodeRationalNumber? NumericValue { get { return character != null && characterInfo.NumericType != UnicodeNumericType.None ? characterInfo.NumericValue : null as UnicodeRationalNumber?; } @@ -130,5 +151,50 @@ namespace UnicodeCharacterInspector { get { return character != null ? characterInfo.CoreProperties : null as CoreProperties?; } } + + public string MandarinReading + { + get { return character != null ? characterInfo.MandarinReading : null; } + } + + public string CantoneseReading + { + get { return character != null ? characterInfo.CantoneseReading : null; } + } + + public string JapaneseKunReading + { + get { return character != null ? characterInfo.JapaneseKunReading : null; } + } + + public string JapaneseOnReading + { + get { return character != null ? characterInfo.JapaneseOnReading : null; } + } + + public string KoreanReading + { + get { return character != null ? characterInfo.KoreanReading : null; } + } + + public string HangulReading + { + get { return character != null ? characterInfo.HangulReading : null; } + } + + public string VietnameseReading + { + get { return character != null ? characterInfo.VietnameseReading : null; } + } + + public string SimplifiedVariant + { + get { return character != null ? characterInfo.SimplifiedVariant : null; } + } + + public string TraditionalVariant + { + get { return character != null ? characterInfo.TraditionalVariant : null; } + } } } diff --git a/UnicodeCharacterInspector/MainWindow.xaml b/UnicodeCharacterInspector/MainWindow.xaml index 7820420..5790bb3 100644 --- a/UnicodeCharacterInspector/MainWindow.xaml +++ b/UnicodeCharacterInspector/MainWindow.xaml @@ -75,6 +75,10 @@ + + + + @@ -83,26 +87,34 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UnicodeCharacterInspector/UnicodeCharacterInspector.csproj b/UnicodeCharacterInspector/UnicodeCharacterInspector.csproj index b767a37..c50bb47 100644 --- a/UnicodeCharacterInspector/UnicodeCharacterInspector.csproj +++ b/UnicodeCharacterInspector/UnicodeCharacterInspector.csproj @@ -69,6 +69,7 @@ + MainWindow.xaml diff --git a/UnicodeCharacterInspector/ZeroToVisibilityConverter.cs b/UnicodeCharacterInspector/ZeroToVisibilityConverter.cs new file mode 100644 index 0000000..fe46568 --- /dev/null +++ b/UnicodeCharacterInspector/ZeroToVisibilityConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace UnicodeCharacterInspector +{ + internal sealed class ZeroToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value != null && System.Convert.ToInt32(value) != 0 ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + } +} diff --git a/UnicodeInformation.Builder/CharacterDecompositionMapping.cs b/UnicodeInformation.Builder/CharacterDecompositionMapping.cs index 17ded32..c5709de 100644 --- a/UnicodeInformation.Builder/CharacterDecompositionMapping.cs +++ b/UnicodeInformation.Builder/CharacterDecompositionMapping.cs @@ -46,7 +46,7 @@ namespace System.Unicode.Builder if (c == ' ') ++index; else { - int codePoint = ParseCodePoint(s, ref index); + int codePoint = HexCodePoint.Parse(s, ref index); if (codePoint < 0x10000) buffer[charIndex++] = (char)codePoint; @@ -65,30 +65,5 @@ namespace System.Unicode.Builder return new CharacterDecompositionMapping(tag, new string(buffer, 0, charIndex)); } - - private static int ParseCodePoint(string s, ref int index) - { - int i = index; - int accum = 0; - - while (i < s.Length) - { - char c = s[i]; - - if (c == ' ') break; - - accum <<= 4; - - if (c >= '0' && c <= '9') accum |= c - '0'; - else if (c >= 'A' && c <= 'F') accum |= c - 'A' + 0xA; - else if (c >= 'a' && c <= 'f') accum |= c - 'a' + 0xA; - else throw new FormatException(); - - ++i; - } - - index = i; - return accum; - } } } diff --git a/UnicodeInformation.Builder/HexCodePoint.cs b/UnicodeInformation.Builder/HexCodePoint.cs new file mode 100644 index 0000000..9f63d99 --- /dev/null +++ b/UnicodeInformation.Builder/HexCodePoint.cs @@ -0,0 +1,44 @@ +namespace System.Unicode.Builder +{ + public static class HexCodePoint + { + public static int ParsePrefixed(string s) + { + if (!s.StartsWith("U+")) + { + throw new FormatException("Expected a code point in the form U+nnnn."); + } + return Parse(s, 2); + } + + public static int Parse(string s, int index) + { + return Parse(s, ref index); + } + + public static int Parse(string s, ref int index) + { + int i = index; + int accum = 0; + + while (i < s.Length) + { + char c = s[i]; + + if (c == ' ') break; + + accum <<= 4; + + if (c >= '0' && c <= '9') accum |= c - '0'; + else if (c >= 'A' && c <= 'F') accum |= c - 'A' + 0xA; + else if (c >= 'a' && c <= 'f') accum |= c - 'a' + 0xA; + else throw new FormatException(); + + ++i; + } + + index = i; + return accum; + } + } +} diff --git a/UnicodeInformation.Builder/UnicodeDataFileReader.cs b/UnicodeInformation.Builder/UnicodeDataFileReader.cs index f40567a..7aac2ab 100644 --- a/UnicodeInformation.Builder/UnicodeDataFileReader.cs +++ b/UnicodeInformation.Builder/UnicodeDataFileReader.cs @@ -9,80 +9,24 @@ namespace System.Unicode.Builder { public class UnicodeDataFileReader : IDisposable { - private struct Utf8Buffer - { - private readonly UnicodeDataFileReader reader; - private int length; - - public Utf8Buffer(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.utf8StringBuffer.Length < checked(length + count)) - Array.Resize(ref reader.utf8StringBuffer, Math.Max(length + count, reader.utf8StringBuffer.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.utf8StringBuffer; - - for (int i = startIndex; i < count; ++i) - { - buffer[length++] = value[i]; - } - } - - public override string ToString() - { - return length > 0 ? Encoding.UTF8.GetString(reader.utf8StringBuffer, 0, length) : string.Empty; - } - - public string ToTrimmedString() - { - if (length == 0) return string.Empty; - - var buffer = reader.utf8StringBuffer; - int start = 0; - int end = length; - - while (buffer[start] == ' ') if (++start == length) return string.Empty; - while (buffer[--end] == ' ') ; - - return length > 0 ? Encoding.UTF8.GetString(reader.utf8StringBuffer, start, end - start + 1) : string.Empty; - } - } - private readonly Stream stream; private readonly byte[] byteBuffer; - private byte[] utf8StringBuffer; private int index; private int length; + private readonly char fieldSeparator; private bool hasField = false; private readonly bool leaveOpen; - public UnicodeDataFileReader(Stream stream) - : this(stream, false) + public UnicodeDataFileReader(Stream stream, char fieldSeparator) + : this(stream, fieldSeparator, false) { } - public UnicodeDataFileReader(Stream stream, bool leaveOpen) + public UnicodeDataFileReader(Stream stream, char fieldSeparator, bool leaveOpen) { this.stream = stream; + this.fieldSeparator = fieldSeparator; this.byteBuffer = new byte[8192]; - this.utf8StringBuffer = new byte[100]; this.leaveOpen = leaveOpen; } @@ -163,49 +107,51 @@ namespace System.Unicode.Builder } } - var buffer = new Utf8Buffer(this); - int startOffset; - int endOffset; - - do + using (var buffer = Utf8Buffer.Get()) { - startOffset = index; - endOffset = -1; + int startOffset; + int endOffset; - while (index < length) + do { - byte b = byteBuffer[index]; + startOffset = index; + endOffset = -1; - if (IsNewLineOrComment(b)) // NB: Do not advance to the next byte when end of line has been reached. + while (index < length) { - endOffset = index; - hasField = false; + 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 == fieldSeparator) + { + endOffset = index++; + break; + } + else + { + ++index; + } + } + + if (endOffset >= 0) + { + buffer.Append(byteBuffer, startOffset, endOffset - startOffset); break; } - else if(b == ';') + else if (index > startOffset) { - endOffset = index++; - break; + buffer.Append(byteBuffer, startOffset, index - startOffset); } - else - { - ++index; - } - } + } while (RefillBuffer()); - if (endOffset >= 0) - { - buffer.Append(byteBuffer, startOffset, endOffset - startOffset); - break; - } - else if (index > startOffset) - { - buffer.Append(byteBuffer, startOffset, index - startOffset); - } - } while (RefillBuffer()); - - return trim ? buffer.ToTrimmedString() : buffer.ToString(); - } + return trim ? buffer.ToTrimmedString() : buffer.ToString(); + } + } /// Reads the next data field. /// This method will return on end of line. @@ -255,7 +201,7 @@ namespace System.Unicode.Builder { ++index; - if (b == ';') + if (b == fieldSeparator) { return true; } diff --git a/UnicodeInformation.Builder/UnicodeDataProcessor.cs b/UnicodeInformation.Builder/UnicodeDataProcessor.cs index 93a90a9..6deb488 100644 --- a/UnicodeInformation.Builder/UnicodeDataProcessor.cs +++ b/UnicodeInformation.Builder/UnicodeDataProcessor.cs @@ -14,6 +14,9 @@ namespace System.Unicode.Builder public const string PropListFileName = "PropList.txt"; public const string DerivedCorePropertiesFileName = "DerivedCoreProperties.txt"; public const string BlocksFileName = "Blocks.txt"; + public const string UnihanReadingsFileName = "Unihan_Readings.txt"; + public const string UnihanVariantsFileName = "Unihan_Variants.txt"; + public const string UnihanNumericValuesFileName = "Unihan_NumericValues.txt"; private static string ParseSimpleCaseMapping(string mapping) { @@ -35,13 +38,16 @@ namespace System.Unicode.Builder await ProcessPropListFile(ucdSource, builder).ConfigureAwait(false); await ProcessDerivedCorePropertiesFile(ucdSource, builder).ConfigureAwait(false); await ProcessBlocksFile(ucdSource, builder).ConfigureAwait(false); + await ProcessUnihanReadings(unihanSource, builder).ConfigureAwait(false); + await ProcessUnihanVariants(unihanSource, builder).ConfigureAwait(false); + await ProcessUnihanNumericValues(unihanSource, builder).ConfigureAwait(false); return builder; } private static async Task ProcessUnicodeDataFile(IDataSource ucdSource, UnicodeInfoBuilder builder) { - using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(UnicodeDataFileName).ConfigureAwait(false))) + using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(UnicodeDataFileName).ConfigureAwait(false), ';')) { int rangeStartCodePoint = -1; @@ -158,7 +164,7 @@ namespace System.Unicode.Builder private static async Task ProcessPropListFile(IDataSource ucdSource, UnicodeInfoBuilder builder) { - using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(PropListFileName).ConfigureAwait(false))) + using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(PropListFileName).ConfigureAwait(false), ';')) { while (reader.MoveToNextLine()) { @@ -175,7 +181,7 @@ namespace System.Unicode.Builder private static async Task ProcessDerivedCorePropertiesFile(IDataSource ucdSource, UnicodeInfoBuilder builder) { - using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(DerivedCorePropertiesFileName).ConfigureAwait(false))) + using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(DerivedCorePropertiesFileName).ConfigureAwait(false), ';')) { while (reader.MoveToNextLine()) { @@ -192,7 +198,7 @@ namespace System.Unicode.Builder private static async Task ProcessBlocksFile(IDataSource ucdSource, UnicodeInfoBuilder builder) { - using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(BlocksFileName).ConfigureAwait(false))) + using (var reader = new UnicodeDataFileReader(await ucdSource.OpenDataFileAsync(BlocksFileName).ConfigureAwait(false), ';')) { while (reader.MoveToNextLine()) { @@ -200,5 +206,126 @@ namespace System.Unicode.Builder } } } + + private static async Task ProcessUnihanReadings(IDataSource unihanDataSource, UnicodeInfoBuilder builder) + { + using (var reader = new UnihanDataFileReader(await unihanDataSource.OpenDataFileAsync(UnihanReadingsFileName).ConfigureAwait(false))) + { + while (reader.Read()) + { + // This statement is used to skip unhandled properties entirely. + switch (reader.PropertyName) + { + case UnihanProperty.kDefinition: + case UnihanProperty.kMandarin: + case UnihanProperty.kCantonese: + case UnihanProperty.kJapaneseKun: + case UnihanProperty.kJapaneseOn: + case UnihanProperty.kKorean: + case UnihanProperty.kHangul: + case UnihanProperty.kVietnamese: + break; + default: + // Ignore unhandled properties for now. + continue; + } + + // This entry will only be created if there is meaningful data. + var entry = builder.GetUnihan(reader.CodePoint); + + switch (reader.PropertyName) + { + case UnihanProperty.kDefinition: + entry.Definition = reader.PropertyValue; + break; + case UnihanProperty.kMandarin: + entry.MandarinReading = reader.PropertyValue; + break; + case UnihanProperty.kCantonese: + entry.CantoneseReading = reader.PropertyValue; + break; + case UnihanProperty.kJapaneseKun: + entry.JapaneseKunReading = reader.PropertyValue; + break; + case UnihanProperty.kJapaneseOn: + entry.JapaneseOnReading = reader.PropertyValue; + break; + case UnihanProperty.kKorean: + entry.KoreanReading = reader.PropertyValue; + break; + case UnihanProperty.kHangul: + entry.HangulReading = reader.PropertyValue; + break; + case UnihanProperty.kVietnamese: + entry.VietnameseReading = reader.PropertyValue; + break; + default: + throw new InvalidOperationException(); + } + } + } + } + + private static async Task ProcessUnihanVariants(IDataSource unihanDataSource, UnicodeInfoBuilder builder) + { + using (var reader = new UnihanDataFileReader(await unihanDataSource.OpenDataFileAsync(UnihanVariantsFileName).ConfigureAwait(false))) + { + while (reader.Read()) + { + // This statement is used to skip unhandled properties entirely. + switch (reader.PropertyName) + { + case UnihanProperty.kSimplifiedVariant: + case UnihanProperty.kTraditionalVariant: + break; + default: + // Ignore unhandled properties for now. + continue; + } + + var entry = builder.GetUnihan(reader.CodePoint); + + switch (reader.PropertyName) + { + case UnihanProperty.kSimplifiedVariant: + entry.SimplifiedVariant = char.ConvertFromUtf32(HexCodePoint.ParsePrefixed(reader.PropertyValue)); + break; + case UnihanProperty.kTraditionalVariant: + entry.TraditionalVariant = char.ConvertFromUtf32(HexCodePoint.ParsePrefixed(reader.PropertyValue)); + break; + default: + throw new InvalidOperationException(); + } + } + } + } + + private static async Task ProcessUnihanNumericValues(IDataSource unihanDataSource, UnicodeInfoBuilder builder) + { + using (var reader = new UnihanDataFileReader(await unihanDataSource.OpenDataFileAsync(UnihanNumericValuesFileName).ConfigureAwait(false))) + { + while (reader.Read()) + { + var entry = builder.GetUnihan(reader.CodePoint); + + switch (reader.PropertyName) + { + case UnihanProperty.kAccountingNumeric: + entry.NumericType = UnihanNumericType.Accounting; + break; + case UnihanProperty.kOtherNumeric: + entry.NumericType = UnihanNumericType.Other; + break; + case UnihanProperty.kPrimaryNumeric: + entry.NumericType = UnihanNumericType.Primary; + break; + default: + throw new InvalidDataException("Unrecognized property name: " + reader.PropertyName + "."); + } + + entry.NumericValue = long.Parse(reader.PropertyValue); + } + } + } } } diff --git a/UnicodeInformation.Builder/UnicodeInfoBuilder.cs b/UnicodeInformation.Builder/UnicodeInfoBuilder.cs index 19027cb..b3ef64a 100644 --- a/UnicodeInformation.Builder/UnicodeInfoBuilder.cs +++ b/UnicodeInformation.Builder/UnicodeInfoBuilder.cs @@ -10,8 +10,10 @@ namespace System.Unicode.Builder internal class UnicodeInfoBuilder { private readonly Version unicodeVersion; - private UnicodeCharacterDataBuilder[] entries = new UnicodeCharacterDataBuilder[10000]; - private int entryCount; + private UnicodeCharacterDataBuilder[] ucdEntries = new UnicodeCharacterDataBuilder[10000]; + private int ucdEntryCount; + private UnihanCharacterDataBuilder[] unihanEntries = new UnihanCharacterDataBuilder[10000]; + private int unihanEntryCount; private List blockEntries = new List(100); public UnicodeInfoBuilder(Version unicodeVersion) @@ -19,18 +21,18 @@ namespace System.Unicode.Builder this.unicodeVersion = unicodeVersion; } - private int FindCodePoint(int codePoint) + private int FindUcdCodePoint(int codePoint) { - if (entryCount == 0) return -1; + if (ucdEntryCount == 0) return -1; int minIndex = 0; - int maxIndex = entryCount - 1; + int maxIndex = ucdEntryCount - 1; do { int index = (minIndex + maxIndex) >> 1; - int Δ = entries[index].CodePointRange.CompareCodePoint(codePoint); + int Δ = ucdEntries[index].CodePointRange.CompareCodePoint(codePoint); if (Δ == 0) return index; else if (Δ < 0) maxIndex = index - 1; @@ -40,61 +42,144 @@ namespace System.Unicode.Builder return -1; } - private int FindInsertionPoint(int startCodePoint, int endCodePoint) + private int FindUcdInsertionPoint(int startCodePoint, int endCodePoint) { int minIndex; int maxIndex; - if (entryCount == 0 || entries[maxIndex = entryCount - 1].CodePointRange.LastCodePoint < startCodePoint) return entryCount; - else if (endCodePoint < entries[minIndex = 0].CodePointRange.FirstCodePoint) return 0; - else if (entryCount == 1) return -1; + if (ucdEntryCount == 0 || ucdEntries[maxIndex = ucdEntryCount - 1].CodePointRange.LastCodePoint < startCodePoint) return ucdEntryCount; + else if (endCodePoint < ucdEntries[minIndex = 0].CodePointRange.FirstCodePoint) return 0; + else if (ucdEntryCount == 1) return -1; do { int index = (minIndex + maxIndex) >> 1; - int Δ = entries[index].CodePointRange.CompareCodePoint(startCodePoint); + int Δ = ucdEntries[index].CodePointRange.CompareCodePoint(startCodePoint); if (Δ == 0) return -1; else if (Δ < 0) maxIndex = index; else minIndex = index; } while (maxIndex - minIndex > 1); - if (entries[maxIndex].CodePointRange.FirstCodePoint < endCodePoint) return -1; + if (ucdEntries[maxIndex].CodePointRange.FirstCodePoint < endCodePoint) return -1; + else return maxIndex; + } + + private int FindUnihanCodePoint(int codePoint) + { + if (unihanEntryCount == 0) return -1; + + int minIndex = 0; + int maxIndex = unihanEntryCount - 1; + + do + { + int index = (minIndex + maxIndex) >> 1; + + int Δ = codePoint - unihanEntries[index].CodePoint; + + if (Δ == 0) return index; + else if (Δ < 0) maxIndex = index - 1; + else minIndex = index + 1; + } while (minIndex <= maxIndex); + + return -1; + } + + private int FindUnihanInsertionPoint(int codePoint) + { + int minIndex; + int maxIndex; + + if (unihanEntryCount == 0 || unihanEntries[maxIndex = unihanEntryCount - 1].CodePoint < codePoint) return unihanEntryCount; + else if (codePoint < unihanEntries[minIndex = 0].CodePoint) return 0; + else if (unihanEntryCount == 1) return -1; + + do + { + int index = (minIndex + maxIndex) >> 1; + + int Δ = codePoint - unihanEntries[index].CodePoint; + + if (Δ == 0) return -1; + else if (Δ < 0) maxIndex = index; + else minIndex = index; + } while (maxIndex - minIndex > 1); + + if (unihanEntries[maxIndex].CodePoint < codePoint) return -1; else return maxIndex; } public void Insert(UnicodeCharacterDataBuilder data) { - int insertionPoint = FindInsertionPoint(data.CodePointRange.FirstCodePoint, data.CodePointRange.LastCodePoint); + int insertionPoint = FindUcdInsertionPoint(data.CodePointRange.FirstCodePoint, data.CodePointRange.LastCodePoint); if (insertionPoint < 0) throw new InvalidOperationException("The specified range overlaps with pre-existing ranges."); - if (insertionPoint >= entries.Length) + if (insertionPoint >= ucdEntries.Length) { - Array.Resize(ref entries, entries.Length << 1); + Array.Resize(ref ucdEntries, ucdEntries.Length << 1); } - if (insertionPoint < entryCount) + if (insertionPoint < ucdEntryCount) { - Array.Copy(entries, insertionPoint, entries, insertionPoint + 1, entryCount - insertionPoint); + Array.Copy(ucdEntries, insertionPoint, ucdEntries, insertionPoint + 1, ucdEntryCount - insertionPoint); } - entries[insertionPoint] = data; - ++entryCount; + ucdEntries[insertionPoint] = data; + ++ucdEntryCount; } - public UnicodeCharacterDataBuilder Get(int codePoint) + private void Insert(UnihanCharacterDataBuilder data) { - int index = FindCodePoint(codePoint); + int insertionPoint = FindUnihanInsertionPoint(data.CodePoint); - return index >= 0 ? entries[index] : null; + if (insertionPoint < 0) throw new InvalidOperationException("The specified range overlaps with pre-existing ranges."); + + if (insertionPoint >= unihanEntries.Length) + { + Array.Resize(ref unihanEntries, unihanEntries.Length << 1); + } + + if (insertionPoint < unihanEntryCount) + { + Array.Copy(unihanEntries, insertionPoint, unihanEntries, insertionPoint + 1, unihanEntryCount - insertionPoint); + } + + unihanEntries[insertionPoint] = data; + ++unihanEntryCount; + } + + public UnicodeCharacterDataBuilder GetUcd(int codePoint) + { + int index = FindUcdCodePoint(codePoint); + + return index >= 0 ? ucdEntries[index] : null; + } + + public UnihanCharacterDataBuilder GetUnihan(int codePoint) + { + int index = FindUnihanCodePoint(codePoint); + + if (index >= 0) + { + return unihanEntries[index]; + } + else + { + var data = new UnihanCharacterDataBuilder(codePoint); + + Insert(data); + + return data; + } } public void SetProperties(ContributoryProperties property, UnicodeCharacterRange codePointRange) { - int firstIndex = FindCodePoint(codePointRange.FirstCodePoint); - int lastIndex = FindCodePoint(codePointRange.LastCodePoint); + int firstIndex = FindUcdCodePoint(codePointRange.FirstCodePoint); + int lastIndex = FindUcdCodePoint(codePointRange.LastCodePoint); if (firstIndex < 0 && lastIndex < 0) { @@ -104,8 +189,8 @@ namespace System.Unicode.Builder if (firstIndex < 0 || lastIndex < 0 - || entries[firstIndex].CodePointRange.FirstCodePoint < codePointRange.FirstCodePoint - || entries[lastIndex].CodePointRange.LastCodePoint > codePointRange.LastCodePoint) + || ucdEntries[firstIndex].CodePointRange.FirstCodePoint < codePointRange.FirstCodePoint + || ucdEntries[lastIndex].CodePointRange.LastCodePoint > codePointRange.LastCodePoint) { throw new InvalidOperationException(); } @@ -114,7 +199,7 @@ namespace System.Unicode.Builder while (true) { - entries[i].ContributoryProperties |= property; + ucdEntries[i].ContributoryProperties |= property; if (i == lastIndex) break; @@ -124,8 +209,8 @@ namespace System.Unicode.Builder public void SetProperties(CoreProperties property, UnicodeCharacterRange codePointRange) { - int firstIndex = FindCodePoint(codePointRange.FirstCodePoint); - int lastIndex = FindCodePoint(codePointRange.LastCodePoint); + int firstIndex = FindUcdCodePoint(codePointRange.FirstCodePoint); + int lastIndex = FindUcdCodePoint(codePointRange.LastCodePoint); if (firstIndex < 0 && lastIndex < 0) { @@ -135,8 +220,8 @@ namespace System.Unicode.Builder if (firstIndex < 0 || lastIndex < 0 - || entries[firstIndex].CodePointRange.FirstCodePoint < codePointRange.FirstCodePoint - || entries[lastIndex].CodePointRange.LastCodePoint > codePointRange.LastCodePoint) + || ucdEntries[firstIndex].CodePointRange.FirstCodePoint < codePointRange.FirstCodePoint + || ucdEntries[lastIndex].CodePointRange.LastCodePoint > codePointRange.LastCodePoint) { throw new InvalidOperationException(); } @@ -145,7 +230,7 @@ namespace System.Unicode.Builder while (true) { - entries[i].CoreProperties |= property; + ucdEntries[i].CoreProperties |= property; if (i == lastIndex) break; @@ -160,12 +245,17 @@ namespace System.Unicode.Builder public UnicodeInfo ToUnicodeData() { - var finalData = new UnicodeCharacterData[entryCount]; + var finalUnicodeData = new UnicodeCharacterData[ucdEntryCount]; - for (int i = 0; i < finalData.Length; ++i) - finalData[i] = entries[i].ToCharacterData(); + for (int i = 0; i < finalUnicodeData.Length; ++i) + finalUnicodeData[i] = ucdEntries[i].ToCharacterData(); - return new UnicodeInfo(unicodeVersion, finalData, blockEntries.ToArray()); + var finalUnihanData = new UnihanCharacterData[unihanEntryCount]; + + for (int i = 0; i < finalUnihanData.Length; ++i) + finalUnihanData[i] = unihanEntries[i].ToCharacterData(); + + return new UnicodeInfo(unicodeVersion, finalUnicodeData, finalUnihanData, blockEntries.ToArray()); } private void WriteUnicodeBlockToFile(BinaryWriter writer, UnicodeBlock block) @@ -182,15 +272,22 @@ namespace System.Unicode.Builder writer.Write(new byte[] { (byte)'U', (byte)'C', (byte)'D', 0 }); writer.Write((ushort)7); writer.Write((byte)0); - writer.WriteCodePoint(entryCount); - for (int i = 0; i < entryCount; ++i) + writer.WriteCodePoint(ucdEntryCount); + for (int i = 0; i < ucdEntryCount; ++i) { - entries[i].WriteToFile(writer); + ucdEntries[i].WriteToFile(writer); } if (blockEntries.Count > 255) throw new InvalidOperationException("There are too many block entries. The file format needs to be upgraded."); writer.Write((byte)blockEntries.Count); for (int i = 0; i < blockEntries.Count; ++i) + { WriteUnicodeBlockToFile(writer, blockEntries[i]); + } + writer.WriteCodePoint(unihanEntryCount); + for (int i = 0; i < unihanEntryCount; ++i) + { + unihanEntries[i].WriteToFile(writer); + } } } diff --git a/UnicodeInformation.Builder/UnicodeInformation.Builder.csproj b/UnicodeInformation.Builder/UnicodeInformation.Builder.csproj index 1294119..1408e46 100644 --- a/UnicodeInformation.Builder/UnicodeInformation.Builder.csproj +++ b/UnicodeInformation.Builder/UnicodeInformation.Builder.csproj @@ -53,6 +53,15 @@ + + True + True + UnihanProperty.tt + + + + + @@ -77,6 +86,15 @@ UnicodeInformation + + + TextTemplatingFileGenerator + UnihanProperty.cs + + + + +