2018-09-08 01:26:45 +08:00
|
|
|
|
#Region "Microsoft.VisualBasic::2444e09cd97a4a154b5b102511f65a4b, Microsoft.VisualBasic.Core\CommandLine\InteropService\CLIBuilder.vb"
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
2018-10-25 18:46:39 +08:00
|
|
|
|
' Author:
|
|
|
|
|
|
'
|
|
|
|
|
|
' asuka (amethyst.asuka@gcmodeller.org)
|
|
|
|
|
|
' xie (genetics@smrucc.org)
|
|
|
|
|
|
' xieguigang (xie.guigang@live.com)
|
|
|
|
|
|
'
|
|
|
|
|
|
' Copyright (c) 2018 GPL3 Licensed
|
|
|
|
|
|
'
|
|
|
|
|
|
'
|
|
|
|
|
|
' GNU GENERAL PUBLIC LICENSE (GPL3)
|
|
|
|
|
|
'
|
|
|
|
|
|
'
|
|
|
|
|
|
' This program is free software: you can redistribute it and/or modify
|
|
|
|
|
|
' it under the terms of the GNU General Public License as published by
|
|
|
|
|
|
' the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
|
' (at your option) any later version.
|
|
|
|
|
|
'
|
|
|
|
|
|
' This program is distributed in the hope that it will be useful,
|
|
|
|
|
|
' but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
' GNU General Public License for more details.
|
|
|
|
|
|
'
|
|
|
|
|
|
' You should have received a copy of the GNU General Public License
|
|
|
|
|
|
' along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
' /********************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
' Summaries:
|
|
|
|
|
|
|
|
|
|
|
|
' Module CLIBuildMethod
|
|
|
|
|
|
'
|
|
|
|
|
|
' Function: GetCLI, SimpleBuilder
|
|
|
|
|
|
' Delegate Function
|
|
|
|
|
|
'
|
|
|
|
|
|
' Function: __booleanRule, __pathRule, __stringEnumRule, __stringRule, ClearParameters
|
|
|
|
|
|
'
|
|
|
|
|
|
'
|
|
|
|
|
|
'
|
|
|
|
|
|
' /********************************************************************************/
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
#End Region
|
|
|
|
|
|
|
|
|
|
|
|
Imports System.ComponentModel
|
|
|
|
|
|
Imports System.Reflection
|
|
|
|
|
|
Imports System.Runtime.CompilerServices
|
|
|
|
|
|
Imports System.Text
|
|
|
|
|
|
Imports Microsoft.VisualBasic.ApplicationServices
|
|
|
|
|
|
Imports Microsoft.VisualBasic.CommandLine.Reflection
|
|
|
|
|
|
Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel.SchemaMaps
|
|
|
|
|
|
Imports Microsoft.VisualBasic.Language
|
|
|
|
|
|
|
|
|
|
|
|
Namespace CommandLine.InteropService
|
|
|
|
|
|
|
|
|
|
|
|
Public Module CLIBuildMethod
|
|
|
|
|
|
|
2018-10-25 18:46:39 +08:00
|
|
|
|
ReadOnly Argument As Type = GetType(Argv)
|
|
|
|
|
|
|
|
|
|
|
|
<Extension>
|
|
|
|
|
|
Public Function GetPrefix([property] As PropertyInfo) As String
|
|
|
|
|
|
With [property].GetCustomAttribute(Of Prefix)
|
|
|
|
|
|
If .IsNothing Then
|
|
|
|
|
|
Return Nothing
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return .Value
|
|
|
|
|
|
End If
|
|
|
|
|
|
End With
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
<Extension>
|
|
|
|
|
|
Public Function GetPrefix(Of T As Class)(obj As T) As String
|
|
|
|
|
|
With obj.GetType.GetCustomAttribute(Of Prefix)
|
|
|
|
|
|
If .IsNothing Then
|
|
|
|
|
|
Return Nothing
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return .Value
|
|
|
|
|
|
End If
|
|
|
|
|
|
End With
|
|
|
|
|
|
End Function
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Generates the command line string value for the invoked target cli program using this interop services object instance.
|
|
|
|
|
|
''' (生成命令行参数)
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <typeparam name="TInteropService">
|
|
|
|
|
|
''' A class type object for interaction with a commandline program.
|
|
|
|
|
|
''' (与命令行程序进行交互的模块对象类型)
|
|
|
|
|
|
''' </typeparam>
|
|
|
|
|
|
''' <param name="app">目标交互对象的实例</param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
''' <remarks>
|
|
|
|
|
|
''' 依照类型<see cref="CLITypes"/>来生成参数字符串
|
|
|
|
|
|
'''
|
|
|
|
|
|
''' <see cref="CLITypes.Boolean"/>, True => 参数名;
|
|
|
|
|
|
''' <see cref="CLITypes.Double"/>, <see cref="CLITypes.Integer"/>, <see cref="CLITypes.String"/>, => 参数名 + 参数值,假若字符串为空则不添加;
|
|
|
|
|
|
''' (假若是枚举值类型,可能还需要再枚举值之中添加<see cref="DescriptionAttribute"/>属性)
|
|
|
|
|
|
''' <see cref="CLITypes.File"/>, 假若字符串为空则不添加,有空格自动添加双引号,相对路径会自动转换为全路径。
|
|
|
|
|
|
''' </remarks>
|
|
|
|
|
|
<Extension>
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Public Function GetCLI(Of TInteropService As Class)(app As TInteropService, Optional prefix$ = "") As String
|
|
|
|
|
|
Dim prop As PropertyInfo = Nothing
|
|
|
|
|
|
Dim argv As Argv
|
|
|
|
|
|
Dim args As BindProperty(Of Argv)() = LinqAPI.Exec(Of BindProperty(Of Argv)) _
|
2018-08-02 20:14:48 +08:00
|
|
|
|
_
|
2018-10-25 18:46:39 +08:00
|
|
|
|
() <= From [property] As PropertyInfo
|
|
|
|
|
|
In GetType(TInteropService).GetProperties()
|
|
|
|
|
|
Let attrs As Object() = [property].GetCustomAttributes(attributeType:=Argument, inherit:=True)
|
|
|
|
|
|
Where Not attrs.IsNullOrEmpty
|
|
|
|
|
|
Let attr As Argv = DirectCast(attrs.First, Argv)
|
|
|
|
|
|
Select New BindProperty(Of Argv)(
|
|
|
|
|
|
attr:=attr,
|
|
|
|
|
|
prop:=[property],
|
|
|
|
|
|
getName:=Function(a) a.Name
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
Dim sb As New StringBuilder()
|
|
|
|
|
|
|
|
|
|
|
|
' undefined类型需要用户自己进行处理
|
|
|
|
|
|
For Each argum As BindProperty(Of Argv) In args.Where(Function(a) a.field.Type <> CLITypes.Undefined)
|
|
|
|
|
|
Dim getCLIToken As getValue = convertMethods(argum.field.Type)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Dim value As Object = argum.GetValue(app)
|
|
|
|
|
|
|
2018-10-25 18:46:39 +08:00
|
|
|
|
' integer, double这些类型都用nullable类型
|
|
|
|
|
|
' 所以如果value是nothing,就说明该属性肯定没有赋值
|
|
|
|
|
|
If value Is Nothing Then
|
|
|
|
|
|
' 如果是nothing则表示没有赋值
|
|
|
|
|
|
' 跳过
|
|
|
|
|
|
Continue For
|
|
|
|
|
|
Else
|
|
|
|
|
|
prop = DirectCast(argum.member, PropertyInfo)
|
|
|
|
|
|
argv = argum.field
|
2018-08-02 20:14:48 +08:00
|
|
|
|
End If
|
2018-10-25 18:46:39 +08:00
|
|
|
|
|
|
|
|
|
|
With getCLIToken(value, argv, prop)
|
|
|
|
|
|
' 有些类型是会返回空字符串结果的
|
|
|
|
|
|
' 例如boolean类型的数据,false的时候是返回空字符串
|
|
|
|
|
|
' 所以会需要在这里判断一下
|
|
|
|
|
|
|
|
|
|
|
|
If Not .StringEmpty Then
|
|
|
|
|
|
' 如果prefix参数不为空,则会添加统一的前缀
|
|
|
|
|
|
sb.AppendLine(prefix & .ByRef)
|
|
|
|
|
|
End If
|
|
|
|
|
|
End With
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Next
|
|
|
|
|
|
|
|
|
|
|
|
Return sb.ToString.TrimEnd
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Creates a command line string by using simply fills the name and parameter values
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="name"></param>
|
|
|
|
|
|
''' <param name="args"></param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
Public Function SimpleBuilder(name$, args As IEnumerable(Of KeyValuePair(Of String, String))) As String
|
|
|
|
|
|
Dim sbr As New StringBuilder(name)
|
|
|
|
|
|
|
|
|
|
|
|
For Each x In args
|
|
|
|
|
|
If String.IsNullOrEmpty(x.Value) Then
|
|
|
|
|
|
Continue For
|
|
|
|
|
|
End If
|
|
|
|
|
|
|
|
|
|
|
|
Call sbr.Append(" ")
|
|
|
|
|
|
Call sbr.Append(x.Key & " ")
|
|
|
|
|
|
Call sbr.Append(x.Value.CLIToken)
|
|
|
|
|
|
Next
|
|
|
|
|
|
|
|
|
|
|
|
Return sbr.ToString
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
#Region ""
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Converts the property value to a CLI token
|
|
|
|
|
|
''' </summary>
|
2018-10-25 18:46:39 +08:00
|
|
|
|
ReadOnly convertMethods As New Dictionary(Of CLITypes, getValue) From {
|
2018-08-02 20:14:48 +08:00
|
|
|
|
_
|
2018-10-25 18:46:39 +08:00
|
|
|
|
{CLITypes.Boolean, AddressOf CLIBuildMethod.booleanRule},
|
|
|
|
|
|
{CLITypes.Double, AddressOf CLIBuildMethod.stringRule},
|
|
|
|
|
|
{CLITypes.File, AddressOf CLIBuildMethod.pathRule},
|
|
|
|
|
|
{CLITypes.Integer, AddressOf CLIBuildMethod.stringRule},
|
|
|
|
|
|
{CLITypes.String, AddressOf CLIBuildMethod.stringRule}
|
2018-08-02 20:14:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-25 18:46:39 +08:00
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Convert property to cli parameter value string
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="value"></param>
|
|
|
|
|
|
''' <param name="attr"></param>
|
|
|
|
|
|
''' <param name="prop"></param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
Private Delegate Function getValue(value As Object, attr As Argv, prop As PropertyInfo) As String
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' The different between the String and Path is that applying <see cref="CLIToken"/> or <see cref="CLIPath"/>.
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="value">只能是<see cref="System.String"/>类型的</param>
|
|
|
|
|
|
''' <param name="attr"></param>
|
|
|
|
|
|
''' <param name="prop"></param>
|
|
|
|
|
|
''' <returns></returns>
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Private Function pathRule(value As Object, attr As Argv, prop As PropertyInfo) As String
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Dim path As String = DirectCast(value, String)
|
2018-10-25 18:46:39 +08:00
|
|
|
|
|
2018-08-02 20:14:48 +08:00
|
|
|
|
If Not String.IsNullOrEmpty(path) Then
|
|
|
|
|
|
path = $"{attr.Name} {path.CLIPath}"
|
|
|
|
|
|
End If
|
2018-10-25 18:46:39 +08:00
|
|
|
|
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Return path
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' 可能包含有枚举值
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="value"></param>
|
|
|
|
|
|
''' <param name="attr"></param>
|
|
|
|
|
|
''' <param name="prop"></param>
|
|
|
|
|
|
''' <returns></returns>
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Private Function stringRule(value As Object, attr As Argv, prop As PropertyInfo) As String
|
2018-08-02 20:14:48 +08:00
|
|
|
|
If prop.PropertyType.Equals(GetType(String)) Then
|
|
|
|
|
|
Dim str As String = Scripting.ToString(value)
|
2018-10-25 18:46:39 +08:00
|
|
|
|
|
2018-08-02 20:14:48 +08:00
|
|
|
|
If String.IsNullOrEmpty(str) Then
|
|
|
|
|
|
Return ""
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return $"{attr.Name} {str.CLIToken}"
|
|
|
|
|
|
End If
|
|
|
|
|
|
ElseIf prop.PropertyType.IsInheritsFrom(GetType([Enum])) Then
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Return stringEnumRule(value, attr, prop)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Else
|
|
|
|
|
|
Dim str As String = Scripting.ToString(value)
|
2018-10-25 18:46:39 +08:00
|
|
|
|
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Return $"{attr.Name} {str}"
|
|
|
|
|
|
End If
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
2018-10-25 18:46:39 +08:00
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' 将枚举类型的属性值转换为命令行的参数值,这个转换过程与<see cref="Argv.Type"/>的值相关
|
|
|
|
|
|
'''
|
|
|
|
|
|
''' + <see cref="CLITypes.String"/>的时候,会直接调用ToString生成参数值
|
|
|
|
|
|
''' + <see cref="CLITypes.Integer"/>的时候,会将枚举值的数值作为命令行参数值
|
|
|
|
|
|
'''
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="value"></param>
|
|
|
|
|
|
''' <param name="attr"></param>
|
|
|
|
|
|
''' <param name="prop"></param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
Private Function stringEnumRule(value As Object, attr As Argv, prop As PropertyInfo) As String
|
|
|
|
|
|
Dim enumValue As [Enum] = DirectCast(value, [Enum])
|
|
|
|
|
|
|
|
|
|
|
|
If attr.Type = CLITypes.String Then
|
|
|
|
|
|
Return $"{attr.Name} {enumValue.Description.CLIToken}"
|
|
|
|
|
|
ElseIf attr.Type = CLITypes.Integer Then
|
|
|
|
|
|
Return $"{attr.Name} {Convert.ToInt32(enumValue)}"
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Else
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Throw New InvalidCastException($"Unable cast {enumValue.GetType.FullName} enum value to such type: {attr.Type.ToString}")
|
2018-08-02 20:14:48 +08:00
|
|
|
|
End If
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Property value to boolean flag in the CLI
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="value"></param>
|
|
|
|
|
|
''' <param name="attr"></param>
|
|
|
|
|
|
''' <param name="prop"></param>
|
|
|
|
|
|
''' <returns></returns>
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Private Function booleanRule(value As Object, attr As Argv, prop As PropertyInfo) As String
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Dim name As String = attr.Name
|
|
|
|
|
|
Dim b As Boolean
|
|
|
|
|
|
|
|
|
|
|
|
If prop.PropertyType.Equals(GetType(Boolean)) Then
|
|
|
|
|
|
b = DirectCast(value, Boolean)
|
|
|
|
|
|
Else
|
2018-10-25 18:46:39 +08:00
|
|
|
|
b = Scripting.ToString(value).ParseBoolean
|
2018-08-02 20:14:48 +08:00
|
|
|
|
End If
|
|
|
|
|
|
|
|
|
|
|
|
If b Then
|
|
|
|
|
|
Return name
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return ""
|
|
|
|
|
|
End If
|
|
|
|
|
|
End Function
|
|
|
|
|
|
#End Region
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Reset the CLI parameters property in the target class object.
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <typeparam name="TInteropService"></typeparam>
|
|
|
|
|
|
''' <param name="inst"></param>
|
|
|
|
|
|
''' <returns>返回所重置的参数的个数</returns>
|
|
|
|
|
|
''' <remarks></remarks>
|
|
|
|
|
|
Public Function ClearParameters(Of TInteropService As Class)(inst As TInteropService) As Integer
|
|
|
|
|
|
Dim n As Integer
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Dim properties As PropertyInfo() = inst.GetType().GetProperties()
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
Try
|
2018-10-25 18:46:39 +08:00
|
|
|
|
For Each [property] As PropertyInfo In properties
|
|
|
|
|
|
Dim attrs As Object() = [property].GetCustomAttributes(Argument, inherit:=False)
|
|
|
|
|
|
|
2018-08-02 20:14:48 +08:00
|
|
|
|
If Not (attrs Is Nothing OrElse attrs.Length = 0) Then
|
2018-10-25 18:46:39 +08:00
|
|
|
|
Call [property].SetValue(inst, Nothing, Nothing)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
n += 1
|
|
|
|
|
|
End If
|
|
|
|
|
|
Next
|
|
|
|
|
|
Catch ex As Exception
|
|
|
|
|
|
Throw New InvalidOperationException(InvalidOperation)
|
|
|
|
|
|
End Try
|
|
|
|
|
|
|
|
|
|
|
|
Return n
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
Const InvalidOperation$ = "The target type information is not the 'System.String'!"
|
|
|
|
|
|
End Module
|
|
|
|
|
|
End Namespace
|