1 #Region "Microsoft.VisualBasic::befe620a7db53198a08198cf6b5207f4, Microsoft.VisualBasic.Core\Scripting\Expressions\Selector.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module Selector
35     
36     '         Function: (+3 Overloads) [Select]
37     '         Delegate Function
38     
39     '             Function: [Select], ParseExpression
40     
41     
42     
43     ' /********************************************************************************/
44
45 #End Region
46
47 Imports System.Reflection
48 Imports System.Runtime.CompilerServices
49 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
50 Imports Microsoft.VisualBasic.Emit.Marshal
51 Imports Microsoft.VisualBasic.Language
52 Imports Microsoft.VisualBasic.Scripting.Runtime
53
54 Namespace Scripting.Expressions
55
56     ''' <summary>
57     ''' The property value selector
58     ''' </summary>
59     Public Module Selector
60
61         ''' <summary>
62         ''' Select property value from target source collection by using a specific property name
63         ''' </summary>
64         ''' <param name="source"></param>
65         ''' <param name="type"></param>
66         ''' <param name="propertyName$"></param>
67         ''' <returns></returns>
68         <Extension>
69         Public Iterator Function [Select](source As IEnumerable, type As Type, propertyName$) As IEnumerable(Of Object)
70             Dim [property] As PropertyInfo =
71                 type _
72                 .GetProperties(BindingFlags.Public Or BindingFlags.Instance) _
73                 .Where(Function(prop) prop.Name.TextEquals([propertyName])) _
74                 .FirstOrDefault
75
76             For Each o As Object In source
77                 Yield [property].GetValue(o, Nothing)
78             Next
79         End Function
80
81         ''' <summary>
82         ''' Select property value from target source collection by using a specific property name and with a specific type casting constraint.
83         ''' </summary>
84         ''' <typeparam name="T"></typeparam>
85         ''' <param name="source"></param>
86         ''' <param name="type"></param>
87         ''' <param name="propertyName$"></param>
88         ''' <returns></returns>
89         <Extension>
90         Public Function [Select](Of T)(source As IEnumerable, type As Type, propertyName$) As IEnumerable(Of T)
91             Return source.Select(type, propertyName).Select(Function(o) DirectCast(o, T))
92         End Function
93
94         ''' <summary>
95         ''' Select all of the object elements their a specific property value and convert to a specific value type.
96         ''' (将对象类型之中的某一个属性筛选出来,然后转换为指定的数据类型)
97         ''' </summary>
98         ''' <typeparam name="T"></typeparam>
99         ''' <typeparam name="V"></typeparam>
100         ''' <param name="source"></param>
101         ''' <param name="propertyName$">
102         ''' If the property name is ``$`` expression, that it means makes a type casting on it self.
103         ''' (如果属性名称为``$``,即引用自身,则这个函数的作用只是进行强制的``CType``类型转换)
104         ''' </param>
105         ''' <returns></returns>
106         <Extension>
107         Public Function [Select](Of T, V)(source As IEnumerable(Of T), propertyName$) As IEnumerable(Of V)
108             If propertyName = "$" Then
109                 Return source.Select(Function(o) CType(CObj(o), V))
110             Else
111                 Return source.Select(GetType(T), propertyName).Select(Function(o) DirectCast(o, V))
112             End If
113         End Function
114
115         ''' <summary>
116         ''' The object value selector function pointer template
117         ''' </summary>
118         ''' <typeparam name="T"></typeparam>
119         ''' <param name="property$"></param>
120         ''' <param name="type"></param>
121         ''' <returns></returns>
122         Public Delegate Function Selector(Of T)(property$, ByRef type As Type) As Func(Of T, Object)
123
124         ''' <summary>
125         ''' Where selector.(这个函数之中只有数字和字符串的比较)
126         ''' </summary>
127         ''' <typeparam name="T"></typeparam>
128         ''' <param name="source"></param>
129         ''' <param name="expression$">
130         ''' ###### propertyName operator value
131         ''' 
132         ''' 1. ``a = b``
133         ''' 2. ``a > b``
134         ''' 3. ``a &lt; b``
135         ''' 4. ``a => b``
136         ''' 5. ``a &lt;= b``
137         ''' 4. ``a IN b``
138         ''' 
139         ''' ``$``符号表示对象自身
140         ''' </param>
141         ''' <returns></returns>
142         <Extension>
143         Public Function [Select](Of T)(source As IEnumerable(Of T), expression$, Optional selector As Selector(Of T) = NothingAs IEnumerable(Of T)
144             Dim type As Type = GetType(T)
145             Dim expr As NamedValue(Of String) = expression.ParseExpression
146             Dim value As Object
147             Dim compare As Func(Of T, Boolean)
148
149             With expr
150                 Dim getValue As Func(Of T, Object)
151
152                 If selector Is Nothing Then
153                     If .Name = "$" Then
154                         getValue = Function(x) x
155                     Else
156                         Dim [property] As PropertyInfo =
157                             type _
158                             .GetProperties(BindingFlags.Public Or BindingFlags.Instance) _
159                             .Where(Function(prop) prop.Name.TextEquals(expr.Name)) _
160                             .FirstOrDefault
161                         type = [property].PropertyType
162                         getValue = Function(x)
163                                        Return [property].GetValue(x)
164                                    End Function
165                     End If
166                 Else
167                     getValue = selector(.Name, type)
168                 End If
169
170                 value = .Value.CTypeDynamic(type)
171
172                 If .Description = "=" Then
173                     compare = Function(o) getValue(o).Equals(value)
174                 ElseIf .Description.TextEquals("IN"Then
175                     ' 字符串查找
176                     Dim s$ = CStrSafe(value)
177                     compare = Function(o) InStr(s, CStrSafe(getValue(o))) > 0
178                 Else
179                     Dim icompareValue = Val(value) DirectCast(value, IComparable)
180
181                     If .Description = ">" Then
182                         compare = Function(o)
183                                       Return Val(getValue(o)) > (icompareValue)
184                                   End Function
185                     ElseIf .Description = "<" Then
186                         compare = Function(o)
187                                       Return Val(getValue(o)) < (icompareValue)
188                                   End Function
189                     ElseIf .Description = "=>" Then
190                         compare = Function(o)
191                                       Return Val(getValue(o)) >= (icompareValue)
192                                   End Function
193                     ElseIf .Description = "<=" Then
194                         compare = Function(o)
195                                       Return Val(getValue(o)) <= (icompareValue)
196                                   End Function
197                     Else
198                         Throw New NotSupportedException(expression)
199                     End If
200                 End If
201             End With
202
203             Return source.Where(predicate:=compare)
204         End Function
205
206         <Extension>
207         Public Function ParseExpression(expression$) As NamedValue(Of String)
208             Dim tmp As New List(Of Char)
209             Dim l As New List(Of String)
210             Dim source As New Pointer(Of Char)(expression)
211
212             Do While Not source.EndRead
213                 Dim c As Char = +source
214
215                 If c <> " "Then
216                     tmp += c
217                 Else
218                     l += New String(tmp)
219                     tmp *= 0
220
221                     If l.Count = 2 Then
222                         l += New String(source.RawBuffer.Skip(source.Position).ToArray)
223                         Exit Do
224                     End If
225                 End If
226             Loop
227
228             If l.Count <> 3 Then
229                 Throw New SyntaxErrorException(expression)
230             End If
231
232             Return New NamedValue(Of StringWith {
233                 .Name = l(Scan0),
234                 .Description = l(1),
235                 .Value = l.Last
236             }
237         End Function
238     End Module
239 End Namespace