2020-08-17 10:00:56 -07:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
using System.Globalization ;
using System.Text ;
using System.Text.RegularExpressions ;
2021-01-20 11:38:52 +01:00
namespace Microsoft.PowerToys.Run.Plugin.Calculator
2020-08-17 10:00:56 -07:00
{
/// <summary>
/// Tries to convert all numbers in a text from one culture format to another.
/// </summary>
public class NumberTranslator
{
private readonly CultureInfo sourceCulture ;
private readonly CultureInfo targetCulture ;
private readonly Regex splitRegexForSource ;
private readonly Regex splitRegexForTarget ;
private NumberTranslator ( CultureInfo sourceCulture , CultureInfo targetCulture )
{
this . sourceCulture = sourceCulture ;
this . targetCulture = targetCulture ;
splitRegexForSource = GetSplitRegex ( this . sourceCulture ) ;
splitRegexForTarget = GetSplitRegex ( this . targetCulture ) ;
}
/// <summary>
2021-01-25 10:51:32 +01:00
/// Create a new <see cref="NumberTranslator"/>.
2020-08-17 10:00:56 -07:00
/// </summary>
/// <param name="sourceCulture">source culture</param>
/// <param name="targetCulture">target culture</param>
/// <returns>Number translator for target culture</returns>
public static NumberTranslator Create ( CultureInfo sourceCulture , CultureInfo targetCulture )
{
2023-11-22 12:46:59 -05:00
ArgumentNullException . ThrowIfNull ( sourceCulture ) ;
ArgumentNullException . ThrowIfNull ( targetCulture ) ;
2020-08-17 10:00:56 -07:00
2021-01-25 10:51:32 +01:00
return new NumberTranslator ( sourceCulture , targetCulture ) ;
2020-08-17 10:00:56 -07:00
}
/// <summary>
/// Translate from source to target culture.
/// </summary>
/// <param name="input">input string to translate</param>
/// <returns>translated string</returns>
public string Translate ( string input )
{
return Translate ( input , sourceCulture , targetCulture , splitRegexForSource ) ;
}
/// <summary>
/// Translate from target to source culture.
/// </summary>
/// <param name="input">input string to translate back to source culture</param>
/// <returns>source culture string</returns>
public string TranslateBack ( string input )
{
return Translate ( input , targetCulture , sourceCulture , splitRegexForTarget ) ;
}
private static string Translate ( string input , CultureInfo cultureFrom , CultureInfo cultureTo , Regex splitRegex )
{
var outputBuilder = new StringBuilder ( ) ;
2024-01-03 06:22:09 -05:00
var hexRegex = new Regex ( @"(?:(0x[\da-fA-F]+))" ) ;
2020-08-17 10:00:56 -07:00
2024-01-03 06:22:09 -05:00
string [ ] hexTokens = hexRegex . Split ( input ) ;
foreach ( string hexToken in hexTokens )
2020-08-17 10:00:56 -07:00
{
2024-01-03 06:22:09 -05:00
if ( hexToken . StartsWith ( "0x" , StringComparison . InvariantCultureIgnoreCase ) )
{
2024-01-26 15:09:38 +01:00
// Mages engine has issues processing large hex number (larger than 7 hex digits + 0x prefix = 9 characters). So we convert it to decimal and pass it to the engine.
if ( hexToken . Length > 9 )
{
try
{
long num = Convert . ToInt64 ( hexToken , 16 ) ;
string numStr = num . ToString ( cultureFrom ) ;
outputBuilder . Append ( numStr ) ;
}
catch ( Exception )
{
outputBuilder . Append ( hexToken ) ;
}
}
else
{
outputBuilder . Append ( hexToken ) ;
}
2024-01-03 06:22:09 -05:00
continue ;
}
2023-09-12 11:22:34 +01:00
2024-01-03 06:22:09 -05:00
string [ ] tokens = splitRegex . Split ( hexToken ) ;
foreach ( string token in tokens )
2023-09-12 11:22:34 +01:00
{
2024-01-03 06:22:09 -05:00
int leadingZeroCount = 0 ;
// Count leading zero characters.
foreach ( char c in token )
2023-09-12 11:22:34 +01:00
{
2024-01-03 06:22:09 -05:00
if ( c ! = '0' )
{
break ;
}
2023-09-12 11:22:34 +01:00
2024-01-03 06:22:09 -05:00
leadingZeroCount + + ;
}
2023-09-12 11:22:34 +01:00
2024-01-03 06:22:09 -05:00
// number is all zero characters. no need to add zero characters at the end.
if ( token . Length = = leadingZeroCount )
{
leadingZeroCount = 0 ;
}
2023-09-12 11:22:34 +01:00
2024-01-03 06:22:09 -05:00
decimal number ;
2023-09-12 11:22:34 +01:00
2024-01-03 06:22:09 -05:00
outputBuilder . Append (
decimal . TryParse ( token , NumberStyles . Number , cultureFrom , out number )
? ( new string ( '0' , leadingZeroCount ) + number . ToString ( cultureTo ) )
: token ) ;
}
2020-08-17 10:00:56 -07:00
}
return outputBuilder . ToString ( ) ;
}
private static Regex GetSplitRegex ( CultureInfo culture )
{
2024-01-03 06:22:09 -05:00
var splitPattern = $"((?:\\d|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}" ;
2020-08-17 10:00:56 -07:00
if ( ! string . IsNullOrEmpty ( culture . NumberFormat . NumberGroupSeparator ) )
{
splitPattern + = $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}" ;
}
splitPattern + = ")+)" ;
return new Regex ( splitPattern ) ;
}
}
}