Imports System
Imports System.Globalization
+Imports System.Runtime.Versioning
Imports System.Text
Imports Microsoft.VisualBasic.CompilerServices
Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils
Namespace Global.Microsoft.VisualBasic
- Public Module Strings
+ Friend NotInheritable Class FormatInfoHolder
+ Implements IFormatProvider
+
+ Friend Sub New(ByVal nfi As NumberFormatInfo)
+ MyBase.New()
+ Me.nfi = nfi
+ End Sub
+
+ Private nfi As NumberFormatInfo
+
+ Private Function GetFormat(ByVal service As Type) As Object Implements IFormatProvider.GetFormat
+ If service Is GetType(NumberFormatInfo) Then
+ Return nfi
+ End If
+ Throw New ArgumentException(GetResourceString(SR.InternalError_VisualBasicRuntime))
+ End Function
+ End Class
+
+ Public Module Strings
+ 'Positive format strings
+ '0 $n
+ '1 n$
+ '2 $ n
+ '3 n $
+ Private ReadOnly CurrencyPositiveFormatStrings() As String = {"'$'n", "n'$'", "'$' n", "n '$'"} 'Note, we wrap the $ in the literal symbol to avoid misinterpretation when using the escape character \ as a currency mark
+
+ 'The negative currency pattern needs to be selected based
+ ' on the criteria provided for parens
+ 'nfi.CurrencyPositivePattern
+ 'Negative format strings
+ '0 ($n)
+ '1 -$n
+ '2 $-n
+ '3 $n-
+ '4 (n$)
+ '5 -n$
+ '6 n-$
+ '7 n$-
+ '8 -n $
+ '9 -$ n
+ '10 n $-
+ '11 $ n-
+ '12 $- n
+ '13 n- $
+ '14 ($ n)
+ '15 (n $)
+ Private ReadOnly CurrencyNegativeFormatStrings() As String =
+ {"('$'n)", "-'$'n", "'$'-n", "'$'n-", "(n'$')", "-n'$'", "n-'$'", "n'$'-",
+ "-n '$'", "-'$' n", "n '$'-", "'$' n-", "'$'- n", "n- '$'", "('$' n)", "(n '$')"} 'Note, we wrap the $ in the literal symbol to avoid misinterpretation when using the escape character \ as a currency mark
+
+ 'Value Associated Pattern
+ '0 (n)
+ '1 -n
+ '2 - n
+ '3 n-
+ '4 n -
+ Private ReadOnly NumberNegativeFormatStrings() As String =
+ {"(n)", "-n", "- n", "n-", "n -"}
+
+ Friend Enum FormatType
+ Number = 0
+ Percent = 1
+ [Currency] = 2
+ End Enum
+
+ Private Const CODEPAGE_SIMPLIFIED_CHINESE As Integer = 936
+ Private Const CODEPAGE_TRADITIONAL_CHINESE As Integer = 950
Private Const STANDARD_COMPARE_FLAGS As CompareOptions =
CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreKanaType
+ Private Const NAMEDFORMAT_FIXED As String = "fixed"
+ Private Const NAMEDFORMAT_YES_NO As String = "yes/no"
+ Private Const NAMEDFORMAT_ON_OFF As String = "on/off"
+ Private Const NAMEDFORMAT_PERCENT As String = "percent"
+ Private Const NAMEDFORMAT_STANDARD As String = "standard"
+ Private Const NAMEDFORMAT_CURRENCY As String = "currency"
+ Private Const NAMEDFORMAT_LONG_TIME As String = "long time"
+ Private Const NAMEDFORMAT_LONG_DATE As String = "long date"
+ Private Const NAMEDFORMAT_SCIENTIFIC As String = "scientific"
+ Private Const NAMEDFORMAT_TRUE_FALSE As String = "true/false"
+ Private Const NAMEDFORMAT_SHORT_TIME As String = "short time"
+ Private Const NAMEDFORMAT_SHORT_DATE As String = "short date"
+ Private Const NAMEDFORMAT_MEDIUM_DATE As String = "medium date"
+ Private Const NAMEDFORMAT_MEDIUM_TIME As String = "medium time"
+ Private Const NAMEDFORMAT_GENERAL_DATE As String = "general date"
+ Private Const NAMEDFORMAT_GENERAL_NUMBER As String = "general number"
+
Friend ReadOnly m_InvariantCompareInfo As CompareInfo = CultureInfo.InvariantCulture.CompareInfo
+ 'This is shared across Cached
+ Private m_SyncObject As Object = New Object
+ Private m_LastUsedYesNoCulture As CultureInfo
+ Private m_CachedYesNoFormatStyle As String
+
+ Private ReadOnly Property CachedYesNoFormatStyle() As String
+ Get
+ Dim ci As CultureInfo = GetCultureInfo()
+ SyncLock m_SyncObject
+ If Not m_LastUsedYesNoCulture Is ci Then
+ m_LastUsedYesNoCulture = ci
+ m_CachedYesNoFormatStyle = GetResourceString(SR.YesNoFormatStyle)
+ End If
+ Return m_CachedYesNoFormatStyle
+ End SyncLock
+ End Get
+ End Property
+
+ Private m_LastUsedOnOffCulture As CultureInfo
+ Private m_CachedOnOffFormatStyle As String
+ Private ReadOnly Property CachedOnOffFormatStyle() As String
+ Get
+ Dim ci As CultureInfo = GetCultureInfo()
+ SyncLock m_SyncObject
+ If Not m_LastUsedOnOffCulture Is ci Then
+ m_LastUsedOnOffCulture = ci
+ m_CachedOnOffFormatStyle = GetResourceString(SR.OnOffFormatStyle)
+ End If
+ Return m_CachedOnOffFormatStyle
+ End SyncLock
+ End Get
+ End Property
+
+ Private m_LastUsedTrueFalseCulture As CultureInfo
+ Private m_CachedTrueFalseFormatStyle As String
+ Private ReadOnly Property CachedTrueFalseFormatStyle() As String
+ Get
+ Dim ci As CultureInfo = GetCultureInfo()
+ SyncLock m_SyncObject
+ If Not m_LastUsedTrueFalseCulture Is ci Then
+ m_LastUsedTrueFalseCulture = ci
+ m_CachedTrueFalseFormatStyle = GetResourceString(SR.TrueFalseFormatStyle)
+ End If
+ Return m_CachedTrueFalseFormatStyle
+ End SyncLock
+ End Get
+ End Property
+
+ Private Function PRIMARYLANGID(ByVal lcid As Integer) As Integer
+ Return (lcid And &H3FF)
+ End Function
+
'============================================================================
' Character manipulation functions.
'============================================================================
End Try
End Function
+ Public Function Join(ByVal SourceArray() As Object, Optional ByVal Delimiter As String = " ") As String
+ Dim Size As Integer = UBound(SourceArray)
+ Dim StringSource(Size) As String
+ Dim i As Integer
+
+ Try
+ For i = 0 To Size
+ StringSource(i) = CStr(SourceArray(i))
+ Next i
+ Catch ex As StackOverflowException
+ Throw ex
+ Catch ex As OutOfMemoryException
+ Throw ex
+ Catch ex As System.Threading.ThreadAbortException
+ Throw ex
+ Catch
+ Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValueType2, "SourceArray", "String"))
+ End Try
+
+ Return Join(StringSource, Delimiter)
+ End Function
+
+ Public Function Join(ByVal SourceArray() As String, Optional ByVal Delimiter As String = " ") As String
+ Try
+ If IsArrayEmpty(SourceArray) Then
+ 'EmptyArray returns empty string
+ Return Nothing
+ End If
+
+ If SourceArray.Rank <> 1 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_RankEQOne1))
+ End If
+
+ Return System.String.Join(Delimiter, SourceArray)
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Public Function LCase(ByVal Value As String) As String
+ Try
+ If Value Is Nothing Then
+ Return Nothing
+ Else
+ Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToLower(Value)
+ End If
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Public Function LCase(ByVal Value As Char) As Char
+ Try
+ Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToLower(Value)
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
Public Function Len(ByVal Expression As Boolean) As Integer
Return 2
End Function
Throw VbMakeException(vbErrors.TypeMismatch)
End Function
+ Public Function Replace(ByVal Expression As String, ByVal Find As String, ByVal Replacement As String, Optional ByVal Start As Integer = 1, Optional ByVal Count As Integer = -1, <Microsoft.VisualBasic.CompilerServices.OptionCompareAttribute()> Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As String
+ Try
+ 'Validate Parameters
+ If Count < -1 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_GEMinusOne1, "Count"))
+ End If
+
+ If Start <= 0 Then
+ Throw New ArgumentException(GetResourceString("Argument_GTZero1", "Start"))
+ End If
+
+ If (Expression Is Nothing) OrElse (Start > Expression.Length) Then
+ Return Nothing
+ End If
+
+ If Start <> 1 Then
+ Expression = Expression.Substring(Start - 1)
+ End If
+
+ If Find Is Nothing Then
+ GoTo EmptyFindString
+ End If
+
+ If Find.Length = 0 OrElse Count = 0 Then
+EmptyFindString:
+ Return Expression
+ End If
+
+ If Count = -1 Then
+ Count = Expression.Length
+ End If
+
+ Return ReplaceInternal(Expression, Find, Replacement, Count, [Compare])
+
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Private Function ReplaceInternal(ByVal Expression As String, ByVal Find As String, ByVal Replacement As String, ByVal Count As Integer, ByVal [Compare] As CompareMethod) As String
+
+ System.Diagnostics.Debug.Assert(Expression <> "", "Expression is empty")
+ System.Diagnostics.Debug.Assert(Find <> "", "Find is empty")
+ System.Diagnostics.Debug.Assert(Count > 0, "Number of replacements is 0 or less")
+ System.Diagnostics.Debug.Assert([Compare] = CompareMethod.Text Or [Compare] = CompareMethod.Binary, "Unknown compare.")
+
+ Dim ExpressionLength As Integer = Expression.Length
+ Dim FindLength As Integer = Find.Length
+
+ Dim Start As Integer
+ Dim FindLocation As Integer
+ Dim Replacements As Integer
+
+ Dim Comparer As CompareInfo
+ Dim CompareFlags As CompareOptions
+
+ Dim Builder As StringBuilder = New StringBuilder(ExpressionLength)
+
+ If [Compare] = CompareMethod.Text Then
+ Comparer = GetCultureInfo().CompareInfo
+ CompareFlags = STANDARD_COMPARE_FLAGS
+ Else
+ Comparer = m_InvariantCompareInfo
+ CompareFlags = CompareOptions.Ordinal
+ End If
+
+ 'We build the new string (with the replacements) by walking through Expression, searching for the
+ 'Find, and appending sections of Expression or Replacement as we go. For example, if
+ 'Expression = "This is a test.", Find = "is" and Replacement = "YYY" then we would append
+ '"Th" to the new string, then "YYY" then " " then "YYY" and finally " a test."
+ While Start < ExpressionLength
+ If Replacements = Count Then
+ 'We've made all the replacements the caller wanted so append the remaining string
+ Builder.Append(Expression.Substring(Start))
+ Exit While
+ End If
+
+ FindLocation = Comparer.IndexOf(Expression, Find, Start, CompareFlags)
+ If FindLocation < 0 Then
+ 'We didn't find the Find string append the rest of the string
+ Builder.Append(Expression.Substring(Start))
+ Exit While
+ Else
+ 'Append to our string builder everything up to the found string, then
+ 'append the replacement
+ Builder.Append(Expression.Substring(Start, FindLocation - Start))
+ Builder.Append(Replacement)
+ Replacements += 1
+
+ 'Move the start of our search past the string we just replaced
+ Start = FindLocation + FindLength
+ End If
+ End While
+
+ Return Builder.ToString()
+
+ End Function
+
+ Public Function Space(ByVal Number As Integer) As String
+
+ If Number >= 0 Then
+ Return New String(ChrW(32), Number)
+ End If
+
+ Throw New ArgumentException(GetResourceString(SR.Argument_GEZero1, "Number"))
+
+ End Function
+
+ Public Function Split(ByVal Expression As String, Optional ByVal Delimiter As String = " ", Optional ByVal Limit As Integer = -1, <Microsoft.VisualBasic.CompilerServices.OptionCompareAttribute()> Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As String()
+ Try
+ 'Use String.Split
+ Dim aList() As String
+ Dim iDelLen As Integer
+
+ If Expression Is Nothing Then
+ GoTo EmptyExpression
+ End If
+
+ If Expression.Length = 0 Then
+EmptyExpression:
+ ReDim aList(0)
+ aList(0) = ""
+ Return aList
+ End If
+
+ If Limit = -1 Then
+ Limit = Expression.Length + 1
+ End If
+
+ If Delimiter Is Nothing Then
+ iDelLen = 0
+ Else
+ iDelLen = Delimiter.Length
+ End If
+
+ If iDelLen = 0 Then
+EmptyDelimiterString:
+ ReDim aList(0)
+ aList(0) = Expression
+ Return aList
+ End If
+
+ 'Not handled: LIGATURE expansion
+ Return SplitHelper(Expression, Delimiter, Limit, [Compare])
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Private Function SplitHelper(ByVal sSrc As String, ByVal sFind As String, ByVal cMaxSubStrings As Integer, ByVal [Compare] As Integer) As String()
+ Dim cSubStrings As Integer
+ Dim iIndex As Integer
+ Dim iFindLen As Integer
+ Dim iSrcLen As Integer
+ Dim asSubstrings() As String
+ Dim sSubString As String
+ Dim iLastIndex As Integer
+ Dim cDelimPosMax As Integer
+ Dim cmpInfo As CompareInfo
+ Dim flags As CompareOptions
+
+ If sFind Is Nothing Then
+ iFindLen = 0
+ Else
+ iFindLen = sFind.Length
+ End If
+
+ If sSrc Is Nothing Then
+ iSrcLen = 0
+ Else
+ iSrcLen = sSrc.Length
+ End If
+
+ If iFindLen = 0 Then
+ ReDim asSubstrings(0)
+ asSubstrings(0) = sSrc
+ Return asSubstrings
+ End If
+
+ If iSrcLen = 0 Then
+ ReDim asSubstrings(0)
+ asSubstrings(0) = sSrc
+ Return asSubstrings
+ End If
+
+ cDelimPosMax = 20
+
+ If cDelimPosMax > cMaxSubStrings Then
+ cDelimPosMax = cMaxSubStrings
+ End If
+
+ ReDim asSubstrings(cDelimPosMax)
+
+ If [Compare] = CompareMethod.Binary Then
+ flags = CompareOptions.Ordinal
+ cmpInfo = m_InvariantCompareInfo
+ Else
+ cmpInfo = GetCultureInfo().CompareInfo
+ flags = STANDARD_COMPARE_FLAGS
+ End If
+
+ Do While (iLastIndex < iSrcLen)
+ iIndex = cmpInfo.IndexOf(sSrc, sFind, iLastIndex, iSrcLen - iLastIndex, flags)
+
+ If (iIndex = -1) OrElse (cSubStrings + 1 = cMaxSubStrings) Then
+ 'Just put the remainder of the string in the next element
+ sSubString = sSrc.Substring(iLastIndex)
+ If sSubString Is Nothing Then
+ sSubString = ""
+ End If
+ asSubstrings(cSubStrings) = sSubString
+ Exit Do
+ Else
+ 'Put the characters between iLastIndex and iIndex into the next element
+ sSubString = sSrc.Substring(iLastIndex, iIndex - iLastIndex)
+ If sSubString Is Nothing Then
+ sSubString = ""
+ End If
+ asSubstrings(cSubStrings) = sSubString
+ iLastIndex = iIndex + iFindLen
+ End If
+
+ cSubStrings += 1
+
+ If (cSubStrings > cDelimPosMax) Then
+ cDelimPosMax += 20
+ If cDelimPosMax > cMaxSubStrings Then
+ cDelimPosMax = cMaxSubStrings + 1
+ End If
+ ReDim Preserve asSubstrings(cDelimPosMax)
+ End If
+
+ 'Must Initialize to empty string, otherwise it looks like an object
+ asSubstrings(cSubStrings) = ""
+
+ If cSubStrings = cMaxSubStrings Then
+ sSubString = sSrc.Substring(iLastIndex)
+ If sSubString Is Nothing Then
+ sSubString = ""
+ End If
+ asSubstrings(cSubStrings) = sSubString
+ Exit Do
+ End If
+ Loop
+
+RedimAndExit:
+ If cSubStrings + 1 = asSubstrings.Length Then
+ Return asSubstrings
+ End If
+
+ ReDim Preserve asSubstrings(cSubStrings)
+ Return asSubstrings
+ End Function
+
'============================================================================
' Fixed-length string functions.
'============================================================================
+ Public Function LSet(ByVal Source As String, ByVal Length As Integer) As String
+ If (Length = 0) Then
+ Return ""
+ ElseIf (Source Is Nothing) Then
+ Return New String(" "c, Length)
+ End If
+
+ If Length > Source.Length Then
+ Return Source.PadRight(Length)
+ Else
+ Return Source.Substring(0, Length)
+ End If
+ End Function
+
+ Public Function RSet(ByVal Source As String, ByVal Length As Integer) As String
+ If (Length = 0) Then
+ Return ""
+ ElseIf Source Is Nothing Then
+ Return New String(" "c, Length)
+ End If
+
+ If Length > Source.Length Then
+ Return Source.PadLeft(Length)
+ Else
+ Return Source.Substring(0, Length)
+ End If
+ End Function
+
+ Public Function StrDup(ByVal Number As Integer, ByVal Character As Object) As Object
+ Dim s As String
+ Dim SingleChar As Char
+
+ If Number < 0 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Number"))
+ End If
+
+ If Character Is Nothing Then
+ Throw New ArgumentNullException(GetResourceString(SR.Argument_InvalidNullValue1, "Character"))
+ End If
+
+ s = TryCast(Character, String)
+
+ If s IsNot Nothing Then
+ If s.Length = 0 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_LengthGTZero1, "Character"))
+ End If
+ SingleChar = s.Chars(0)
+ Else
+ Try
+ SingleChar = CChar(Character)
+ Catch ex As StackOverflowException
+ Throw ex
+ Catch ex As OutOfMemoryException
+ Throw ex
+ Catch ex As System.Threading.ThreadAbortException
+ Throw ex
+ Catch
+ Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Character"))
+ End Try
+ End If
+
+ Return New String(SingleChar, Number)
+ End Function
+
+ Public Function StrDup(ByVal Number As Integer, ByVal Character As Char) As String
+ If Number < 0 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_GEZero1, "Number"))
+ End If
+
+ Return New String(Character, Number)
+ End Function
+
+ Public Function StrDup(ByVal Number As Integer, ByVal Character As String) As String
+ If Number < 0 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_GEZero1, "Number"))
+ End If
+
+ If Character Is Nothing OrElse Character.Length = 0 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_LengthGTZero1, "Character"))
+ End If
+
+ Return New String(Character.Chars(0), Number)
+ End Function
+
Public Function StrReverse(ByVal Expression As String) As String
If (Expression Is Nothing) Then
End Function
+ Public Function UCase(ByVal [Value] As String) As String
+ Try
+ If Value Is Nothing Then
+ Return ""
+ Else
+ Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToUpper(Value)
+ End If
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Public Function UCase(ByVal Value As Char) As Char
+ Try
+ Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToUpper(Value)
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ '*************************************************************
+ '** PERF NOTE:
+ '** All Format calls must go through FormatNamed
+ '** But we don't want to put a bunch of overhead on the more
+ '** common cases that are not named formats
+ '** The expensive CompareInfo.Compare calls have been limited
+ '** just one call
+ '**************************************************************
+
+ Private Function FormatNamed(ByVal Expression As Object, ByVal Style As String, ByRef ReturnValue As String) As Boolean
+ Dim StyleLength As Integer = Style.Length
+
+ ReturnValue = Nothing
+
+ Select Case StyleLength
+
+ Case 5
+ Select Case Style.Chars(0)
+ '(F)ixed
+ Case "f"c, "F"c
+ If String.Compare(Style, NAMEDFORMAT_FIXED, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDbl(Expression).ToString("0.00", Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 6
+ 'switch off 1st char (index 0) to reduce number of string compares
+ '(Y)es/no
+ '(O)n/off
+ Select Case Style.Chars(0)
+ Case "y"c, "Y"c
+ If String.Compare(Style, NAMEDFORMAT_YES_NO, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CInt(CBool(Expression)).ToString(CachedYesNoFormatStyle, Nothing)
+ Return True
+ End If
+
+ Case "o"c, "O"c
+ If String.Compare(Style, NAMEDFORMAT_ON_OFF, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CInt(CBool(Expression)).ToString(CachedOnOffFormatStyle, Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 7
+ 'switch off 1st char (index 0) to reduce number of string compares
+ '(P)ercent
+ Select Case Style.Chars(0)
+ Case "p"c, "P"c
+ If String.Compare(Style, NAMEDFORMAT_PERCENT, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDbl(Expression).ToString("0.00%", Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 8
+ 'switch off 6th char (index 5) to reduce number of string compares
+ '(S)tandard
+ '(C)urrency
+
+ Select Case Style.Chars(0)
+ Case "s"c, "S"c
+ If String.Compare(Style, NAMEDFORMAT_STANDARD, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDbl(Expression).ToString("N2", Nothing)
+ Return True
+ End If
+ Case "c"c, "C"c
+ If String.Compare(Style, NAMEDFORMAT_CURRENCY, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDbl(Expression).ToString("C", Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 9
+ 'switch off 6th char (index 5) to reduce number of string compares
+ 'Long (T)ime
+ 'Long (D)ate
+
+ Select Case Style.Chars(5)
+ Case "t"c, "T"c
+ If String.Compare(Style, NAMEDFORMAT_LONG_TIME, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("T", Nothing)
+ Return True
+ End If
+
+ Case "d"c, "D"c
+ If String.Compare(Style, NAMEDFORMAT_LONG_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("D", Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 10
+ 'switch off 7th char (index 6) to reduce number of string compares
+ 'true/f(A)lse
+ 'short (T)ime
+ 'short (D)ate
+ 'scient(I)fic
+
+ Select Case Style.Chars(6)
+ Case "a"c, "A"c
+ If String.Compare(Style, NAMEDFORMAT_TRUE_FALSE, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CInt(CBool(Expression)).ToString(CachedTrueFalseFormatStyle, Nothing)
+ Return True
+ End If
+
+ Case "t"c, "T"c
+ If String.Compare(Style, NAMEDFORMAT_SHORT_TIME, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("t", Nothing)
+ Return True
+ End If
+
+ Case "d"c, "D"c
+ If String.Compare(Style, NAMEDFORMAT_SHORT_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("d", Nothing)
+ Return True
+ End If
+
+ Case "i"c, "I"c
+ If String.Compare(Style, NAMEDFORMAT_SCIENTIFIC, StringComparison.OrdinalIgnoreCase) = 0 Then
+ Dim dbl As Double
+ dbl = CDbl(Expression)
+ If System.Double.IsNaN(dbl) OrElse System.Double.IsInfinity(dbl) Then
+ ReturnValue = dbl.ToString("G", Nothing)
+ Else
+ ReturnValue = dbl.ToString("0.00E+00", Nothing)
+ End If
+ Return True
+ End If
+
+ End Select
+
+ Case 11
+ 'switch off 8th char (index 7) to reduce number of string compares
+ 'medium (T)ime
+ 'medium (D)ate
+
+ Select Case Style.Chars(7)
+ Case "t"c, "T"c
+ If String.Compare(Style, NAMEDFORMAT_MEDIUM_TIME, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("T", Nothing)
+ Return True
+ End If
+
+ Case "d"c, "D"c
+ If String.Compare(Style, NAMEDFORMAT_MEDIUM_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("D", Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 12
+ Select Case Style.Chars(0)
+ Case "g"c, "G"c
+ If String.Compare(Style, NAMEDFORMAT_GENERAL_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDate(Expression).ToString("G", Nothing)
+ Return True
+ End If
+ End Select
+
+ Case 14
+ Select Case Style.Chars(0)
+ Case "g"c, "G"c
+ If String.Compare(Style, NAMEDFORMAT_GENERAL_NUMBER, StringComparison.OrdinalIgnoreCase) = 0 Then
+ ReturnValue = CDbl(Expression).ToString("G", Nothing)
+ Return True
+ End If
+ End Select
+
+ End Select
+
+ Return False
+
+ End Function
+
+ '============================================================================
+ ' Format functions.
+ '============================================================================
+ Public Function Format(ByVal Expression As Object, Optional ByVal Style As String = "") As String
+ Try
+ Dim cp As IFormatProvider = Nothing 'GetCultureInfo()
+ Dim tc As TypeCode
+ Dim iformat As IFormattable = Nothing
+
+ If (Expression Is Nothing) OrElse (Expression.GetType() Is Nothing) Then
+ Return ""
+ End If
+
+ If Style Is Nothing OrElse Style.Length = 0 Then
+ Return CStr(Expression)
+ End If
+
+ Dim ConvertibleExpression As IConvertible = CType(Expression, IConvertible)
+ tc = ConvertibleExpression.GetTypeCode()
+
+ If Style.Length > 0 Then
+ Try
+ Dim ReturnValue As String = Nothing
+
+ If FormatNamed(Expression, Style, ReturnValue) Then
+ Return ReturnValue
+ End If
+ Catch ex As StackOverflowException
+ Throw ex
+ Catch ex As OutOfMemoryException
+ Throw ex
+ Catch ex As System.Threading.ThreadAbortException
+ Throw ex
+ Catch
+ 'Object could not be converted to required type
+ 'so just return the string
+ Return CStr(Expression)
+ End Try
+ End If
+
+ iformat = TryCast(Expression, IFormattable)
+
+ If iformat Is Nothing Then
+ tc = System.Convert.GetTypeCode(Expression)
+ If tc <> TypeCode.String AndAlso tc <> TypeCode.Boolean Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Expression"))
+ End If
+ End If
+
+ Select Case tc
+ Case TypeCode.Boolean
+ Return System.String.Format(cp, Style, CStr(ConvertibleExpression.ToBoolean(Nothing)))
+ Case TypeCode.SByte,
+ TypeCode.Byte,
+ TypeCode.Int16,
+ TypeCode.UInt16,
+ TypeCode.Int32,
+ TypeCode.UInt32,
+ TypeCode.Int64,
+ TypeCode.UInt64,
+ TypeCode.Decimal,
+ TypeCode.DateTime,
+ TypeCode.Char,
+ TypeCode.Object
+ Return iformat.ToString(Style, cp)
+ Case TypeCode.DBNull
+ Return ""
+ Case TypeCode.Double
+ Dim dbl As Double
+
+ dbl = ConvertibleExpression.ToDouble(Nothing)
+
+ If Style Is Nothing OrElse Style.Length = 0 Then
+ Return CStr(dbl)
+ End If
+
+ If dbl = 0 Then
+ 'Used to get rid of possible negative zero,
+ 'which will format as -0
+ dbl = 0
+ End If
+ Return dbl.ToString(Style, cp)
+ Case TypeCode.Empty
+ Return ""
+ Case TypeCode.Single
+ Dim sng As Single
+
+ sng = ConvertibleExpression.ToSingle(Nothing)
+
+ If Style Is Nothing OrElse Style.Length = 0 Then
+ Return CStr(sng)
+ End If
+
+ If sng = 0 Then
+ 'Used to get rid of possible negative zero
+ sng = 0
+ End If
+
+ Return sng.ToString(Style, cp)
+ Case TypeCode.String
+ Return System.String.Format(cp, Style, Expression)
+ Case Else
+ Return iformat.ToString(Style, cp)
+ End Select
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Public Function FormatCurrency(ByVal Expression As Object,
+ Optional ByVal NumDigitsAfterDecimal As Integer = -1,
+ Optional ByVal IncludeLeadingDigit As TriState = TriState.UseDefault,
+ Optional ByVal UseParensForNegativeNumbers As TriState = TriState.UseDefault,
+ Optional ByVal GroupDigits As TriState = TriState.UseDefault) As String
+
+ Dim ifmt As IFormattable
+ Dim typ As Type
+ Dim fp As IFormatProvider = Nothing
+ Dim dbl As Double
+
+ Try
+ ValidateTriState(IncludeLeadingDigit)
+ ValidateTriState(UseParensForNegativeNumbers)
+ ValidateTriState(GroupDigits)
+
+ If NumDigitsAfterDecimal > 99 Then 'Was 255 in VB6, but System.Globalization.NumberFormatInfo.CurrencyDecimalDigits limits this to 99.
+ Throw New ArgumentException(GetResourceString(SR.Argument_Range0to99_1, "NumDigitsAfterDecimal"))
+ End If
+
+ If Expression Is Nothing Then
+ Return ""
+ End If
+
+ typ = Expression.GetType()
+
+ If typ Is GetType(System.String) Then
+ Expression = CDbl(Expression)
+ ElseIf Not Symbols.IsNumericType(typ) Then
+ Throw New InvalidCastException(GetResourceString(SR.InvalidCast_FromTo, VBFriendlyName(typ), "Currency"))
+ End If
+
+ ifmt = CType(Expression, IFormattable)
+
+ Dim FormatStyle As String
+ If IncludeLeadingDigit = TriState.False Then
+ dbl = CDbl(Expression)
+ If dbl >= 1 OrElse dbl <= -1 Then
+ ' If leading digit doesn't matter, this avoids
+ ' going through the overhead of creating a format string
+ IncludeLeadingDigit = TriState.True
+ End If
+ End If
+ FormatStyle = GetCurrencyFormatString(IncludeLeadingDigit, NumDigitsAfterDecimal, UseParensForNegativeNumbers, GroupDigits, fp)
+
+ Return ifmt.ToString(FormatStyle, fp)
+
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Public Function FormatDateTime(ByVal Expression As DateTime, Optional ByVal NamedFormat As DateFormat = DateFormat.GeneralDate) As String
+ Dim sFormat As String
+
+ Try
+ Select Case NamedFormat
+ Case DateFormat.LongDate
+ sFormat = "D"
+ Case DateFormat.ShortDate
+ sFormat = "d"
+ Case DateFormat.LongTime
+ sFormat = "T"
+ Case DateFormat.ShortTime
+ sFormat = "HH:mm"
+ Case DateFormat.GeneralDate
+ If Expression.TimeOfDay.Ticks = Expression.Ticks Then
+ 'Date is 1/1/0001 - don't print date part
+ 'Same as LongTime
+ sFormat = "T"
+ ElseIf Expression.TimeOfDay.Ticks = 0 Then
+ '12AM - don't print time part
+ 'Same as ShortDate
+ sFormat = "d"
+ Else
+ 'Short date + Long Time
+ sFormat = "G"
+ End If
+ Case Else
+ Throw VbMakeException(vbErrors.IllegalFuncCall)
+ End Select
+
+ Return Expression.ToString(sFormat, Nothing)
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Public Function FormatNumber(ByVal Expression As Object, Optional ByVal NumDigitsAfterDecimal As Integer = -1, Optional ByVal IncludeLeadingDigit As TriState = TriState.UseDefault, Optional ByVal UseParensForNegativeNumbers As TriState = TriState.UseDefault, Optional ByVal GroupDigits As TriState = TriState.UseDefault) As String
+ Dim ifmt As IFormattable
+ Dim typ As Type
+
+ Try
+ ValidateTriState(IncludeLeadingDigit)
+ ValidateTriState(UseParensForNegativeNumbers)
+ ValidateTriState(GroupDigits)
+
+ If Expression Is Nothing Then
+ Return ""
+ End If
+
+ typ = Expression.GetType()
+
+ If typ Is GetType(System.String) Then
+ Expression = CDbl(Expression)
+ ElseIf typ Is GetType(System.Boolean) Then
+ If CBool(Expression) Then
+ Expression = -1.0
+ Else
+ Expression = 0.0
+ End If
+ ElseIf Not Symbols.IsNumericType(typ) Then
+ Throw New InvalidCastException(GetResourceString(SR.InvalidCast_FromTo, VBFriendlyName(typ), "Currency"))
+ End If
+
+ ifmt = CType(Expression, IFormattable)
+
+ Return ifmt.ToString(GetNumberFormatString(NumDigitsAfterDecimal, IncludeLeadingDigit,
+ UseParensForNegativeNumbers, GroupDigits), Nothing)
+ Catch ex As Exception
+ Throw ex
+ End Try
+ End Function
+
+ Friend Function GetFormatString(ByVal NumDigitsAfterDecimal As Integer,
+ ByVal IncludeLeadingDigit As TriState, ByVal UseParensForNegativeNumbers As TriState,
+ ByVal GroupDigits As TriState, ByVal FormatTypeValue As FormatType) As String
+
+ Dim nfi As NumberFormatInfo
+ Dim sb As StringBuilder
+ Dim sGroup As String
+ Dim sLeadDigit As String
+ Dim sDigitsAfterDecimal As String
+ Dim ci As CultureInfo
+
+ sb = New StringBuilder(30)
+
+ ci = GetCultureInfo()
+ nfi = CType(ci.GetFormat(GetType(System.Globalization.NumberFormatInfo)), NumberFormatInfo)
+
+ If NumDigitsAfterDecimal < -1 Then
+ Throw VbMakeException(vbErrors.IllegalFuncCall)
+ ElseIf NumDigitsAfterDecimal = -1 Then
+ If FormatTypeValue = FormatType.Percent Then
+ 'NOTE: We use NumberDecimalDigits, which is set in the
+ ' control panel for VB6 compatibility
+ ' The urt does not use this setting, but makes a default
+ ' of their own.
+ NumDigitsAfterDecimal = nfi.NumberDecimalDigits
+ ElseIf FormatTypeValue = FormatType.Number Then
+ NumDigitsAfterDecimal = nfi.NumberDecimalDigits
+ ElseIf FormatTypeValue = FormatType.Currency Then
+ NumDigitsAfterDecimal = nfi.CurrencyDecimalDigits
+ End If
+ End If
+
+ If GroupDigits = TriState.UseDefault Then
+ GroupDigits = TriState.True
+ If FormatTypeValue = FormatType.Percent Then
+ If IsArrayEmpty(nfi.PercentGroupSizes) Then
+ GroupDigits = TriState.False
+ End If
+ ElseIf FormatTypeValue = FormatType.Number Then
+ If IsArrayEmpty(nfi.NumberGroupSizes) Then
+ GroupDigits = TriState.False
+ End If
+ ElseIf FormatTypeValue = FormatType.Currency Then
+ If IsArrayEmpty(nfi.CurrencyGroupSizes) Then
+ GroupDigits = TriState.False
+ End If
+ End If
+ End If
+
+ If UseParensForNegativeNumbers = TriState.UseDefault Then
+ UseParensForNegativeNumbers = TriState.False
+ 'If FormatTypeValue = FormatType.Percent Then
+ ' If nfi.PercentNegativePattern = 0 Then
+ ' UseParensForNegativeNumbers = TriState.True
+ ' End If
+ 'Else
+
+ If FormatTypeValue = FormatType.Number Then
+ If nfi.NumberNegativePattern = 0 Then
+ UseParensForNegativeNumbers = TriState.True
+ End If
+ ElseIf FormatTypeValue = FormatType.Currency Then
+ If nfi.CurrencyNegativePattern = 0 Then
+ UseParensForNegativeNumbers = TriState.True
+ End If
+ End If
+ End If
+
+ If GroupDigits = TriState.True Then
+ sGroup = "#,##"
+ Else
+ sGroup = ""
+ End If
+
+ If IncludeLeadingDigit <> TriState.False Then
+ sLeadDigit = "0"
+ Else
+ sLeadDigit = "#"
+ End If
+
+ If NumDigitsAfterDecimal > 0 Then
+ sDigitsAfterDecimal = "." & (New System.String("0"c, NumDigitsAfterDecimal))
+ Else
+ sDigitsAfterDecimal = ""
+ End If
+
+ 'Now put together the string
+ If FormatTypeValue = FormatType.Currency Then
+ sb.Append(nfi.CurrencySymbol)
+ End If
+
+ sb.Append(sGroup)
+ sb.Append(sLeadDigit)
+ sb.Append(sDigitsAfterDecimal)
+
+ If FormatTypeValue = FormatType.Percent Then
+ sb.Append(nfi.PercentSymbol)
+ End If
+
+ If UseParensForNegativeNumbers = TriState.True Then
+ Dim sTmp As String
+ sTmp = sb.ToString()
+ sb.Append(";(")
+ sb.Append(sTmp)
+ sb.Append(")")
+ End If
+
+ Return sb.ToString()
+ End Function
+
+ Friend Function GetCurrencyFormatString(
+ ByVal IncludeLeadingDigit As TriState,
+ ByVal NumDigitsAfterDecimal As Integer,
+ ByVal UseParensForNegativeNumbers As TriState,
+ ByVal GroupDigits As TriState,
+ ByRef formatProvider As IFormatProvider) As String
+
+ Dim nfi As NumberFormatInfo
+ Dim ci As CultureInfo
+ Dim CurrencyNegativePattern, CurrencyPositivePattern As Integer
+ Dim FormatString, NumberFormat As String
+
+ GetCurrencyFormatString = "C"
+
+ ci = GetCultureInfo()
+ nfi = CType(ci.GetFormat(GetType(System.Globalization.NumberFormatInfo)), NumberFormatInfo)
+ nfi = CType(nfi.Clone(), NumberFormatInfo)
+
+ If GroupDigits = TriState.False Then
+ nfi.CurrencyGroupSizes = New Int32() {0}
+ End If
+
+ CurrencyPositivePattern = nfi.CurrencyPositivePattern
+ CurrencyNegativePattern = nfi.CurrencyNegativePattern
+
+ If UseParensForNegativeNumbers = TriState.UseDefault Then
+
+ Select Case CurrencyNegativePattern
+ Case 0, 4, 14, 15
+ UseParensForNegativeNumbers = TriState.True
+ Case Else
+ UseParensForNegativeNumbers = TriState.False
+ End Select
+
+ ElseIf UseParensForNegativeNumbers = TriState.False Then
+
+ Select Case CurrencyNegativePattern
+ Case 0
+ CurrencyNegativePattern = 1
+ Case 4
+ CurrencyNegativePattern = 5
+ Case 14
+ CurrencyNegativePattern = 9
+ Case 15
+ CurrencyNegativePattern = 10
+ End Select
+
+ Else
+
+ UseParensForNegativeNumbers = TriState.True
+
+ Select Case CurrencyNegativePattern
+ Case 1, 2, 3 'leading $ w/o space
+ CurrencyNegativePattern = 0
+ Case 5, 6, 7 'trailing $ w/o space
+ CurrencyNegativePattern = 4
+ Case 8, 10, 13 'Trailing $ / leading with space
+ CurrencyNegativePattern = 15
+ Case 9, 11, 12
+ CurrencyNegativePattern = 14
+ End Select
+
+ End If
+
+ nfi.CurrencyNegativePattern = CurrencyNegativePattern
+
+ If NumDigitsAfterDecimal = -1 Then
+ NumDigitsAfterDecimal = nfi.CurrencyDecimalDigits
+ End If
+ nfi.CurrencyDecimalDigits = NumDigitsAfterDecimal
+
+ formatProvider = New FormatInfoHolder(nfi)
+
+ If IncludeLeadingDigit = TriState.False Then
+ 'We need to build our own string in this case, since the NDP does not
+ ' make this accessible
+
+ nfi.NumberGroupSizes = nfi.CurrencyGroupSizes
+
+ FormatString = CurrencyPositiveFormatStrings(CurrencyPositivePattern) & ";" &
+ CurrencyNegativeFormatStrings(CurrencyNegativePattern)
+
+ If GroupDigits = TriState.False Then
+ If IncludeLeadingDigit = TriState.False Then
+ NumberFormat = "#"
+ Else
+ NumberFormat = "0"
+ End If
+ Else
+ If IncludeLeadingDigit = TriState.False Then
+ NumberFormat = "#,###"
+ Else
+ NumberFormat = "#,##0"
+ End If
+ End If
+
+ If NumDigitsAfterDecimal > 0 Then
+ NumberFormat = NumberFormat & "." & New String("0"c, NumDigitsAfterDecimal)
+ End If
+
+ If System.String.CompareOrdinal("$", nfi.CurrencySymbol) <> 0 Then
+ 'Replace the '$' sign with the locale specific symbol
+ 'Note, the currency symbol in the FormatString is surrounded by the literal symbol ', e.g. '$'
+ 'We do this to guard against the case where the currency symbol is the literal symbol \ This was causing problems on Japanese
+ 'systems because that meant our format string ended up as "\#,###.00" when we wanted "'\'#,###.00" But because the currency symbol
+ 'we are replacing with could concievably be the literal symbol ' as well, we need to make sure we don't end up with an invalid string like "'''#,###.00"
+ 'So if the currency symbol is a ' we replace it with '' so that our format string will be balanced like "''''#,###.00" which will result in the format
+ 'succeeding. You won't see the ' as the currency symbol in this case but this was never supported anyway.
+ FormatString = FormatString.Replace("$", nfi.CurrencySymbol.Replace("'", "''"))
+ End If
+
+ Return FormatString.Replace("n", NumberFormat)
+ End If
+
+ End Function
+
+ Friend Function GetNumberFormatString(
+ ByVal NumDigitsAfterDecimal As Integer,
+ ByVal IncludeLeadingDigit As TriState,
+ ByVal UseParensForNegativeNumbers As TriState,
+ ByVal GroupDigits As TriState) As String
+
+ Dim nfi As NumberFormatInfo
+ Dim ci As CultureInfo
+ Dim NumberNegativePattern As Integer
+ Dim FormatString, NumberFormat As String
+
+ ci = GetCultureInfo()
+ nfi = CType(ci.GetFormat(GetType(System.Globalization.NumberFormatInfo)), NumberFormatInfo)
+
+ If NumDigitsAfterDecimal = -1 Then
+ NumDigitsAfterDecimal = nfi.NumberDecimalDigits
+ ElseIf (NumDigitsAfterDecimal > 99) OrElse (NumDigitsAfterDecimal < -1) Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_Range0to99_1, "NumDigitsAfterDecimal"))
+ End If
+
+ If GroupDigits = TriState.UseDefault Then
+ If nfi.NumberGroupSizes Is Nothing OrElse nfi.NumberGroupSizes.Length = 0 Then
+ GroupDigits = TriState.False
+ Else
+ GroupDigits = TriState.True
+ End If
+ End If
+
+ NumberNegativePattern = nfi.NumberNegativePattern
+
+ 'Value Associated Pattern
+ '0 (n)
+ '1 - n
+ '2 - n
+ '3 n -
+ '4 n -
+ If UseParensForNegativeNumbers = TriState.UseDefault Then
+ Select Case NumberNegativePattern
+ Case 0
+ UseParensForNegativeNumbers = TriState.True
+ Case Else
+ UseParensForNegativeNumbers = TriState.False
+ End Select
+ ElseIf UseParensForNegativeNumbers = TriState.False Then
+ If NumberNegativePattern = 0 Then
+ NumberNegativePattern = 1
+ End If
+ Else
+ UseParensForNegativeNumbers = TriState.True
+
+ Select Case NumberNegativePattern
+ Case 1, 2, 3, 4
+ NumberNegativePattern = 0
+ End Select
+ End If
+
+ If UseParensForNegativeNumbers = TriState.UseDefault Then
+ UseParensForNegativeNumbers = TriState.True
+ End If
+
+ FormatString = "n;" & NumberNegativeFormatStrings(NumberNegativePattern)
+ If System.String.CompareOrdinal("-", nfi.NegativeSign) <> 0 Then
+ 'Replace the "-" sign with the actual locale-specific symbol (escaped with quotes).
+ ' Note: there appears to be no performance benefit in using a StringBuilder over simple concats.
+ FormatString = FormatString.Replace("-", """" & nfi.NegativeSign & """")
+ End If
+
+ If IncludeLeadingDigit <> TriState.False Then
+ NumberFormat = "0"
+ Else
+ NumberFormat = "#"
+ End If
+
+ If GroupDigits = TriState.False OrElse nfi.NumberGroupSizes.Length = 0 Then
+ 'Just use setting done above '#' or '0'
+ Else
+ If nfi.NumberGroupSizes.Length = 1 Then
+ NumberFormat = "#," & New String("#"c, nfi.NumberGroupSizes(0)) & NumberFormat
+ Else
+ Dim i As Integer
+
+ NumberFormat = New String("#"c, nfi.NumberGroupSizes(0) - 1) & NumberFormat
+ For i = 1 To nfi.NumberGroupSizes.GetUpperBound(0)
+ NumberFormat = "," & New String("#"c, nfi.NumberGroupSizes(i)) & "," & NumberFormat
+ Next i
+ End If
+ End If
+
+ If NumDigitsAfterDecimal > 0 Then
+ NumberFormat = NumberFormat & "." & New String("0"c, NumDigitsAfterDecimal)
+ End If
+
+ Return Replace(FormatString, "n", NumberFormat)
+ End Function
+
+ Public Function FormatPercent(ByVal Expression As Object,
+ Optional ByVal NumDigitsAfterDecimal As Integer = -1,
+ Optional ByVal IncludeLeadingDigit As TriState = TriState.UseDefault,
+ Optional ByVal UseParensForNegativeNumbers As TriState = TriState.UseDefault,
+ Optional ByVal GroupDigits As TriState = TriState.UseDefault) As String
+
+ Dim ifmt As IFormattable
+ Dim typ As Type
+ Dim sFormat As String
+
+ ValidateTriState(IncludeLeadingDigit)
+ ValidateTriState(UseParensForNegativeNumbers)
+ ValidateTriState(GroupDigits)
+
+ If Expression Is Nothing Then
+ Return ""
+ End If
+
+ typ = Expression.GetType()
+
+ If typ Is GetType(System.String) Then
+ Expression = CDbl(Expression)
+ ElseIf Not Symbols.IsNumericType(typ) Then
+ Throw New InvalidCastException(GetResourceString(SR.InvalidCast_FromTo, VBFriendlyName(typ), "numeric"))
+ End If
+
+ ifmt = CType(Expression, IFormattable)
+ sFormat = GetFormatString(NumDigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers,
+ GroupDigits, FormatType.Percent)
+ Return ifmt.ToString(sFormat, Nothing)
+ End Function
+
+ '============================================================================
+ ' GetChar function (new for VB7)
+ '============================================================================
+ Public Function GetChar(ByVal [str] As String, ByVal Index As Integer) As Char
+ If [str] Is Nothing Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_LengthGTZero1, "String"))
+ ElseIf (Index < 1) Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_GEOne1, "Index"))
+ ElseIf (Index > [str].Length) Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_IndexLELength2, "Index", "String"))
+ Else
+ Return [str].Chars(Index - 1)
+ End If
+ End Function
+
'============================================================================
' Left/Right/Mid/Trim functions.
'============================================================================
Throw ex
End Try
End Function
+
+ Friend Function IsValidCodePage(ByVal codepage As Integer) As Boolean
+ IsValidCodePage = False
+
+ Try
+ If Encoding.GetEncoding(codepage) IsNot Nothing Then
+ IsValidCodePage = True
+ End If
+ Catch ex As StackOverflowException
+ Throw ex
+ Catch ex As OutOfMemoryException
+ Throw ex
+ Catch ex As System.Threading.ThreadAbortException
+ Throw ex
+ Catch
+ End Try
+ End Function
+
+ Public Function StrConv(ByVal [str] As String, ByVal Conversion As VbStrConv, Optional ByVal LocaleID As Integer = 0) As String
+#If PLATFORM_WINDOWS Then
+ Try
+ Const LANG_CHINESE As Integer = &H4I
+ Const LANG_JAPANESE As Integer = &H11I
+ Const LANG_KOREAN As Integer = &H12I
+ Dim dwMapFlags As Integer
+ Dim loc As CultureInfo
+ Dim langid As Integer
+
+ If (LocaleID = 0 OrElse LocaleID = 1) Then
+ loc = GetCultureInfo()
+ LocaleID = loc.LCID()
+ Else
+ Try
+ loc = New CultureInfo(LocaleID And &HFFFFI)
+ Catch ex As StackOverflowException
+ Throw ex
+ Catch ex As OutOfMemoryException
+ Throw ex
+ Catch ex As System.Threading.ThreadAbortException
+ Throw ex
+ Catch
+ Throw New ArgumentException(GetResourceString(SR.Argument_LCIDNotSupported1, CStr(LocaleID)))
+ End Try
+ End If
+
+ langid = PRIMARYLANGID(LocaleID)
+
+ 'Ensure only valid bits for Conversion are passed in.
+ If (Conversion And Not (VbStrConv.Uppercase Or VbStrConv.Lowercase Or VbStrConv.Wide Or VbStrConv.Narrow _
+ Or VbStrConv.Katakana Or VbStrConv.Hiragana Or VbStrConv.SimplifiedChinese Or VbStrConv.TraditionalChinese _
+ Or VbStrConv.LinguisticCasing)) <> 0 Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_InvalidVbStrConv))
+ End If
+
+ '*** VbStrConv.SimplifiedChinese/VbStrConv.TraditionalChinese handling
+ Select Case (Conversion And (VbStrConv.SimplifiedChinese + VbStrConv.TraditionalChinese))
+
+ Case 0
+ 'Flags not used
+ Case (VbStrConv.SimplifiedChinese + VbStrConv.TraditionalChinese)
+ Throw New ArgumentException(GetResourceString(SR.Argument_StrConvSCandTC))
+ Case VbStrConv.SimplifiedChinese
+ If IsValidCodePage(CODEPAGE_SIMPLIFIED_CHINESE) AndAlso IsValidCodePage(CODEPAGE_TRADITIONAL_CHINESE) Then
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_SIMPLIFIED_CHINESE
+ Else
+ Throw New ArgumentException(GetResourceString(SR.Argument_SCNotSupported))
+ End If
+ Case VbStrConv.TraditionalChinese
+ If IsValidCodePage(CODEPAGE_SIMPLIFIED_CHINESE) AndAlso IsValidCodePage(CODEPAGE_TRADITIONAL_CHINESE) Then
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_TRADITIONAL_CHINESE
+ Else
+ Throw New ArgumentException(GetResourceString(SR.Argument_TCNotSupported))
+ End If
+ End Select
+
+ '*** Upper/Lowercase handling
+ Select Case (Conversion And (VbStrConv.Uppercase Or VbStrConv.Lowercase))
+ Case VbStrConv.None
+ 'No conversion
+ If (Conversion And VbStrConv.LinguisticCasing) <> 0 Then
+ Throw New ArgumentException(GetResourceString(SR.LinguisticRequirements))
+ End If
+
+ Case (VbStrConv.Uppercase Or VbStrConv.Lowercase) ' VbStrConv.ProperCase is special: see below
+ 'Proper casing gets done below
+ dwMapFlags = 0
+ Case VbStrConv.Uppercase
+ If Conversion = VbStrConv.Uppercase Then
+ Return loc.TextInfo.ToUpper(str)
+ Else
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_UPPERCASE
+ End If
+ Case VbStrConv.Lowercase
+ If Conversion = VbStrConv.Lowercase Then
+ Return loc.TextInfo.ToLower(str)
+ Else
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_LOWERCASE
+ End If
+ End Select
+
+ If ((Conversion And (VbStrConv.Katakana + VbStrConv.Hiragana)) <> 0) Then
+ If (langid <> LANG_JAPANESE) OrElse (Not ValidLCID(LocaleID)) Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_JPNNotSupported))
+ Else
+ 'Locale is ok
+ End If
+ End If
+
+ If (Conversion And (VbStrConv.Wide Or VbStrConv.Narrow)) <> 0 Then
+ If (langid = LANG_JAPANESE) OrElse
+ (langid = LANG_KOREAN) OrElse
+ (langid = LANG_CHINESE) Then
+ If Not ValidLCID(LocaleID) Then
+ Throw New ArgumentException(GetResourceString(SR.Argument_LocalNotSupported))
+ End If
+ Else
+ Throw New ArgumentException(GetResourceString(SR.Argument_WideNarrowNotApplicable))
+ End If
+ End If
+
+ '*** Width handling
+ Select Case (Conversion And (VbStrConv.Wide Or VbStrConv.Narrow))
+ Case VbStrConv.None
+ Case VbStrConv.Wide Or VbStrConv.Narrow ' VbStrConv.Wide+VbStrConv.Narrow is reserved
+ Throw New ArgumentException(GetResourceString(SR.Argument_IllegalWideNarrow))
+ Case VbStrConv.Wide ' VbStrConv.Wide
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_FULLWIDTH
+ Case VbStrConv.Narrow ' VbStrConv.Narrow
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_HALFWIDTH
+ End Select
+
+ '*** Kana handling
+ Select Case (Conversion And (VbStrConv.Katakana Or VbStrConv.Hiragana))
+ Case VbStrConv.None
+ Case (VbStrConv.Katakana Or VbStrConv.Hiragana) ' VbStrConv.Katakana+VbStrConv.Hiragana is reserved
+ Throw New ArgumentException(GetResourceString(SR.Argument_IllegalKataHira))
+ Case VbStrConv.Katakana ' VbStrConv.Katakana
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_KATAKANA
+ Case VbStrConv.Hiragana ' VbStrConv.Hiragana
+ dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_HIRAGANA
+ End Select
+
+ ' accents field (Conversion And 192) in Conversion is reserved
+ If ((Conversion And VbStrConv.ProperCase) = VbStrConv.ProperCase) Then
+ Return ProperCaseString(loc, dwMapFlags, [str])
+ ElseIf dwMapFlags <> 0 Then
+ Return vbLCMapString(loc, dwMapFlags, [str])
+ Else
+ Return [str]
+ End If
+ Catch ex As Exception
+ Throw ex
+ End Try
+#Else
+ Throw New PlatformNotSupportedException()
+#End If
+ End Function
+
+#If PLATFORM_WINDOWS Then
+ Friend Function ValidLCID(ByVal LocaleID As Integer) As Boolean
+ Try
+ Dim loc As CultureInfo = New CultureInfo(LocaleID)
+ ValidLCID = True
+ Catch ex As StackOverflowException
+ Throw ex
+ Catch ex As OutOfMemoryException
+ Throw ex
+ Catch ex As System.Threading.ThreadAbortException
+ Throw ex
+ Catch
+ ValidLCID = False
+ End Try
+ End Function
+
+ Private Function ProperCaseString(ByVal loc As CultureInfo, ByVal dwMapFlags As Integer, ByVal sSrc As String) As String
+ Dim iSrcLen As Integer
+ Dim sb As StringBuilder
+
+ If sSrc Is Nothing Then
+ iSrcLen = 0
+ Else
+ iSrcLen = sSrc.Length
+ End If
+
+ If iSrcLen = 0 Then
+ Return ""
+ End If
+
+ ' do the mapping specified by dwMapFlags, and at the same time, lowercase
+ ' the whole string
+ sb = New StringBuilder(vbLCMapString(loc, dwMapFlags Or NativeTypes.LCMAP_LOWERCASE, sSrc))
+
+ 'ToTitleCase is a more linguistically correct casing for the current locale
+ Return loc.TextInfo.ToTitleCase(sb.ToString())
+
+ End Function
+
+ <ResourceExposure(ResourceScope.None)>
+ <ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)>
+ Friend Function vbLCMapString(ByVal loc As CultureInfo, ByVal dwMapFlags As Integer, ByVal sSrc As String) As String
+ Dim length As Integer
+
+ If sSrc Is Nothing Then
+ length = 0
+ Else
+ length = sSrc.Length
+ End If
+
+ If length = 0 Then
+ Return ""
+ End If
+
+ Dim sDest As String
+ Dim lenDest As Integer
+ Dim lcid As Integer = loc.LCID
+ Dim enc As Text.Encoding = Text.Encoding.GetEncoding(loc.TextInfo.ANSICodePage)
+
+ If Not enc.IsSingleByte Then
+
+ 'VB6 syntax note: ByVal String in Declare statements is really a ByRef String syntax
+ 'So sTemp will always be updated here on copyback
+ Dim sTemp As String = sSrc
+ Dim bytesSrc, bytesDest As Byte()
+
+ 'Forced to use ANSI here
+ 'Char count can actual increase or decrease
+
+ 'Get byte array
+ bytesSrc = enc.GetBytes(sTemp)
+
+ 'Get required byte length for new destination
+ lenDest = UnsafeNativeMethods.LCMapStringA(lcid, dwMapFlags, bytesSrc, bytesSrc.Length, Nothing, 0)
+
+ 'Create destination byte array of required length
+ bytesDest = New Byte(lenDest - 1) {}
+
+ 'Call again to do the actual translation
+ lenDest = UnsafeNativeMethods.LCMapStringA(lcid, dwMapFlags, bytesSrc, bytesSrc.Length, bytesDest, lenDest)
+
+ 'Now convert back to a string
+ sDest = enc.GetString(bytesDest)
+
+ Return sDest
+
+ Else
+ 'We do not use StringBuilder here because embedded NULLs cause an early termination of the string
+ sDest = New String(" "c, length)
+ lenDest = UnsafeNativeMethods.LCMapString(lcid, dwMapFlags, sSrc, length, sDest, length)
+ Return sDest
+ End If
+
+ End Function
+#End If
+
+ Private Sub ValidateTriState(ByVal Param As TriState)
+ If (Param <> vbTrue) AndAlso (Param <> vbFalse) AndAlso (Param <> vbUseDefault) Then
+ Throw VbMakeException(vbErrors.IllegalFuncCall)
+ End If
+ End Sub
+
+ Private Function IsArrayEmpty(ByVal array As System.Array) As Boolean
+ If array Is Nothing Then
+ Return True
+ End If
+ Return (array.Length = 0)
+ End Function
End Module
End Namespace
// See the LICENSE file in the project root for more information.
using System;
-using System.Diagnostics;
+using System.Collections.Generic;
using System.Globalization;
+using System.Runtime.InteropServices;
using System.Text;
using Microsoft.VisualBasic.CompilerServices.Tests;
using Microsoft.DotNet.RemoteExecutor;
Assert.Equal(excludeExpected, Strings.Filter(source, match, Include: false));
}
+ [Theory]
+ [MemberData(nameof(Format_TestData))]
+ public void Format(object expression, string style, string expected)
+ {
+ Assert.Equal(expected, Strings.Format(expression, style));
+ }
+
+ [Theory]
+ [MemberData(nameof(Format_InvalidCastException_TestData))]
+ public void Format_InvalidCastException(object expression, string style)
+ {
+ Assert.Throws<InvalidCastException>(() => Strings.Format(expression, style));
+ }
+
+ private static IEnumerable<object[]> Format_TestData()
+ {
+ yield return new object[] { null, null, "" };
+ yield return new object[] { null, "", "" };
+ yield return new object[] { "", null, "" };
+ yield return new object[] { "", "", "" };
+ yield return new object[] { sbyte.MinValue, "0", "-128" };
+ yield return new object[] { sbyte.MaxValue, "0", "127" };
+ yield return new object[] { ushort.MinValue, "0", "0" };
+ yield return new object[] { ushort.MaxValue, "0", "65535" };
+ yield return new object[] { false, "", "False" };
+ yield return new object[] { false, "0", "0" };
+ if (IsEnUS())
+ {
+ yield return new object[] { 1.234, "", "1.234" };
+ yield return new object[] { 1.234, "0", "1" };
+ yield return new object[] { 1.234, "0.0", "1.2" };
+ yield return new object[] { 1.234, "fixed", "1.23" };
+ yield return new object[] { 1.234, "percent", "123.40%" };
+ yield return new object[] { 1.234, "standard", "1.23" };
+ yield return new object[] { 1.234, "currency", "$1.23" };
+ yield return new object[] { false, "yes/no", "No" };
+ yield return new object[] { true, "yes/no", "Yes" };
+ yield return new object[] { false, "on/off", "Off" };
+ yield return new object[] { true, "on/off", "On" };
+ yield return new object[] { false, "true/false", "False" };
+ yield return new object[] { true, "true/false", "True" };
+ yield return new object[] { 0, "yes/no", "No" };
+ yield return new object[] { "ABC", "yes/no", "ABC" };
+ yield return new object[] { 123.4, "scientific", "1.23E+02" };
+ }
+ DateTime d = DateTime.Now;
+ yield return new object[] { d, "long time", d.ToString("T") };
+ yield return new object[] { d, "medium time", d.ToString("T") };
+ yield return new object[] { d, "short time", d.ToString("t") };
+ yield return new object[] { d, "long date", d.ToString("D") };
+ yield return new object[] { d, "medium date", d.ToString("D") };
+ yield return new object[] { d, "short date", d.ToString("d") };
+ yield return new object[] { d, "general date", d.ToString("G") };
+ yield return new object[] { 123.4, "general number", 123.4.ToString("G", null) };
+ }
+
+ private static IEnumerable<object[]> Format_InvalidCastException_TestData()
+ {
+ yield return new object[] { new object(), null };
+ yield return new object[] { new object(), "0" };
+ }
+
+ [Theory]
+ [MemberData(nameof(FormatCurrency_TestData))]
+ public void FormatCurrency(object expression, int numDigitsAfterDecimal, TriState includeLeadingDigit, TriState useParensForNegativeNumbers, TriState groupDigits, string expected)
+ {
+ Assert.Equal(expected, Strings.FormatCurrency(expression, numDigitsAfterDecimal, includeLeadingDigit, useParensForNegativeNumbers, groupDigits));
+ }
+
+ private static IEnumerable<object[]> FormatCurrency_TestData()
+ {
+ yield return new object[] { null, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "" };
+ if (IsEnUS())
+ {
+ yield return new object[] { 0.123, 0, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "$0" };
+ yield return new object[] { 0.123, 1, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "$0.1" };
+ yield return new object[] { 0.123, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "$0.12" };
+ yield return new object[] { 0.123, 4, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "$0.1230" };
+ yield return new object[] { 0.123, 2, TriState.False, TriState.UseDefault, TriState.UseDefault, "$.12" };
+ yield return new object[] { 0.123, 2, TriState.True, TriState.UseDefault, TriState.UseDefault, "$0.12" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.False, TriState.UseDefault, "-$0.12" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.True, TriState.UseDefault, "($0.12)" };
+ yield return new object[] { 1234.5, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "$1,234.50" };
+ yield return new object[] { 1234.5, 2, TriState.UseDefault, TriState.UseDefault, TriState.False, "$1234.50" };
+ yield return new object[] { 1234.5, 2, TriState.UseDefault, TriState.UseDefault, TriState.True, "$1,234.50" };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(FormatDateTime_TestData))]
+ public void FormatDateTime(DateTime expression, DateFormat format, string expected)
+ {
+ Assert.Equal(expected, Strings.FormatDateTime(expression, format));
+ }
+
+ private static IEnumerable<object[]> FormatDateTime_TestData()
+ {
+ DateTime d = DateTime.Now;
+ yield return new object[] { d, DateFormat.LongTime, d.ToString("T") };
+ yield return new object[] { d, DateFormat.ShortTime, d.ToString("HH:mm") };
+ yield return new object[] { d, DateFormat.LongDate, d.ToString("D") };
+ yield return new object[] { d, DateFormat.ShortDate, d.ToString("d") };
+ yield return new object[] { d, DateFormat.GeneralDate, d.ToString("G") };
+ }
+
+ [Theory]
+ [MemberData(nameof(FormatNumber_TestData))]
+ public void FormatNumber(object expression, int numDigitsAfterDecimal, TriState includeLeadingDigit, TriState useParensForNegativeNumbers, TriState groupDigits, string expected)
+ {
+ Assert.Equal(expected, Strings.FormatNumber(expression, numDigitsAfterDecimal, includeLeadingDigit, useParensForNegativeNumbers, groupDigits));
+ }
+
+ private static IEnumerable<object[]> FormatNumber_TestData()
+ {
+ yield return new object[] { null, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "" };
+ if (IsEnUS())
+ {
+ yield return new object[] { 0.123, 0, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "0" };
+ yield return new object[] { 0.123, 1, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "0.1" };
+ yield return new object[] { 0.123, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "0.12" };
+ yield return new object[] { 0.123, 4, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "0.1230" };
+ yield return new object[] { 0.123, 2, TriState.False, TriState.UseDefault, TriState.UseDefault, ".12" };
+ yield return new object[] { 0.123, 2, TriState.True, TriState.UseDefault, TriState.UseDefault, "0.12" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "-0.12" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.False, TriState.UseDefault, "-0.12" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.True, TriState.UseDefault, "(0.12)" };
+ yield return new object[] { 1234.5, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "1,234.50" };
+ yield return new object[] { 1234.5, 2, TriState.UseDefault, TriState.UseDefault, TriState.False, "1234.50" };
+ yield return new object[] { 1234.5, 2, TriState.UseDefault, TriState.UseDefault, TriState.True, "1,234.50" };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(FormatPercent_TestData))]
+ public void FormatPercent(object expression, int numDigitsAfterDecimal, TriState includeLeadingDigit, TriState useParensForNegativeNumbers, TriState groupDigits, string expected)
+ {
+ Assert.Equal(expected, Strings.FormatPercent(expression, numDigitsAfterDecimal, includeLeadingDigit, useParensForNegativeNumbers, groupDigits));
+ }
+
+ private static IEnumerable<object[]> FormatPercent_TestData()
+ {
+ yield return new object[] { null, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "" };
+ if (IsEnUS())
+ {
+ yield return new object[] { 0.123, 0, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "12%" };
+ yield return new object[] { 0.123, 1, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "12.3%" };
+ yield return new object[] { 0.123, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "12.30%" };
+ yield return new object[] { 0.123, 4, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "12.3000%" };
+ yield return new object[] { 0.00123, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "0.12%" };
+ yield return new object[] { 0.00123, 2, TriState.False, TriState.UseDefault, TriState.UseDefault, ".12%" };
+ yield return new object[] { 0.00123, 2, TriState.True, TriState.UseDefault, TriState.UseDefault, "0.12%" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "-12.30%" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.False, TriState.UseDefault, "-12.30%" };
+ yield return new object[] { -0.123, 2, TriState.UseDefault, TriState.True, TriState.UseDefault, "(12.30%)" };
+ yield return new object[] { 12.345, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault, "1,234.50%" };
+ yield return new object[] { 12.345, 2, TriState.UseDefault, TriState.UseDefault, TriState.False, "1234.50%" };
+ yield return new object[] { 12.345, 2, TriState.UseDefault, TriState.UseDefault, TriState.True, "1,234.50%" };
+ }
+ }
+
+ [Theory]
+ [InlineData("ABC", 1, 'A')]
+ [InlineData("ABC", 2, 'B')]
+ [InlineData("ABC", 3, 'C')]
+ public void GetChar(string str, int index, char expected)
+ {
+ Assert.Equal(expected, Strings.GetChar(str, index));
+ }
+
+ [Theory]
+ [InlineData(null, 0)]
+ [InlineData(null, 1)]
+ [InlineData("", 0)]
+ [InlineData("", 1)]
+ [InlineData("ABC", 0)]
+ [InlineData("ABC", 4)]
+ public void GetChar_ArgumentException(string str, int index)
+ {
+ Assert.Throws< ArgumentException>(() => Strings.GetChar(str, index));
+ }
+
[Theory]
[MemberData(nameof(InStr_TestData_NullsAndEmpties))]
[MemberData(nameof(InStr_FromBegin_TestData))]
AssertExtensions.Throws<ArgumentException>("Start", null, () => Strings.InStrRev("a", "a", start));
}
+ [Theory]
+ [MemberData(nameof(Join_Object_TestData))]
+ [MemberData(nameof(Join_String_TestData))]
+ public void Join(object[] source, string delimiter, string expected)
+ {
+ Assert.Equal(expected, Strings.Join(source, delimiter));
+ }
+
+ private static IEnumerable<object[]> Join_Object_TestData()
+ {
+ yield return new object[] { new object[0], null, null };
+ yield return new object[] { new object[0], ",", null };
+ yield return new object[] { new object[] { 1 }, ",", "1" };
+ yield return new object[] { new object[] { 1, null, 3 }, null, "13" };
+ yield return new object[] { new object[] { true, false }, "", "TrueFalse" };
+ yield return new object[] { new object[] { 1, 2, 3 }, ", ", "1, 2, 3" };
+ }
+
+ [Theory]
+ [MemberData(nameof(Join_String_TestData))]
+ public void Join(string[] source, string delimiter, string expected)
+ {
+ Assert.Equal(expected, Strings.Join(source, delimiter));
+ }
+
+ private static IEnumerable<object[]> Join_String_TestData()
+ {
+ yield return new object[] { new string[0], null, null };
+ yield return new object[] { new string[0], ",", null };
+ yield return new object[] { new string[] { "A" }, ",", "A" };
+ yield return new object[] { new string[] { "", null, "" }, null, "" };
+ yield return new object[] { new string[] { "", "AB", "C" }, "", "ABC" };
+ yield return new object[] { new string[] { "A", "B", "C" }, ", ", "A, B, C" };
+ }
+
+ [Theory]
+ [InlineData('\0', "\0")]
+ [InlineData('\uffff', "\uffff")]
+ [InlineData('a', "a")]
+ [InlineData('A', "a")]
+ [InlineData('1', "1")]
+ public void LCase(char value, char expected)
+ {
+ Assert.Equal(expected, Strings.LCase(value));
+ }
+
+ [Theory]
+ [InlineData(null, null)]
+ [InlineData("", "")]
+ [InlineData("\0", "\0")]
+ [InlineData("\uffff", "\uffff")]
+ [InlineData("abc", "abc")]
+ [InlineData("ABC", "abc")]
+ [InlineData("123", "123")]
+ public void LCase(string value, string expected)
+ {
+ Assert.Equal(expected, Strings.LCase(value));
+ }
+
+ [Theory]
+ [InlineData('\0', "\0")]
+ [InlineData('\uffff', "\uffff")]
+ [InlineData('a', "A")]
+ [InlineData('A', "A")]
+ [InlineData('1', "1")]
+ public void UCase(char value, char expected)
+ {
+ Assert.Equal(expected, Strings.UCase(value));
+ }
+
+ [Theory]
+ [InlineData(null, "")]
+ [InlineData("", "")]
+ [InlineData("\0", "\0")]
+ [InlineData("\uffff", "\uffff")]
+ [InlineData("abc", "ABC")]
+ [InlineData("ABC", "ABC")]
+ [InlineData("123", "123")]
+ public void UCase(string value, string expected)
+ {
+ Assert.Equal(expected, Strings.UCase(value));
+ }
+
[Theory]
[InlineData("a", -1)]
public void Left_Invalid(string str, int length)
Assert.Equal(expected, Strings.Mid(str, start, length));
}
+ [Theory]
+ [InlineData(null, 0, "")]
+ [InlineData(null, 1, " ")]
+ [InlineData("", 0, "")]
+ [InlineData("", 1, " ")]
+ [InlineData("A", 0, "")]
+ [InlineData("A", 1, "A")]
+ [InlineData("A", 2, "A ")]
+ [InlineData("AB", 0, "")]
+ [InlineData("AB", 1, "A")]
+ [InlineData("AB", 2, "AB")]
+ [InlineData("AB", 4, "AB ")]
+ public void LSet(string source, int length, string expected)
+ {
+ Assert.Equal(expected, Strings.LSet(source, length));
+ }
+
+ [Theory]
+ [InlineData(null, 0, "")]
+ [InlineData(null, 1, " ")]
+ [InlineData("", 0, "")]
+ [InlineData("", 1, " ")]
+ [InlineData("A", 0, "")]
+ [InlineData("A", 1, "A")]
+ [InlineData("A", 2, " A")]
+ [InlineData("AB", 0, "")]
+ [InlineData("AB", 1, "A")]
+ [InlineData("AB", 2, "AB")]
+ [InlineData("AB", 4, " AB")]
+ public void RSet(string source, int length, string expected)
+ {
+ Assert.Equal(expected, Strings.RSet(source, length));
+ }
+
[Theory]
[InlineData(null, "")]
[InlineData("", "")]
Assert.Equal(expected, Strings.Trim(str));
}
+ [Theory]
+ [InlineData("", "", null, 1, -1, CompareMethod.Text, null)]
+ [InlineData("", null, "", 1, -1, CompareMethod.Text, null)]
+ [InlineData("", "", "", 1, -1, CompareMethod.Text, null)]
+ [InlineData("ABC", "", "", 1, -1, CompareMethod.Text, "ABC")]
+ [InlineData("ABC", "bc", "23", 1, -1, CompareMethod.Binary, "ABC")]
+ [InlineData("ABC", "BC", "23", 1, -1, CompareMethod.Binary, "A23")]
+ [InlineData("ABC", "bc", "23", 1, -1, CompareMethod.Text, "A23")]
+ [InlineData("abcbc", "bc", "23", 1, -1, CompareMethod.Text, "a2323")]
+ [InlineData("abcbc", "bc", "23", 1, 0, CompareMethod.Text, "abcbc")]
+ [InlineData("abcbc", "bc", "23", 1, 1, CompareMethod.Text, "a23bc")]
+ [InlineData("abc", "bc", "23", 2, -1, CompareMethod.Text, "23")]
+ [InlineData("abc", "bc", "23", 3, -1, CompareMethod.Text, "c")]
+ [InlineData("abc", "bc", "23", 4, -1, CompareMethod.Text, null)]
+ public void Replace(string expression, string find, string replacement, int start, int n, CompareMethod compare, string expected)
+ {
+ Assert.Equal(expected, Strings.Replace(expression, find, replacement, start, n, compare));
+ }
+
+ [Theory]
+ [InlineData(null, null, null, 0, 0, CompareMethod.Text)]
+ [InlineData(null, "", "", 0, 0, CompareMethod.Text)]
+ public void Replace_ArgumentException(string expression, string find, string replacement, int start, int length, CompareMethod compare)
+ {
+ Assert.Throws< ArgumentException>(() => Strings.Replace(expression, find, replacement, start, length, compare));
+ }
+
+ [Theory]
+ [InlineData(0, "")]
+ [InlineData(1, " ")]
+ [InlineData(3, " ")]
+ public void Space(int number, string expected)
+ {
+ Assert.Equal(expected, Strings.Space(number));
+ }
+
+ [Theory]
+ [InlineData(null, null, -1, CompareMethod.Text, new string[] { "" })]
+ [InlineData(null, "", -1, CompareMethod.Text, new string[] { "" })]
+ [InlineData("", null, -1, CompareMethod.Text, new string[] { "" })]
+ [InlineData("", "", -1, CompareMethod.Text, new string[] { "" })]
+ [InlineData("ABC", ",", -1, CompareMethod.Text, new string[] { "ABC" })]
+ [InlineData("A,,BC", ",", -1, CompareMethod.Text, new string[] { "A", "", "BC" })]
+ [InlineData("A,,BC", ",", -1, CompareMethod.Text, new string[] { "A", "", "BC" })]
+ [InlineData("ABC", "b", -1, CompareMethod.Text, new string[] { "A", "C" })]
+ [InlineData("ABC", "b", -1, CompareMethod.Binary, new string[] { "ABC" })]
+ [InlineData("A, B, C", ", ", -1, CompareMethod.Text, new string[] { "A", "B", "C" })]
+ [InlineData("A, B, C", ", ", 1, CompareMethod.Text, new string[] { "A, B, C" })]
+ [InlineData("A, B, C", ", ", 2, CompareMethod.Text, new string[] { "A", "B, C" })]
+ [InlineData("A, B, C", ", ", int.MaxValue, CompareMethod.Text, new string[] { "A", "B", "C" })]
+ public void Split(string expression, string delimiter, int limit, CompareMethod compare, string[] expected)
+ {
+ Assert.Equal(expected, Strings.Split(expression, delimiter, limit, compare));
+ }
+
+ [Theory]
+ [InlineData("A, B, C", ", ", 0, CompareMethod.Text)]
+ public void Split_IndexOutOfRangeException(string expression, string delimiter, int limit, CompareMethod compare)
+ {
+ Assert.Throws< IndexOutOfRangeException>(() => Strings.Split(expression, delimiter, limit, compare));
+ }
+
[Theory]
[InlineData("a", "a", 0, 0)]
[InlineData("a", "b", -1, -1)]
Assert.Equal(expectedTextCompare, Strings.StrComp(left, right, CompareMethod.Text));
}
+ [Theory]
+ [InlineData(null, VbStrConv.None, 0, null)]
+ [InlineData("", VbStrConv.None, 0, "")]
+ [InlineData("ABC123", VbStrConv.None, 0, "ABC123")]
+ [InlineData("", VbStrConv.Lowercase, 0, "")]
+ [InlineData("Abc123", VbStrConv.Lowercase, 0, "abc123")]
+ [InlineData("Abc123", VbStrConv.Uppercase, 0, "ABC123")]
+ public void StrConv(string str, Microsoft.VisualBasic.VbStrConv conversion, int localeID, string expected)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ Assert.Equal(expected, Strings.StrConv(str, conversion, localeID));
+ }
+ else
+ {
+ Assert.Throws<PlatformNotSupportedException>(() => Strings.StrConv(str, conversion, localeID));
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(StrDup_Object_TestData))]
+ [MemberData(nameof(StrDup_Char_TestData))]
+ [MemberData(nameof(StrDup_String_TestData))]
+ public void StrDup(int number, object character, object expected)
+ {
+ Assert.Equal(expected, Strings.StrDup(number, character));
+ }
+
+ [Theory]
+ [MemberData(nameof(StrDup_Object_ArgumentException_TestData))]
+ public void StrDup_ArgumentException(int number, object character)
+ {
+ Assert.Throws< ArgumentException>(() => Strings.StrDup(number, character));
+ }
+
+ [Theory]
+ [MemberData(nameof(StrDup_Char_TestData))]
+ public void StrDup(int number, char character, string expected)
+ {
+ Assert.Equal(expected, Strings.StrDup(number, character));
+ }
+
+ [Theory]
+ [MemberData(nameof(StrDup_Char_ArgumentException_TestData))]
+ public void StrDup_ArgumentException(int number, char character)
+ {
+ Assert.Throws<ArgumentException>(() => Strings.StrDup(number, character));
+ }
+
+ [Theory]
+ [MemberData(nameof(StrDup_String_TestData))]
+ public void StrDup(int number, string character, string expected)
+ {
+ Assert.Equal(expected, Strings.StrDup(number, character));
+ }
+
+ [Theory]
+ [MemberData(nameof(StrDup_String_ArgumentException_TestData))]
+ public void StrDup_ArgumentException(int number, string character)
+ {
+ Assert.Throws<ArgumentException>(() => Strings.StrDup(number, character));
+ }
+
+ private static IEnumerable<object[]> StrDup_Object_TestData()
+ {
+ yield break;
+ }
+
+ private static IEnumerable<object[]> StrDup_Char_TestData()
+ {
+ yield return new object[] { 3, '\0', "\0\0\0" };
+ yield return new object[] { 0, 'A', "" };
+ yield return new object[] { 1, 'A', "A" };
+ yield return new object[] { 3, 'A', "AAA" };
+ }
+
+ private static IEnumerable<object[]> StrDup_String_TestData()
+ {
+ yield return new object[] { 0, "A", "" };
+ yield return new object[] { 1, "A", "A" };
+ yield return new object[] { 3, "A", "AAA" };
+ yield return new object[] { 0, "ABC", "" };
+ yield return new object[] { 1, "ABC", "A" };
+ yield return new object[] { 3, "ABC", "AAA" };
+ }
+
+ private static IEnumerable<object[]> StrDup_Object_ArgumentException_TestData()
+ {
+ yield return new object[] { -1, new object() };
+ yield return new object[] { 1, 0 };
+ yield return new object[] { 1, (int)'A' };
+ yield return new object[] { -1, 'A' };
+ yield return new object[] { -1, "A" };
+ yield return new object[] { 1, "" };
+ }
+
+ private static IEnumerable<object[]> StrDup_Char_ArgumentException_TestData()
+ {
+ yield return new object[] { -1, 'A' };
+ }
+
+ private static IEnumerable<object[]> StrDup_String_ArgumentException_TestData()
+ {
+ yield return new object[] { -1, "A" };
+ yield return new object[] { 1, null };
+ yield return new object[] { 1, "" };
+ }
+
[Theory]
[InlineData(null, "")]
[InlineData("", "")]
{ "aa", "ab", 1, 0 },
{ "abab", "ab", 3, 1 },
};
+
+ private static bool IsEnUS() => System.Threading.Thread.CurrentThread.CurrentUICulture.Name == "en-US";
}
}