1 | #Region "Microsoft.VisualBasic::14407b1f2769d73e591b8092ffaade2b, Microsoft.VisualBasic.Core\CommandLine\InteropService\CLIBuilder.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 CLIBuildMethod |
35 | ' |
36 | ' Function: GetCLI, (+2 Overloads) GetPrefix, SimpleBuilder |
37 | ' Delegate Function |
38 | ' |
39 | ' Function: booleanRule, ClearParameters, pathRule, stringEnumRule, stringRule |
40 | ' |
41 | ' |
42 | ' |
43 | ' /********************************************************************************/ |
44 | |
45 | #End Region |
46 | |
47 | Imports System.ComponentModel |
48 | Imports System.Reflection |
49 | Imports System.Runtime.CompilerServices |
50 | Imports System.Text |
51 | Imports Microsoft.VisualBasic.ApplicationServices |
52 | Imports Microsoft.VisualBasic.CommandLine.Reflection |
53 | Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel.SchemaMaps |
54 | Imports Microsoft.VisualBasic.Language |
55 | |
56 | Namespace CommandLine.InteropService |
57 | |
58 | Public Module CLIBuildMethod |
59 | |
60 | ReadOnly Argument As Type = GetType(Argv) |
61 | |
62 | <Extension> |
63 | Public Function GetPrefix([property] As PropertyInfo) As String |
64 | With [property].GetCustomAttribute(Of Prefix) |
65 | If .IsNothing Then |
66 | Return Nothing |
67 | Else |
68 | Return .Value |
69 | End If |
70 | End With |
71 | End Function |
72 | |
73 | <Extension> |
74 | Public Function GetPrefix(Of T As Class)(obj As T) As String |
75 | With obj.GetType.GetCustomAttribute(Of Prefix) |
76 | If .IsNothing Then |
77 | Return Nothing |
78 | Else |
79 | Return .Value |
80 | End If |
81 | End With |
82 | End Function |
83 | |
84 | ''' <summary> |
85 | ''' Generates the command line string value for the invoked target cli program using this interop services object instance. |
86 | ''' (生成命令行参数) |
87 | ''' </summary> |
88 | ''' <typeparam name="TInteropService"> |
89 | ''' A class type object for interaction with a commandline program. |
90 | ''' (与命令行程序进行交互的模块对象类型) |
91 | ''' </typeparam> |
92 | ''' <param name="app">目标交互对象的实例</param> |
93 | ''' <returns></returns> |
94 | ''' <remarks> |
95 | ''' 依照类型<see cref="CLITypes"/>来生成参数字符串 |
96 | ''' |
97 | ''' <see cref="CLITypes.Boolean"/>, True => 参数名; |
98 | ''' <see cref="CLITypes.Double"/>, <see cref="CLITypes.Integer"/>, <see cref="CLITypes.String"/>, => 参数名 + 参数值,假若字符串为空则不添加; |
99 | ''' (假若是枚举值类型,可能还需要再枚举值之中添加<see cref="DescriptionAttribute"/>属性) |
100 | ''' <see cref="CLITypes.File"/>, 假若字符串为空则不添加,有空格自动添加双引号,相对路径会自动转换为全路径。 |
101 | ''' </remarks> |
102 | <Extension> |
103 | Public Function GetCLI(Of TInteropService As Class)(app As TInteropService, Optional prefix$ = "") As String |
104 | Dim prop As PropertyInfo = Nothing |
105 | Dim argv As Argv |
106 | Dim args As BindProperty(Of Argv)() = LinqAPI.Exec(Of BindProperty(Of Argv)) _ |
107 | _ |
108 | () <= From [property] As PropertyInfo |
109 | In GetType(TInteropService).GetProperties() |
110 | Let attrs As Object() = [property].GetCustomAttributes(attributeType:=Argument, inherit:=True) |
111 | Where Not attrs.IsNullOrEmpty |
112 | Let attr As Argv = DirectCast(attrs.First, Argv) |
113 | Select New BindProperty(Of Argv)( |
114 | attr:=attr, |
115 | prop:=[property], |
116 | getName:=Function(a) a.Name |
117 | ) |
118 | |
119 | Dim sb As New StringBuilder() |
120 | |
121 | ' undefined类型需要用户自己进行处理 |
122 | For Each argum As BindProperty(Of Argv) In args.Where(Function(a) a.field.Type <> CLITypes.Undefined) |
123 | Dim getCLIToken As getValue = convertMethods(argum.field.Type) |
124 | Dim value As Object = argum.GetValue(app) |
125 | |
126 | ' integer, double这些类型都用nullable类型 |
127 | ' 所以如果value是nothing,就说明该属性肯定没有赋值 |
128 | If value Is Nothing Then |
129 | ' 如果是nothing则表示没有赋值 |
130 | ' 跳过 |
131 | Continue For |
132 | Else |
133 | prop = DirectCast(argum.member, PropertyInfo) |
134 | argv = argum.field |
135 | End If |
136 | |
137 | With getCLIToken(value, argv, prop) |
138 | ' 有些类型是会返回空字符串结果的 |
139 | ' 例如boolean类型的数据,false的时候是返回空字符串 |
140 | ' 所以会需要在这里判断一下 |
141 | |
142 | If Not .StringEmpty Then |
143 | ' 如果prefix参数不为空,则会添加统一的前缀 |
144 | sb.AppendLine(prefix & .ByRef) |
145 | End If |
146 | End With |
147 | Next |
148 | |
149 | Return sb.ToString.TrimEnd |
150 | End Function |
151 | |
152 | ''' <summary> |
153 | ''' Creates a command line string by using simply fills the name and parameter values |
154 | ''' </summary> |
155 | ''' <param name="name"></param> |
156 | ''' <param name="args"></param> |
157 | ''' <returns></returns> |
158 | Public Function SimpleBuilder(name$, args As IEnumerable(Of KeyValuePair(Of String, String))) As String |
159 | Dim sbr As New StringBuilder(name) |
160 | |
161 | For Each x In args |
162 | If String.IsNullOrEmpty(x.Value) Then |
163 | Continue For |
164 | End If |
165 | |
166 | Call sbr.Append(" ") |
167 | Call sbr.Append(x.Key & " ") |
168 | Call sbr.Append(x.Value.CLIToken) |
169 | Next |
170 | |
171 | Return sbr.ToString |
172 | End Function |
173 | |
174 | #Region "" |
175 | |
176 | ''' <summary> |
177 | ''' Converts the property value to a CLI token |
178 | ''' </summary> |
179 | ReadOnly convertMethods As New Dictionary(Of CLITypes, getValue) From { |
180 | _ |
181 | {CLITypes.Boolean, AddressOf CLIBuildMethod.booleanRule}, |
182 | {CLITypes.Double, AddressOf CLIBuildMethod.stringRule}, |
183 | {CLITypes.File, AddressOf CLIBuildMethod.pathRule}, |
184 | {CLITypes.Integer, AddressOf CLIBuildMethod.stringRule}, |
185 | {CLITypes.String, AddressOf CLIBuildMethod.stringRule} |
186 | } |
187 | |
188 | ''' <summary> |
189 | ''' Convert property to cli parameter value string |
190 | ''' </summary> |
191 | ''' <param name="value"></param> |
192 | ''' <param name="attr"></param> |
193 | ''' <param name="prop"></param> |
194 | ''' <returns></returns> |
195 | Private Delegate Function getValue(value As Object, attr As Argv, prop As PropertyInfo) As String |
196 | |
197 | ''' <summary> |
198 | ''' The different between the String and Path is that applying <see cref="CLIToken"/> or <see cref="CLIPath"/>. |
199 | ''' </summary> |
200 | ''' <param name="value">只能是<see cref="System.String"/>类型的</param> |
201 | ''' <param name="attr"></param> |
202 | ''' <param name="prop"></param> |
203 | ''' <returns></returns> |
204 | Private Function pathRule(value As Object, attr As Argv, prop As PropertyInfo) As String |
205 | Dim path As String = DirectCast(value, String) |
206 | |
207 | If Not String.IsNullOrEmpty(path) Then |
208 | path = $"{attr.Name} {path.CLIPath}" |
209 | End If |
210 | |
211 | Return path |
212 | End Function |
213 | |
214 | ''' <summary> |
215 | ''' 可能包含有枚举值 |
216 | ''' </summary> |
217 | ''' <param name="value"></param> |
218 | ''' <param name="attr"></param> |
219 | ''' <param name="prop"></param> |
220 | ''' <returns></returns> |
221 | Private Function stringRule(value As Object, attr As Argv, prop As PropertyInfo) As String |
222 | If prop.PropertyType.Equals(GetType(String)) Then |
223 | Dim str As String = Scripting.ToString(value) |
224 | |
225 | If String.IsNullOrEmpty(str) Then |
226 | Return "" |
227 | Else |
228 | Return $"{attr.Name} {str.CLIToken}" |
229 | End If |
230 | ElseIf prop.PropertyType.IsInheritsFrom(GetType([Enum])) Then |
231 | Return stringEnumRule(value, attr, prop) |
232 | Else |
233 | Dim str As String = Scripting.ToString(value) |
234 | |
235 | Return $"{attr.Name} {str}" |
236 | End If |
237 | End Function |
238 | |
239 | ''' <summary> |
240 | ''' 将枚举类型的属性值转换为命令行的参数值,这个转换过程与<see cref="Argv.Type"/>的值相关 |
241 | ''' |
242 | ''' + <see cref="CLITypes.String"/>的时候,会直接调用ToString生成参数值 |
243 | ''' + <see cref="CLITypes.Integer"/>的时候,会将枚举值的数值作为命令行参数值 |
244 | ''' |
245 | ''' </summary> |
246 | ''' <param name="value"></param> |
247 | ''' <param name="attr"></param> |
248 | ''' <param name="prop"></param> |
249 | ''' <returns></returns> |
250 | Private Function stringEnumRule(value As Object, attr As Argv, prop As PropertyInfo) As String |
251 | Dim enumValue As [Enum] = DirectCast(value, [Enum]) |
252 | |
253 | If attr.Type = CLITypes.String Then |
254 | Return $"{attr.Name} {enumValue.Description.CLIToken}" |
255 | ElseIf attr.Type = CLITypes.Integer Then |
256 | Return $"{attr.Name} {Convert.ToInt32(enumValue)}" |
257 | Else |
258 | Throw New InvalidCastException($"Unable cast {enumValue.GetType.FullName} enum value to such type: {attr.Type.ToString}") |
259 | End If |
260 | End Function |
261 | |
262 | ''' <summary> |
263 | ''' Property value to boolean flag in the CLI |
264 | ''' </summary> |
265 | ''' <param name="value"></param> |
266 | ''' <param name="attr"></param> |
267 | ''' <param name="prop"></param> |
268 | ''' <returns></returns> |
269 | Private Function booleanRule(value As Object, attr As Argv, prop As PropertyInfo) As String |
270 | Dim name As String = attr.Name |
271 | Dim b As Boolean |
272 | |
273 | If prop.PropertyType.Equals(GetType(Boolean)) Then |
274 | b = DirectCast(value, Boolean) |
275 | Else |
276 | b = Scripting.ToString(value).ParseBoolean |
277 | End If |
278 | |
279 | If b Then |
280 | Return name |
281 | Else |
282 | Return "" |
283 | End If |
284 | End Function |
285 | #End Region |
286 | |
287 | ''' <summary> |
288 | ''' Reset the CLI parameters property in the target class object. |
289 | ''' </summary> |
290 | ''' <typeparam name="TInteropService"></typeparam> |
291 | ''' <param name="inst"></param> |
292 | ''' <returns>返回所重置的参数的个数</returns> |
293 | ''' <remarks></remarks> |
294 | Public Function ClearParameters(Of TInteropService As Class)(inst As TInteropService) As Integer |
295 | Dim n As Integer |
296 | Dim properties As PropertyInfo() = inst.GetType().GetProperties() |
297 | |
298 | Try |
299 | For Each [property] As PropertyInfo In properties |
300 | Dim attrs As Object() = [property].GetCustomAttributes(Argument, inherit:=False) |
301 | |
302 | If Not (attrs Is Nothing OrElse attrs.Length = 0) Then |
303 | Call [property].SetValue(inst, Nothing, Nothing) |
304 | n += 1 |
305 | End If |
306 | Next |
307 | Catch ex As Exception |
308 | Throw New InvalidOperationException(InvalidOperation) |
309 | End Try |
310 | |
311 | Return n |
312 | End Function |
313 | |
314 | Const InvalidOperation$ = "The target type information is not the 'System.String'!" |
315 | End Module |
316 | End Namespace |