Imports System.Text
Imports System.Text.RegularExpressions
Public Class NumericSorter
#Region "定数"
Private Const AddColumnString As String = "_NUMBERSTRINGSORTCOLUMN"
#End Region
#Region "コンストラクタ"
Private Sub New()
'インスタンス作成禁止
End Sub
#End Region
#Region "共有メソッド"
Public Shared Function Sort(ByVal table As DataTable, ByVal sortString As String) As DataTable
Dim sorts() As String = sortString.Split(","c)
Dim sortItems As New ArrayList
'Sort情報アイテム作成
For Each sortValue As String In sorts
Dim value() As String = sortValue.Trim.Split(" "c)
If 1 < value.Length Then
sortItems.Add(New sortItem(value(0).TrimStart("["c).TrimEnd("]"c), value(1)))
Else
sortItems.Add(New sortItem(value(0).TrimStart("["c).TrimEnd("]"c)))
End If
Next
'Sort用カラム追加
For Each item As sortItem In sortItems
'Column存在チェック
If Not table.Columns.Contains(item.ColumnName) Then
Throw New IndexOutOfRangeException(String.Concat("列:", item.ColumnName, "はテーブルに属していません。"))
End If
'Sort用Column追加
Dim addColumnName As String = String.Concat(item.ColumnName, AddColumnString)
If Not table.Columns.Contains(addColumnName) Then
table.Columns.Add(addColumnName, GetType(NumericCompareData))
Else
table.Columns(addColumnName).ReadOnly = False
End If
For Each row As DataRow In table.Select("")
row.Item(addColumnName) = New NumericCompareData(row.Item(item.ColumnName))
Next
table.Columns(addColumnName).ReadOnly = True
Next
'Sort文字列作成
Dim sb As New StringBuilder
Dim isFirst As Boolean = True
For Each item As sortItem In sortItems
If Not isFirst Then
sb.Append(",")
End If
sb.Append(item.ColumnName)
sb.Append(AddColumnString)
sb.Append(" ")
sb.Append(item.AscString)
isFirst = False
Next
'Sort文字列設定
table.DefaultView.Sort = sb.ToString
Return table
End Function
#End Region
#Region "サブクラス"
#Region "sortItem"
Private Class sortItem
Private iIsAsc As Boolean
Private iAscString As String
Private iColumnName As String
Public Sub New(ByVal columnName As String, ByVal ascString As String)
If String.Compare(ascString, "DESC", True) = 0 Then
iIsAsc = False
Else
iIsAsc = True
End If
iAscString = ascString
iColumnName = columnName
End Sub
Public Sub New(ByVal columnName As String)
iColumnName = columnName
End Sub
Public Property IsAsc() As Boolean
Get
Return iIsAsc
End Get
Set(ByVal Value As Boolean)
iIsAsc = Value
End Set
End Property
Public Property ColumnName() As String
Get
Return iColumnName
End Get
Set(ByVal Value As String)
iColumnName = Value
End Set
End Property
Public Property AscString() As String
Get
Return iAscString
End Get
Set(ByVal Value As String)
iAscString = Value
End Set
End Property
Public Overrides Function ToString() As String
Return String.Concat(iColumnName, " ", iAscString)
End Function
End Class
#End Region
#Region "NumericCompareData"
Public Class NumericCompareData : Implements IComparable
Private iValue As String
Private Shared iComparer As New NumericComparer
Public Sub New(ByVal value As String)
iValue = value
End Sub
Public Sub New(ByVal value As Object)
iValue = value.ToString
End Sub
Public Property Value() As String
Get
Return iValue
End Get
Set(ByVal Value As String)
iValue = Value
End Set
End Property
Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
If TypeOf obj Is NumericCompareData Then
Return iComparer.Compare(iValue, DirectCast(obj, NumericCompareData).Value)
Else
Return 0
End If
End Function
Public Overrides Function ToString() As String
Return iValue
End Function
End Class
#End Region
#Region "NumericIComparer"
Public Class NumericComparer : Implements IComparer
Private iNumberRegex As New Regex("(?<number>[0-9\.]*)(?<string>.*)")
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
Dim xNumber, yNumber As Double
Dim xString, yString As String
Dim xBool, yBool As Boolean
xBool = getNumber(x.ToString, xNumber, xString)
yBool = getNumber(y.ToString, yNumber, yString)
If xBool AndAlso yBool AndAlso (Not IsNothing(xString)) And (Not IsNothing(yString)) Then
Dim rtnCompareNumber As Integer = xNumber.CompareTo(yNumber)
If rtnCompareNumber <> 0 Then
Return rtnCompareNumber
Else
Dim rtnCompareString As Integer = xString.CompareTo(yString)
Return rtnCompareString
End If
ElseIf xBool Or IsNothing(yBool) Then
Return -1
ElseIf yBool Or IsNothing(xBool) Then
Return 1
Else
Return 0
End If
End Function
Private Function getNumber(ByVal str As String, ByRef matchNumber As Double, ByRef matchString As String) As Boolean
Dim numberMatch As Match = iNumberRegex.Match(str)
If numberMatch.Success Then
Dim matchNumberString As String = numberMatch.Groups("number").ToString
If matchNumberString <> "" Then
matchNumber = CType(matchNumberString, Double)
Else
matchNumber = Double.MaxValue
End If
matchString = numberMatch.Groups("string").ToString
Return True
Else
Return False
End If
End Function
End Class
#End Region
#End Region
End Class
|