Skip to content

Commit 1545efb

Browse files
authored
BREAKING: Added Rule-Based Number Format Static API (#46)
* ICU4N.Text.NFRuleSet: Changed casing of private constants to match .NET conventions * ICU4N.Text.NFRule: Changed casing of private constants to match .NET conventions * ICU4N.Text (NFRule + NFRuleSet + NFSubstitution + PluralRules): Added note about using Math.Round() instead of Math.Ceiling() because the Java default is ToPositiveInfinity (the equivalent of Math.Ceiling), but the tests only pass with this configuration * ICU4N.Text.NFRule: Corrected casing of RULE_PREFIXES array * ICU4N.Text.NFRuleSet: Changed fractionRules from LinkedList to List, since we don't use any linked list functionality * ICU4N.Numerics.NumberPropertyMapper: Added note about locale not being referenced * RbnfTest: Added proof of concept test for how to format all built-in number formats using the .NET formatter as a replacement for DecimalFormat. * ICU4N.Text.NFRuleSet: Converted the documentation comments * ICU4N.Text.NFRule: Converted the documentation comments * ICU4N.Text.SameValueSubstitution: Updated doc comments * ICU4N.Text.RuleBasedNumberFormat::FindRuleSet(): Converted doc comments * Added skeleton for NumberFormatRules (an immutable RBNF rules engine) that contains all state and accessors * ICU4N.Impl.PatternProps: Added WhiteSpace static field with the same values that are matched with PatternProps.IsWhiteSpace() for use in Trim() method * ICU4N.Globalization: Added RuleBasedNumberFormat methods to parse the rule text into rule/rule set/substitution objects. Also sealed all non-inheritable classes, enabled nullable reference type support, and added guard clauses, where appropriate. * ICU4N.Globalization.NumberFormatRule: Re-wired to use MessagePattern instead of PluralRules, since PluralRules are culture data dependent. * ICU4N.Text (SplitTokenizerEnumerator + MultiDelimiterSplitTokenizerEnumerator): Added features to control delimiter length to exclude from the token and trim behavior for start/end/both (or specify no trimChars for no trimming) * ICU4N.Impl.PatternProps: Fixed initialization order issue with WhiteSpace field. * ICU4N.Globalization: Added tests for NumberFormatRules to ensure the state is equivalent between RuleBasedNumberFormat and NumberFormatRules with the same input string and fixed several bugs in the parser. * PERFORMANCE: ICU4N.Globalization.UCultureInfo.Name: Cache the name locally so it doesn't have to be parsed on every request. * ICU4N.Globalization.UCultureInfo::Canonicalize(): Added null guard clause * ICU4N.Util.ResourceBundle::SetRootType(): Fixed fallthrough to default for Missing case * BREAKING: ICU4N.Impl.ResourceKey::Substring(): Converted from start/end to startIndex/length to match .NET conventions. Optimized to use ValueStringBuilder, where Span<T> is supported. * ICU4N.Impl.ICUResourceBundle: Added TODO for API rework * ICU4N.Util.UResourceBundle: Made GetBundleInstance(string, string, Assembly, bool) and LocaleID property visible internally (as well as protected) * ICU4N.Globalization.NumberFormatRules: Added cache and static factory method to create an instance from UCultureInfo/NumberPresentation. * ICU4N.Globalization.NumberFormatRules: Marked class public * ICU4N.Globalization: (UCultureData, UCultureInfo, UNumberFormatInfo): Added functionality to load and cache resource data in UCultureData and integrated it with UCultureInfo and UNumberFormatInfo. * ICU4N.Impl.ICUResourceBundle.CreateUCultureList: Use a read-only non-cached copy of UCultureInfo when creating the culture list * ICU4N.Globalization.NumberFormatRules: Added overload of CreateInstance() that accepts cultureName (locale baseName) as a string * ICU4N.Globalization.UNumberFormatInfo: Added SpellOut, Ordinal, Duration, and NumberingSystem properties * BREAKING: ICU4N.Impl.PluralRulesLoader + ICU4N.Text.PluralRules: Renamed ForLocale() > GetInstance(). Added overload to pass localeName as a string to decouple from UCultureInfo. * ICU4N.Text.BreakIterator: Added GetSentenceInstance() overload that accepts strings so we are decoupled from any UCultureInfo instance. * ICU4N.Globalization (UCultureData + UCultureInfo): Refactored to remove the UCultureInfo reference from UCultureData so it is allowed to go out of scope. * ICU4N.Globalization.NumberFormatRulesTest: Added test to measure string and object sizes (commented out) * ICU4N.Globalization.UNumberFormatInfoTest: Added tests to verify all cultures load decimal format settings correctly (we still have some way to go for currency data) * ICU4N.Globalization.UNumberFormatInfo: Added Capitalization property (DisplayContext in ICU4J). * ICU4N.Globalization.UNumberFormatInfo: Added NumberGroupSizes property * ICU4N.Globalization:UNumberFormatInfo: Added NativeDigitsLocal, NumberGroupSizesLocal, and DigitSubstitution properties. * ValueStringBuilder: Added TODO * ICU4N.IcuNumber: Added formatters for long and double to output native digits + tests to confirm that they are the same as the DecimalFormat instance that backs RuleBasedNumberFormat. * ICU4N.Text.ValueStringBuilder: Added Insert() overload to handle ReadOnlySpan<char> * ICU4N.Globalization (NumberFormatRules + NumberFormatRuleSet + NumberFormatRule + NumberFormatSubstitution): Added Format() method for double type (minus the plural format) * ICU4N.Support.IcuNumber: Added FormatPlural() method + tests. * ICU4N.Globalization:NumberFormattingRule: Added plural formatting call * ICU4N.Numerics: Added NumberPatternStringProperties struct to store only the info required to rebuild a pattern string * Implemented RuleBasedNumberFormat method overloads for long data type and integrated NumberPatternStringProperties for the business logic that depends on them. * ValueStringBuilder: Added Insert() method to add a single char and optimized Insert() overloads to exit early if count is 0 * ICU4N.Globalization (FractionalPartSubstitution + NumeratorSubstitution): Added missing DoSubstitution overrides * ICU4N.Globalization.NumberFormatRule: Pass in the PluralType so we know which set of plural rules to fetch when formatting. * ICU4N.Globalization.NumberFormatRuleSet: Fixed callers of GetBestFractionRule() to correctly use the constants from NumberFormatRule to lookup fraction rules * ICU4N.Globalization.NumberFormatRules: Redesigned private Format() methods from RBNF to be the entry point into NumberFormatRules and accept a ValueStringBuilder from the caller. * ICU4N.Globalization.NumberFormatRules: Added missing StripWhiteSpace method that is used to pre-process the rules. Made FindRuleSet() internal so we can utilize it from IcuNumber. * ICU4N.Text.NumberPresentationExtensions: Added IsDefined() method so we can quickly check for valid values in guard clauses. * ICU4N.IcuNumber: Added format methods for BigInteger and RuleBased long, double, and BigInteger * RbnfTest: Created an RbnfFormatterSettings class with the same constructors as RuleBasedNumberFormat that stores the state and creation logic to test our IcuNumber rule based format methods. Added an overload of doTest() that accepts this new class as a parameter in place of RuleBasedNumberFormat and added test logic. * RbnfRoundTripTest: Adapted RbnfFormatterSettings class so it can be used from both RbnfTest and RbnfRoundTripTest classes to test the static RBNF formatter * ICU4N.IcuNumber: Added AdjustForContext method to capitalize the output of rule-based format methods. * Moved NumberPresentation enum from ICU4N.Text to ICU4N.Globalization. Changed the values to start with 0 instead of 1 so we have a reasonable default value in .NET. * Fixed some build warnings * ICU4N.Numerics.DecimalQuantity_AbstractBCD: Optimized number parsing using ReadOnlySpan<char> where appropriate * ICU4N.Globalization: Refactored FormatPlural to accept a ValueStringBuilder parameter and fixed the insert operation of pluralization in NumberFormatRule to happen entirely on the stack (if possible). * ICU4N.IcuNumber: Increased the default stack buffer allocation for rule based formatting to 128 chars to cover most cases on the stack. * ICU4N.Globalization.NumberFormatRules: Removed unnecessary CharStackBufferSize constant * ICU4N.Globalization (NumberFormatRules + NumberFormatSubstitution): Use correct override values when formatting numbers (based on where the original DecimalFormat instances got their setting values) * ICU4N.Globalization.Capitialization: Updated docs * Changed the name of FEATURE_READONLYDICTIONARY to FEATURE_IREADONLYCOLLECTIONS to include IReadOnlyList<T> * ICU4N.Globalization.NumberFormatRules: Implemented public members Equals, GetHashCode, ToString(), and DefaultRuleSetName * Added target for .NET 7.0. Fixed conflicts due to the new AsReadOnly() extension methods and build errors due the unsafe logic in PluralRules. * ICU4N.Globalization.NumberFormatRules: Added RuleSetNames property. * ICU4N.Globalization.NumberPresentationExtensions: Added an extension method to quickly lookup the NumberingSystemRules instance for the current UNumberFormatInfo. * BUG: ICU4N.Globalization.NumberFormatRuleSet: Fixed master rule lookup for current context. We no longer store these rules in the nonNumericalRules array, they are in the fractionRules field instead. We get them using the GetBestFractionRule() method and supplying the fraction rule index from NumberFormatRule. * ICU4N.Globalization.NumberFormatRules::Equals(): Don't box when comparing equality on an enum * ICU4N.Globalization.NumberFormatRuleSet: Removed TODO that is already finished * ICU4N: Added FormatNumberRuleBased static class with public ToString() and TryFormat() overloads for every numeric data type. * ICU4N.Globalization.UNumberFormatInfo: Added API documentation and marked APIs internal that are not yet in use (such as properties for currency and percentage formatting). Implemented the missing ReadOnly() method. * ICU4N.Globalization.UCultureInfo: Added ReadOnly() and Clone() implementations and added API documentation for NumberFormat, IsReadOnly, ReadOnly() and Clone() members. * BREAKING: ICU4N.Globalization.UCultureInfo: Removed ClearCachedData() method. This is in .NET only to allow it to fetch culture settings that have changed in the underlying OS. * ICU4N.Globalization.NumberFormatRules: Marked GetInstance() method internal, for now. This won't be very useful until after we have public overloads of the format/parse operations that accept an instance of this. * ICU4N.Impl.ICUResourceBundle.WholeBundle: Lazy-load the UCultureInfo instance so we don't fill up the UCultureData cache with all cultures and non-culture resource names. * ICU4N.Globalization.NumberFormatRules::IsDefaultCandidateRule(): Marked private * BREAKING: ICU4N.Text (DisplayContext + DisplayContextType + DisplayContextExtensions): Removed from the public API since they had been previously refactored in the Globalization namespace as Capitalization, DialectHandling, DisplayLength, and DisplayLength enums and the DisplayContextOptions class * BREAKING: ICU4N.Text (NumberFormat + DecimalFormat + RuleBasedNumberFormat): Removed from the public API and added an IncludeLegacyNumberFormat build property so they can be optionally compiled for those who need these features. The plan is to port them into static APIs later. * ICU4N.FormatNumberRuleBased: Added missing support for System.UInt128 * README.md: Added info about RuleBasedNumberFormat being a feature. Corrected jargon error from transient > transitive dependencies. * BREAKING: ICU4N.Globalization.Capitalization: Added "For" prefix to the names as they were in ICU4J, since this adds clarity to their intended purpose. Also explicitly specified numeric values of all DisplayContext enums in ICU4N.Globalization to match ICU4J.
1 parent 172205e commit 1545efb

100 files changed

Lines changed: 14844 additions & 1038 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Directory.Build.targets

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33
<Import Project="$(MSBuildThisFileDirectory)/.build/dependencies.props" Condition="Exists('$(MSBuildThisFileDirectory)/.build/dependencies.props')" />
44
<Import Project="$(MSBuildThisFileDirectory)/.build/nowarn.props" Condition="Exists('$(MSBuildThisFileDirectory)/.build/nowarn.props')" />
55

6-
<!--Features in .NET 6+ only -->
6+
<!--Features in .NET 7+ only-->
7+
<PropertyGroup Condition=" $(TargetFramework.StartsWith('net7.')) ">
8+
9+
<DefineConstants>$(DefineConstants);FEATURE_IDICTIONARY_ASREADONLY</DefineConstants>
10+
<DefineConstants>$(DefineConstants);FEATURE_ILIST_ASREADONLY</DefineConstants>
11+
<DefineConstants>$(DefineConstants);FEATURE_INT128</DefineConstants>
12+
13+
</PropertyGroup>
14+
15+
<!--Features in .NET 6+ only-->
716
<PropertyGroup Condition=" $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">
817

918
<!-- For now, we only support ArrayPool on .NET 6. -->
@@ -16,6 +25,13 @@
1625
<DefineConstants>$(DefineConstants);FEATURE_SPANFORMATTABLE</DefineConstants>
1726

1827
</PropertyGroup>
28+
29+
<!--Features in .NET 5+ only-->
30+
<PropertyGroup Condition=" $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">
31+
32+
<DefineConstants>$(DefineConstants);FEATURE_HALF</DefineConstants>
33+
34+
</PropertyGroup>
1935

2036
<!--Features in .NET Standard 2.x or .NET Core-->
2137
<PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">
@@ -46,7 +62,7 @@
4662
<DefineConstants>$(DefineConstants);FEATURE_EXCEPTION_HRESULT</DefineConstants>
4763
<DefineConstants>$(DefineConstants);FEATURE_METHODIMPLOPTIONS_AGRESSIVEINLINING</DefineConstants>
4864
<DefineConstants>$(DefineConstants);FEATURE_MICROSOFT_EXTENSIONS_CACHING</DefineConstants>
49-
<DefineConstants>$(DefineConstants);FEATURE_READONLYDICTIONARY</DefineConstants>
65+
<DefineConstants>$(DefineConstants);FEATURE_IREADONLYCOLLECTIONS</DefineConstants>
5066
<DefineConstants>$(DefineConstants);FEATURE_REGEX_MATCHTIMEOUT</DefineConstants>
5167
<DefineConstants>$(DefineConstants);FEATURE_TASK_ASYNC_AWAIT</DefineConstants>
5268
<DefineConstants>$(DefineConstants);FEATURE_TASK_RUN</DefineConstants>
@@ -70,7 +86,10 @@
7086
<DefineConstants>$(DefineConstants);FEATURE_THREADINTERRUPT</DefineConstants>
7187
<DefineConstants>$(DefineConstants);FEATURE_BITARRAY_COPYTO</DefineConstants>
7288

73-
<DefineConstants>$(DefineConstants);FEATURE_LEGACY_NUMBER_FORMAT</DefineConstants>
89+
<!-- NOTE: We have refactored the RuleBasedNumberFormat into the FormatNumberRuleBased class, but
90+
it currently doesn't contain all of the features (parsing), nor do we have the DecimalFormat class functionality.
91+
To add these features to the build, just add /p:IncludeLegacyNumberFormat to the command line. -->
92+
<DefineConstants Condition=" '$(IncludeLegacyNumberFormat)' == 'true' ">$(DefineConstants);FEATURE_LEGACY_NUMBER_FORMAT</DefineConstants>
7493

7594
<!--
7695
<DefineConstants>$(DefineConstants);FEATURE_FIELDPOSITION</DefineConstants>
@@ -93,7 +112,7 @@
93112
<DefineConstants>$(DefineConstants);FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTCULTURE</DefineConstants>
94113
<DefineConstants>$(DefineConstants);FEATURE_EXCEPTION_HRESULT</DefineConstants>
95114
<DefineConstants>$(DefineConstants);FEATURE_MICROSOFT_EXTENSIONS_CACHING</DefineConstants>
96-
<DefineConstants>$(DefineConstants);FEATURE_READONLYDICTIONARY</DefineConstants>
115+
<DefineConstants>$(DefineConstants);FEATURE_IREADONLYCOLLECTIONS</DefineConstants>
97116
<DefineConstants>$(DefineConstants);FEATURE_REGEX_MATCHTIMEOUT</DefineConstants>
98117
<DefineConstants>$(DefineConstants);FEATURE_TASK_ASYNC_AWAIT</DefineConstants>
99118
<DefineConstants>$(DefineConstants);FEATURE_TASK_RUN</DefineConstants>

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Here are some of the major features that have been ported:
2424
7. [RuleBasedCollator](https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/index.html?com/ibm/icu/text/RuleBasedCollator.html)
2525
8. [Transliterator](https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/index.html?com/ibm/icu/text/Transliterator.html)
2626
9. [RuleBasedTransliterator](https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/index.html?com/ibm/icu/text/RuleBasedTransliterator.html)
27+
10. [RuleBasedNumberFormat](https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/index.html?com/ibm/icu/text/RuleBasedNumberFormat.html) - only formatting from number > string is supported by calling members of the `ICU4N.Text.FormatNumberRuleBased` class or by using them as extension methods from [System.Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte), [System.Int16](https://learn.microsoft.com/en-us/dotnet/api/system.int16), [System.Int32](https://learn.microsoft.com/en-us/dotnet/api/system.int32), [System.Int64](https://learn.microsoft.com/en-us/dotnet/api/system.int64), [System.Int128](https://learn.microsoft.com/en-us/dotnet/api/system.int128), [System.Numerics.BigInteger](https://learn.microsoft.com/en-us/dotnet/api/system.numerics.biginteger), [System.SByte](https://learn.microsoft.com/en-us/dotnet/api/system.sbyte), [System.IntPtr](https://learn.microsoft.com/en-us/dotnet/api/system.intptr), [System.UInt16](https://learn.microsoft.com/en-us/dotnet/api/system.uint16), [System.UInt32](https://learn.microsoft.com/en-us/dotnet/api/system.uint32), [System.UInt64](https://learn.microsoft.com/en-us/dotnet/api/system.uint64), [System.UInt128](https://learn.microsoft.com/en-us/dotnet/api/system.uint128), [System.Half](https://learn.microsoft.com/en-us/dotnet/api/system.half), [System.Single](https://learn.microsoft.com/en-us/dotnet/api/system.single), [System.Double](https://learn.microsoft.com/en-us/dotnet/api/system.Double), or [System.Decimal](https://learn.microsoft.com/en-us/dotnet/api/system.decimal).
2728

2829

2930
There are several other features too numerous to mention, but APIs are currently in flux between releases. We have over 4200 passing tests, most failures are just due to missing dependencies.
@@ -63,11 +64,11 @@ There are 2 ways to deploy resources with ICU4N.
6364
6465
### Default Satellite Assemblies
6566

66-
By default, ICU4N includes a transient dependency on [ICU4N.Resources](https://www.nuget.org/packages/ICU4N.Resources/), which includes satellite assemblies for all features and languages. For most projects, this should suffice.
67+
By default, ICU4N includes a transitive dependency on [ICU4N.Resources](https://www.nuget.org/packages/ICU4N.Resources/), which includes satellite assemblies for all features and languages. For most projects, this should suffice.
6768

6869
It is recommended to use the default set of data for class libraries that are deployed via NuGet to be shared, and to only consider using custom subsets of data for executable projects. This gives every consumer of a shared library a chance to customize the ICU resource data.
6970

70-
> **NOTE:** For SDK-Style projects that target `net40` or `net403`, the transient dependency is on [ICU4N.Resources.NETFramework4.0](https://www.nuget.org/packages/ICU4N.Resources.NETFramework4.0/). This package contains exactly the same files as [ICU4N.Resources](https://www.nuget.org/packages/ICU4N.Resources/) and only exists to work around the fact that NuGet doesn't support a single target framework to deploy satellite assemblies to targets below `net45` as well as targets that support `netstandard1.0` (which supports `net45` and higher).
71+
> **NOTE:** For SDK-Style projects that target `net40` or `net403`, the transitive dependency is on [ICU4N.Resources.NETFramework4.0](https://www.nuget.org/packages/ICU4N.Resources.NETFramework4.0/). This package contains exactly the same files as [ICU4N.Resources](https://www.nuget.org/packages/ICU4N.Resources/) and only exists to work around the fact that NuGet doesn't support a single target framework to deploy satellite assemblies to targets below `net45` as well as targets that support `netstandard1.0` (which supports `net45` and higher).
7172
7273
### Custom Satellite Assemblies
7374

@@ -84,7 +85,7 @@ The satellite assemblies are located in folders named like `<culture name>/ICU4N
8485
8586
> **IMPORTANT:** There is a common satellite assembly named `ICU4N.resources.dll` that sits in the assembly directory. This file must always be included for ICU4N to function when using satellite assemblies.
8687
87-
When including custom resource data with ICU4N, be sure to exclude the transitive dependencies from ICU4N as described in [Removing the default Transient Dependency on ICU4N.Resources](removing-the-default-transient-dependency-on-ICU4N.Resources).
88+
When including custom resource data with ICU4N, be sure to exclude the transitive dependencies from ICU4N as described in [Removing the default Transitive Dependency on ICU4N.Resources](removing-the-default-transitive-dependency-on-ICU4N.Resources).
8889

8990
### Custom Resource Files
9091

@@ -94,11 +95,11 @@ Reducing resource data is an advanced topic. See the [ICU Data](https://unicode-
9495

9596
Resources will be detected automatically if they are in the `/data/` directory. Note that including the versioned subdirectory (such as `icudt60b`) is required.
9697

97-
When including custom resource data with ICU4N, be sure to exclude the transitive dependencies from ICU4N as described in [Removing the default Transient Dependency on ICU4N.Resources](removing-the-default-transient-dependency-on-ICU4N.Resources).
98+
When including custom resource data with ICU4N, be sure to exclude the transitive dependencies from ICU4N as described in [Removing the default Transitive Dependency on ICU4N.Resources](removing-the-default-transitive-dependency-on-ICU4N.Resources).
9899

99-
### Removing the default Transient Dependency on ICU4N.Resources
100+
### Removing the default Transitive Dependency on ICU4N.Resources
100101

101-
To deploy custom resources via NuGet with your project, you must remove the transient dependency on the [ICU4N.Resources](https://www.nuget.org/packages/ICU4N.Resources/) package so the full set of resources isn't accidentally deployed to projects that consume yours. This can be done using the [ExcludeAssets](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets) flag called `buildTransitive`.
102+
To deploy custom resources via NuGet with your project, you must remove the transitive dependency on the [ICU4N.Resources](https://www.nuget.org/packages/ICU4N.Resources/) package so the full set of resources isn't accidentally deployed to projects that consume yours. This can be done using the [ExcludeAssets](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets) flag called `buildTransitive`.
102103

103104
```xml
104105
<ItemGroup>

src/CodeGenerationSettings.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,27 @@
1414
</appendables>
1515
<!-- Accessibility of IAppendable interface -->
1616
<appendableAccessibility>public</appendableAccessibility>
17+
<numericTypes>
18+
<!-- integral types -->
19+
<numericType clsCompliant="true" cSharpName="byte">Byte</numericType>
20+
<numericType clsCompliant="true" cSharpName="short">Int16</numericType>
21+
<numericType clsCompliant="true" cSharpName="int">Int32</numericType>
22+
<numericType clsCompliant="true" cSharpName="long">Int64</numericType>
23+
<numericType clsCompliant="true" cSharpName="Int128" feature="FEATURE_INT128">Int128</numericType>
24+
<numericType clsCompliant="true" cSharpName="BigInteger">BigInteger</numericType>
25+
<numericType clsCompliant="true" cSharpName="nint">IntPtr</numericType>
26+
27+
<numericType clsCompliant="false" cSharpName="sbyte">SByte</numericType>
28+
<numericType clsCompliant="false" cSharpName="ushort">UInt16</numericType>
29+
<numericType clsCompliant="false" cSharpName="uint">UInt32</numericType>
30+
<numericType clsCompliant="false" cSharpName="ulong">UInt64</numericType>
31+
<numericType clsCompliant="false" cSharpName="UInt128" feature="FEATURE_INT128">UInt128</numericType>
32+
<numericType clsCompliant="false" cSharpName="nuint">UIntPtr</numericType>
33+
34+
<!-- floating point types -->
35+
<numericType clsCompliant="true" cSharpName="Half" feature="FEATURE_HALF">Half</numericType>
36+
<numericType clsCompliant="true" cSharpName="float">Single</numericType>
37+
<numericType clsCompliant="true" cSharpName="double">Double</numericType>
38+
<numericType clsCompliant="true" cSharpName="decimal">Decimal</numericType>
39+
</numericTypes>
1740
</codeGen>

src/ICU4N.Collation/Text/AlphabeticIndex.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,11 @@ internal BucketList(IList<Bucket<T>> bucketList, IList<Bucket<T>> publicBucketLi
10841084
{
10851085
bucket.DisplayIndex = displayIndex++;
10861086
}
1087+
#if FEATURE_ILIST_ASREADONLY
1088+
immutableVisibleList = System.Collections.Generic.CollectionExtensions.AsReadOnly(publicBucketList);
1089+
#else
10871090
immutableVisibleList = publicBucketList.AsReadOnly();
1091+
#endif
10881092
}
10891093

10901094
internal int BucketCount => immutableVisibleList.Count;

src/ICU4N.CurrencyData/Impl/ICUCurrencyMetaInfo.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,11 @@ internal void Add(T value)
222222

223223
internal IList<T> ToList()
224224
{
225+
#if FEATURE_ILIST_ASREADONLY
226+
return System.Collections.Generic.CollectionExtensions.AsReadOnly(list);
227+
#else
225228
return list.AsReadOnly();
229+
#endif
226230
}
227231
}
228232

@@ -241,7 +245,11 @@ public void Collect(string region, string currency, long from, long to, int prio
241245

242246
public IList<CurrencyInfo> ToList()
243247
{
248+
#if FEATURE_ILIST_ASREADONLY
249+
return System.Collections.Generic.CollectionExtensions.AsReadOnly(result);
250+
#else
244251
return result.AsReadOnly();
252+
#endif
245253
}
246254

247255
public int Collects => Everything;

src/ICU4N/ICU4N.csproj

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net6.0;netstandard2.0;net451;net40</TargetFrameworks>
4+
<TargetFrameworks>net7.0;net6.0;netstandard2.0;net451;net40</TargetFrameworks>
55
<DefineConstants Condition=" '$(TargetFramework)' == 'netstandard1.3' ">$(DefineConstants);LIBLOG_PORTABLE</DefineConstants>
66

77
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@@ -536,6 +536,11 @@
536536
<AutoGen>True</AutoGen>
537537
<DependentUpon>UTS46Extension.tt</DependentUpon>
538538
</Compile>
539+
<Compile Update="Support\FormatNumberRuleBasedExtension.cs">
540+
<DesignTime>True</DesignTime>
541+
<AutoGen>True</AutoGen>
542+
<DependentUpon>FormatNumberRuleBasedExtension.tt</DependentUpon>
543+
</Compile>
539544
<Compile Update="Support\Numerics\BigMath\Messages.Designer.cs">
540545
<DesignTime>True</DesignTime>
541546
<AutoGen>True</AutoGen>
@@ -701,6 +706,10 @@
701706
<Generator>TextTemplatingFileGenerator</Generator>
702707
<LastGenOutput>UCharacterExtension.cs</LastGenOutput>
703708
</None>
709+
<None Update="Support\FormatNumberRuleBasedExtension.tt">
710+
<Generator>TextTemplatingFileGenerator</Generator>
711+
<LastGenOutput>FormatNumberRuleBasedExtension.cs</LastGenOutput>
712+
</None>
704713
<None Update="Support\Text\UnicodeSetExtensionsExtension.tt">
705714
<Generator>TextTemplatingFileGenerator</Generator>
706715
<LastGenOutput>UnicodeSetExtensionsExtension.cs</LastGenOutput>

src/ICU4N/Impl/CurrencyData.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,19 @@ public override string GetNarrowSymbol(string isoCode)
144144
return fallback ? isoCode : null;
145145
}
146146

147-
public override IDictionary<string, string> SymbolMap => new Dictionary<string, string>().AsReadOnly();
148-
149-
public override IDictionary<string, string> NameMap => new Dictionary<string, string>().AsReadOnly();
147+
public override IDictionary<string, string> SymbolMap
148+
#if FEATURE_IDICTIONARY_ASREADONLY
149+
=> System.Collections.Generic.CollectionExtensions.AsReadOnly(new Dictionary<string, string>());
150+
#else
151+
=> new Dictionary<string, string>().AsReadOnly();
152+
#endif
153+
154+
public override IDictionary<string, string> NameMap
155+
#if FEATURE_IDICTIONARY_ASREADONLY
156+
=> System.Collections.Generic.CollectionExtensions.AsReadOnly(new Dictionary<string, string>());
157+
#else
158+
=> new Dictionary<string, string>().AsReadOnly();
159+
#endif
150160

151161
public override UCultureInfo UCulture
152162
=> UCultureInfo.InvariantCulture;
@@ -155,7 +165,11 @@ public override IDictionary<string, string> GetUnitPatterns()
155165
{
156166
if (fallback)
157167
{
168+
#if FEATURE_IDICTIONARY_ASREADONLY
169+
return System.Collections.Generic.CollectionExtensions.AsReadOnly(new Dictionary<string, string>());
170+
#else
158171
return new Dictionary<string, string>().AsReadOnly();
172+
#endif
159173
}
160174
return null;
161175
}

src/ICU4N/Impl/ICULocaleService.cs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public ICULocaleService(string name)
3333
/// Convenience override for callers using locales. This calls
3434
/// <see cref="Get(UCultureInfo, int, out UCultureInfo)"/> with <see cref="LocaleKey.KindAny"/> for kind.
3535
/// </summary>
36-
public virtual object Get(UCultureInfo locale) // ICU4N TODO: API - Use indexer?
36+
public virtual object Get(UCultureInfo locale)
3737
{
3838
return Get(locale, LocaleKey.KindAny);
3939
}
@@ -91,6 +91,36 @@ public virtual object Get(UCultureInfo locale, int kind, out UCultureInfo actual
9191
return result;
9292
}
9393

94+
/// <summary>
95+
/// Convenience override for callers using locales. This uses
96+
/// <see cref="CreateKey(string, int)"/> to create a key, calls
97+
/// <see cref="ICUService.GetKey(ICUServiceKey)"/>, and then
98+
/// returns the <paramref name="actualResult"/> from
99+
/// <see cref="ICUService.GetKey(ICUServiceKey)"/> (stripping any prefix)
100+
/// into a <see cref="UCultureInfo"/>.
101+
/// </summary>
102+
/// <param name="localeName">The localeID or full name of the locale.</param>
103+
/// <param name="kind"></param>
104+
/// <param name="actualResult"></param>
105+
/// <returns></returns>
106+
public virtual object Get(string localeName, int kind, out UCultureInfo actualResult) // ICU4N: Added to decouple services from UCultureInfo
107+
{
108+
actualResult = null;
109+
ICUServiceKey key = CreateKey(localeName, kind);
110+
111+
object result = GetKey(key, out string temp);
112+
if (result != null)
113+
{
114+
int n = temp.IndexOf('/');
115+
if (n >= 0)
116+
{
117+
temp = temp.Substring(n + 1);
118+
}
119+
actualResult = new UCultureInfo(temp);
120+
}
121+
return result;
122+
}
123+
94124
/// <summary>
95125
/// Convenience override for callers using locales. This calls
96126
/// <see cref="RegisterObject(object, UCultureInfo, int, bool)"/>
@@ -277,11 +307,21 @@ public static LocaleKey CreateWithCanonicalFallback(string primaryID, string can
277307
/// </summary>
278308
public static LocaleKey CreateWithCanonical(UCultureInfo locale, string canonicalFallbackID, int kind)
279309
{
280-
if (locale == null)
310+
return CreateWithCanonical(locale.FullName, canonicalFallbackID, kind);
311+
}
312+
313+
/// <summary>
314+
/// Create a <see cref="LocaleKey"/> with canonical primary and fallback IDs.
315+
/// <para/>
316+
/// Note that <paramref name="localeFullName"/> is the same as locale.Name in ICU4J.
317+
/// </summary>
318+
public static LocaleKey CreateWithCanonical(string localeFullName, string canonicalFallbackID, int kind) // ICU4N: Added overload to decouple from UCultureInfo
319+
{
320+
if (localeFullName is null)
281321
{
282322
return null;
283323
}
284-
string canonicalPrimaryID = locale.FullName;
324+
string canonicalPrimaryID = localeFullName;
285325
return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID, kind);
286326
}
287327

0 commit comments

Comments
 (0)