chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace QuantConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for properly parsing and serializing values while properly using
|
||||
/// an IFormatProvider/CultureInfo when applicable
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
private static readonly CultureInfo CultureInfo = CultureInfo.InvariantCulture;
|
||||
private static readonly IFormatProvider FormatProvider = CultureInfo;
|
||||
private static readonly StringComparison StringComparison = StringComparison.InvariantCulture;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="value"/> as <typeparamref name="T"/>
|
||||
/// using <see cref="CultureInfo"/>
|
||||
/// </summary>
|
||||
public static T ConvertInvariant<T>(this object value)
|
||||
{
|
||||
return (T) value.ConvertInvariant(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="value"/> as <paramref name="conversionType"/>
|
||||
/// using <see cref="CultureInfo"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This implementation uses the Convert.ToXXX methods. This causes null values to be converted to the default value
|
||||
/// for the provided <paramref name="conversionType"/>. This is in contrast to directly calling <see cref="IConvertible.ToType"/>
|
||||
/// which results in an <see cref="InvalidCastException"/> or a <see cref="FormatException"/>. Since existing code is
|
||||
/// dependent on this null -> default value conversion behavior, it has been preserved in this method.
|
||||
/// </remarks>
|
||||
public static object ConvertInvariant(this object value, Type conversionType)
|
||||
{
|
||||
switch (Type.GetTypeCode(conversionType))
|
||||
{
|
||||
// these cases are purposefully ordered to ensure the compiler can generate a jump table vs a binary tree
|
||||
case TypeCode.Empty:
|
||||
throw new ArgumentException(Messages.StringExtensions.ConvertInvariantCannotConvertTo(TypeCode.Empty));
|
||||
|
||||
case TypeCode.Object:
|
||||
var convertible = value as IConvertible;
|
||||
if (convertible != null)
|
||||
{
|
||||
return convertible.ToType(conversionType, FormatProvider);
|
||||
}
|
||||
|
||||
return Convert.ChangeType(value, conversionType, FormatProvider);
|
||||
|
||||
case TypeCode.DBNull:
|
||||
throw new ArgumentException(Messages.StringExtensions.ConvertInvariantCannotConvertTo(TypeCode.DBNull));
|
||||
|
||||
case TypeCode.Boolean:
|
||||
return Convert.ToBoolean(value, FormatProvider);
|
||||
|
||||
case TypeCode.Char:
|
||||
return Convert.ToChar(value, FormatProvider);
|
||||
|
||||
case TypeCode.SByte:
|
||||
return Convert.ToSByte(value, FormatProvider);
|
||||
|
||||
case TypeCode.Byte:
|
||||
return Convert.ToByte(value, FormatProvider);
|
||||
|
||||
case TypeCode.Int16:
|
||||
return Convert.ToInt16(value, FormatProvider);
|
||||
|
||||
case TypeCode.UInt16:
|
||||
return Convert.ToUInt16(value, FormatProvider);
|
||||
|
||||
case TypeCode.Int32:
|
||||
return Convert.ToInt32(value, FormatProvider);
|
||||
|
||||
case TypeCode.UInt32:
|
||||
return Convert.ToUInt32(value, FormatProvider);
|
||||
|
||||
case TypeCode.Int64:
|
||||
return Convert.ToInt64(value, FormatProvider);
|
||||
|
||||
case TypeCode.UInt64:
|
||||
return Convert.ToUInt64(value, FormatProvider);
|
||||
|
||||
case TypeCode.Single:
|
||||
return Convert.ToSingle(value, FormatProvider);
|
||||
|
||||
case TypeCode.Double:
|
||||
return Convert.ToDouble(value, FormatProvider);
|
||||
|
||||
case TypeCode.Decimal:
|
||||
return Convert.ToDecimal(value, FormatProvider);
|
||||
|
||||
case TypeCode.DateTime:
|
||||
return Convert.ToDateTime(value, FormatProvider);
|
||||
|
||||
case TypeCode.String:
|
||||
return Convert.ToString(value, FormatProvider);
|
||||
|
||||
default:
|
||||
return Convert.ChangeType(value, conversionType, FormatProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-extension method alias for <see cref="FormattableString.Invariant"/>
|
||||
/// This supports the <code>using static QuantConnect.StringExtensions</code> syntax
|
||||
/// and is aimed at ensuring all formatting is piped through this class instead of
|
||||
/// alternatively piping through directly to <see cref="FormattableString.Invariant"/>
|
||||
/// </summary>
|
||||
public static string Invariant(FormattableString formattable)
|
||||
{
|
||||
return FormattableString.Invariant(formattable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided value to a string using <see cref="CultureInfo"/>
|
||||
/// </summary>
|
||||
public static string ToStringInvariant(this IConvertible convertible)
|
||||
{
|
||||
if (convertible == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return convertible.ToString(FormatProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the provided value using the specified <paramref name="format"/> and
|
||||
/// <see cref="CultureInfo"/>
|
||||
/// </summary>
|
||||
public static string ToStringInvariant(this IFormattable formattable, string format)
|
||||
{
|
||||
if (formattable == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// if we have a colon, this implies there's a width parameter in the format it seems this isn't handled
|
||||
// as one would expect. For example, specifying something like $"{value,10:0.00}" would force the string
|
||||
// to be at least 10 characters wide with extra padding in the front, but passing the string '10:0.00' or
|
||||
// ',10:0.00' doesn't work. If we are able to detect a colon in the format and the values preceding the colon,
|
||||
// are numeric, then we know it starts with a width parameter and we can pipe it into a custom-formed
|
||||
// string.format call to get the correct output
|
||||
if (format != null)
|
||||
{
|
||||
var indexOfColon = format.IndexOfInvariant(":");
|
||||
if (indexOfColon != -1)
|
||||
{
|
||||
int padding;
|
||||
var beforeColon = format.Substring(0, indexOfColon);
|
||||
if (int.TryParse(beforeColon, out padding))
|
||||
{
|
||||
return string.Format(FormatProvider, $"{{0,{format}}}", formattable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return formattable.ToString(format, FormatProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a convenience methods for converting a <see cref="DateTime"/> to an invariant ISO-8601 string
|
||||
/// </summary>
|
||||
public static string ToIso8601Invariant(this DateTime dateTime)
|
||||
{
|
||||
return dateTime.ToStringInvariant("O");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the string starts with the provided <paramref name="beginning"/> using <see cref="CultureInfo"/>
|
||||
/// while optionally ignoring case.
|
||||
/// </summary>
|
||||
public static bool StartsWithInvariant(this string value, string beginning, bool ignoreCase = false)
|
||||
{
|
||||
return value.StartsWith(beginning, ignoreCase, CultureInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the string ends with the provided <paramref name="ending"/> using <see cref="CultureInfo"/>
|
||||
/// while optionally ignoring case.
|
||||
/// </summary>
|
||||
public static bool EndsWithInvariant(this string value, string ending, bool ignoreCase = false)
|
||||
{
|
||||
return value.EndsWith(ending, ignoreCase, CultureInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified <paramref name="character"/> using <see cref="StringComparison"/>
|
||||
/// </summary>
|
||||
public static int IndexOfInvariant(this string value, char character)
|
||||
{
|
||||
return value.IndexOf(character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified <paramref name="substring"/> using <see cref="StringComparison"/>
|
||||
/// or <see cref="System.StringComparison.InvariantCultureIgnoreCase"/> when <paramref name="ignoreCase"/> is true
|
||||
/// </summary>
|
||||
public static int IndexOfInvariant(this string value, string substring, bool ignoreCase = false)
|
||||
{
|
||||
return value.IndexOf(substring, ignoreCase
|
||||
? StringComparison.InvariantCultureIgnoreCase
|
||||
: StringComparison
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified <paramref name="substring"/> using <see cref="StringComparison"/>
|
||||
/// or <see cref="System.StringComparison.InvariantCultureIgnoreCase"/> when <paramref name="ignoreCase"/> is true
|
||||
/// </summary>
|
||||
public static int LastIndexOfInvariant(this string value, string substring, bool ignoreCase = false)
|
||||
{
|
||||
return value.LastIndexOf(substring, ignoreCase
|
||||
? StringComparison.InvariantCultureIgnoreCase
|
||||
: StringComparison
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a shorthand for avoiding the more verbose ternary equivalent.
|
||||
/// Consider the following:
|
||||
/// <code>
|
||||
/// string.IsNullOrEmpty(str) ? (decimal?)null : Convert.ToDecimal(str, CultureInfo.InvariantCulture)
|
||||
/// </code>
|
||||
/// Can be expressed as:
|
||||
/// <code>
|
||||
/// str.IfNotNullOrEmpty<decimal?>(s => Convert.ToDecimal(str, CultureInfo.InvariantCulture))
|
||||
/// </code>
|
||||
/// When combined with additional methods from this class, reducing further to a declarative:
|
||||
/// <code>
|
||||
/// str.IfNotNullOrEmpty<decimal?>(s => s.ParseDecimalInvariant())
|
||||
/// str.IfNotNullOrEmpty<decimal?>(s => s.ConvertInvariant<decimal>())
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <paramref name="value">The string value to check for null or empty</paramref>
|
||||
/// <paramref name="defaultValue">The default value to use if null or empty</paramref>
|
||||
/// <paramref name="func">Function run on non-null string w/ length > 0</paramref>
|
||||
public static T IfNotNullOrEmpty<T>(this string value, T defaultValue, Func<string, T> func)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return func(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a shorthand for avoiding the more verbose ternary equivalent.
|
||||
/// Consider the following:
|
||||
/// <code>
|
||||
/// string.IsNullOrEmpty(str) ? (decimal?)null : Convert.ToDecimal(str, CultureInfo.InvariantCulture)
|
||||
/// </code>
|
||||
/// Can be expressed as:
|
||||
/// <code>
|
||||
/// str.IfNotNullOrEmpty<decimal?>(s => Convert.ToDecimal(str, CultureInfo.InvariantCulture))
|
||||
/// </code>
|
||||
/// When combined with additional methods from this class, reducing further to a declarative:
|
||||
/// <code>
|
||||
/// str.IfNotNullOrEmpty<decimal?>(s => s.ParseDecimalInvariant())
|
||||
/// str.IfNotNullOrEmpty<decimal?>(s => s.ConvertInvariant<decimal>())
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <paramref name="value">The string value to check for null or empty</paramref>
|
||||
/// <paramref name="func">Function run on non-null string w/ length > 0</paramref>
|
||||
public static T IfNotNullOrEmpty<T>(this string value, Func<string, T> func)
|
||||
{
|
||||
return value.IfNotNullOrEmpty(default(T), func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a substring from this instance. The substring starts at a specified
|
||||
/// character position and has a specified length.
|
||||
/// </summary>
|
||||
/// <paramref name="startIndex">The zero-based starting character position of a substring in this instance</paramref>
|
||||
/// <paramref name="length">The number of characters in the substring</paramref>
|
||||
public static string SafeSubstring(this string value, int startIndex, int length)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (startIndex > value.Length - 1)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (startIndex < - 1)
|
||||
{
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
return value.Substring(startIndex, Math.Min(length, value.Length - startIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates a string to the specified maximum length
|
||||
/// </summary>
|
||||
/// <param name="value">The string</param>
|
||||
/// <param name="maxLength">The maximum allowed string</param>
|
||||
/// <returns>
|
||||
/// A new string with <paramref name="maxLength"/> characters if the original one's length was greater than the maximum allowed length.
|
||||
/// Otherwise, the original string is returned.
|
||||
/// </returns>
|
||||
public static string Truncate(this string value, int maxLength)
|
||||
{
|
||||
if (value.Length > maxLength)
|
||||
{
|
||||
return value.Substring(0, maxLength);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user