microsoft-visualbasic-runtime/CommandLine/InteropService/CLIBuilder.vb

317 lines
12 KiB
VB.net
Raw Normal View History

2019-03-10 18:10:46 +08:00
#Region "Microsoft.VisualBasic::c60abc278eb77fea31b61f8de62c95d2, Microsoft.VisualBasic.Core\CommandLine\InteropService\CLIBuilder.vb"
2018-08-02 20:14:48 +08:00
2018-10-27 11:43:28 +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/>.
2018-10-25 18:46:39 +08:00
2018-10-27 11:43:28 +08:00
' /********************************************************************************/
2018-10-25 18:46:39 +08:00
2018-10-27 11:43:28 +08:00
' Summaries:
2018-10-25 18:46:39 +08:00
2018-10-27 11:43:28 +08:00
' Module CLIBuildMethod
'
' Function: GetCLI, (+2 Overloads) GetPrefix, SimpleBuilder
' Delegate Function
'
' Function: booleanRule, ClearParameters, pathRule, stringEnumRule, stringRule
'
'
'
' /********************************************************************************/
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