1 #Region "Microsoft.VisualBasic::3144cbda496e0193f901526dfec42493, Microsoft.VisualBasic.Core\CommandLine\Interpreters\Interpreter.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     '     Class Interpreter
35     
36     '         Properties: APIList, APINameList, Count, ExecuteEmptyCli, ExecuteFile
37     '                     ExecuteNotFound, Info, IsReadOnly, ListCommandInfo, Stack
38     '                     Type
39     
40     '         Constructor: (+1 OverloadsSub New
41     
42     '         Function: __executeEmpty, __getsAllCommands, apiInvoke, Contains, CreateEmptyCLIObject
43     '                   (+3 Overloads) CreateInstance, (+3 Overloads) Execute, ExistsCommand, GetAllCommands, getAPI
44     '                   GetEnumerator, GetEnumerator1, GetPossibleCommand, Help, ListingRelated
45     '                   (+2 Overloads) Remove, SDKdocs, ToDictionary, ToString, TryGetValue
46     
47     '         Sub: (+2 Overloads) Add, AddCommand, Clear, CopyTo, (+2 Overloads) Dispose
48     
49     
50     ' /********************************************************************************/
51
52 #End Region
53
54 Imports System.Reflection
55 Imports System.Runtime.CompilerServices
56 Imports System.Text
57 Imports Microsoft.VisualBasic.ApplicationServices.Debugging
58 Imports Microsoft.VisualBasic.CommandLine.ManView
59 Imports Microsoft.VisualBasic.CommandLine.Reflection
60 Imports Microsoft.VisualBasic.CommandLine.Reflection.EntryPoints
61 Imports Microsoft.VisualBasic.ComponentModel.Settings
62 Imports Microsoft.VisualBasic.Language
63 Imports Microsoft.VisualBasic.Language.UnixBash
64 Imports Microsoft.VisualBasic.Linq.Extensions
65 Imports Microsoft.VisualBasic.Serialization.JSON
66 Imports Microsoft.VisualBasic.Text
67 Imports Microsoft.VisualBasic.Text.Levenshtein
68 Imports VB = Microsoft.VisualBasic.CommandLine.InteropService.SharedORM.VisualBasic
69
70 #Const NET_45 = 0
71
72 Namespace CommandLine
73
74     ''' <summary>
75     ''' Command line interpreter for your **CLI** program.
76     ''' (命令行解释器,请注意,在调试模式之下,命令行解释器会在运行完命令之后暂停,而Release模式之下则不会。
77     ''' 假若在调试模式之下发现程序有很长一段时间处于cpu占用为零的静止状态,则很有可能已经运行完命令并且等待
78     ''' 回车退出)
79     ''' </summary>
80     ''' <remarks></remarks>
81     '''
82     <[Namespace]("Interpreter")> Public Class Interpreter
83
84         Implements IDisposable
85         Implements IDictionary(Of String, APIEntryPoint)
86
87         ''' <summary>
88         ''' 在添加之前请确保键名是小写的字符串
89         ''' </summary>
90         Protected Friend __API_table As New Dictionary(Of String, APIEntryPoint)
91         Protected __rootNamespace$
92
93 #Region "Optional delegates"
94
95         ''' <summary>
96         ''' Public Delegate Function __ExecuteFile(path As String, args As String()) As Integer,
97         ''' (<seealso cref="VisualBasic.CommandLine.ExecuteFile"/>: 假若所传入的命令行的name是文件路径,解释器就会执行这个函数指针)
98         ''' 这个函数指针一般是用作于执行脚本程序的
99         ''' </summary>
100         ''' <returns></returns>
101         Public Property ExecuteFile As ExecuteFile
102         ''' <summary>
103         ''' Public Delegate Function __ExecuteEmptyCli() As Integer,
104         ''' (<seealso cref="VisualBasic.CommandLine.ExecuteEmptyCLI"/>: 假若所传入的命令行是空的,就会执行这个函数指针)
105         ''' </summary>
106         ''' <returns></returns>
107         Public Property ExecuteEmptyCli As ExecuteEmptyCLI
108         Public Property ExecuteNotFound As ExecuteNotFound
109 #End Region
110
111         ''' <summary>
112         ''' Gets the dictionary data which contains all of the available command information in this assembly module.
113         ''' (获取从本模块之中获取得到的所有的命令行信息)
114         ''' </summary>
115         ''' <returns></returns>
116         ''' <remarks></remarks>
117         ''' 
118         <MethodImpl(MethodImplOptions.AggressiveInlining)>
119         Public Function ToDictionary() As Dictionary(Of String, APIEntryPoint)
120             Return __API_table
121         End Function
122
123         Public Overrides Function ToString() As String
124             Return "CLI://" & __rootNamespace
125         End Function
126
127         ''' <summary>
128         ''' Execute the specific command line using this interpreter.
129         ''' </summary>
130         ''' <param name="args">The user input command line string.</param>
131         ''' <returns></returns>
132         ''' <remarks></remarks>
133         Public Overridable Function Execute(args As CommandLine) As Integer
134             If Not args.IsNullOrEmpty Then
135                 Dim i As Integer = apiInvoke(args.Name.ToLower, {args}, args.Parameters)
136 #If DEBUG Then
137
138 #Else
139                 If Stack.TextEquals("Main"Then
140                     If DebuggerArgs.AutoPaused Then
141                         Call Pause()
142                     End If
143                 End If
144 #End If
145                 Return i
146             Else
147                 Return __executeEmpty() ' 命令行是空的
148             End If
149         End Function
150
151         ''' <summary>
152         ''' 命令行是空的话,假若<see cref="ExecuteEmptyCli"/>不是空值的话,会优先执行<see cref="ExecuteEmptyCli"/>函数指针
153         ''' 否则打印出所有的命令名称信息
154         ''' </summary>
155         ''' <returns></returns>
156         Private Function __executeEmpty() As Integer
157             If Not ExecuteEmptyCli Is Nothing Then
158                 Try
159                     Return _ExecuteEmptyCli()
160                 Catch ex As Exception
161                     Call App.LogException(ex)
162                     Call ex.PrintException
163                 End Try
164
165                 Return -100
166             Else
167                 ' 当用户什么也不输入的时候,打印出所有的命令名称帮助信息
168                 Return Help("")
169             End If
170         End Function
171
172         ''' <summary>
173         ''' The interpreter runs all of the command from here.(所有的命令行都从这里开始执行)
174         ''' </summary>
175         ''' <param name="commandName"></param>
176         ''' <param name="argvs">就只有一个命令行对象</param>
177         ''' <param name="help_argvs"></param>
178         ''' <returns></returns>
179         Private Function apiInvoke(commandName$, argvs As Object(), help_argvs$()) As Integer
180             Dim CLI As CommandLine = DirectCast(argvs(Scan0), CommandLine)
181
182             If __API_table.ContainsKey(commandName) Then _
183                 Return __API_table(commandName).Execute(argvs)
184
185             If "??vars".TextEquals(commandName) Then
186                 Dim vars = App.GetAppVariables
187
188                 Call Console.WriteLine()
189                 Call Console.WriteLine(PS1.Fedora12.ToString)
190                 Call Console.WriteLine()
191                 Call Console.WriteLine($"Print environment variables for {GetType(App).FullName}:")
192                 Call Console.WriteLine(ConfigEngine.Prints(vars))
193
194                 Return 0
195
196             ElseIf "??history".TextEquals(commandName) Then
197
198                 Dim logs$ = (App.LogErrDIR.ParentPath & "/.shells.log")
199
200                 With DirectCast(argvs(Scan0), CommandLine)
201                     If .Parameters.IsNullOrEmpty Then
202                         Call Console.WriteLine()
203                         Call logs.ReadAllText.EchoLine
204                         Call Console.WriteLine()
205                     Else
206                         With .ParameterList.First
207                             Select Case .Name.ToLower
208                                 Case "/clear"
209                                     Call New Byte() {}.FlushStream(logs)
210                                 Case "/search"
211
212                                     Dim term$ = .Value
213
214                                     Call Console.WriteLine()
215                                     Call logs.IterateAllLines _
216                                         .Where(Function(line)
217                                                    Return InStr(line, term, CompareMethod.Text) > 0
218                                                End Function) _
219                                         .JoinBy(vbCrLf) _
220                                         .EchoLine
221                                     Call Console.WriteLine()
222
223                                 Case Else
224                                     Console.WriteLine("Unknown command!")
225                             End Select
226                         End With
227                     End If
228                 End With
229
230                 Return 0
231
232             ElseIf String.Equals(commandName, "?"OrElse commandName = "??" OrElse commandName.TextEquals("--help"Then
233                 If help_argvs.IsNullOrEmpty Then
234                     Return Help("")
235                 Else
236                     Return Help(help_argvs.First)
237                 End If
238
239             ElseIf InStr(commandName, "??") = 1 Then  ' 支持类似于R语言里面的 ??帮助命令
240                 commandName = Mid(commandName, 3)     ' 去除前面的两个??问号,得到查询的term
241                 Return Help(commandName)
242
243             ElseIf String.Equals(commandName, "~"Then
244                 ' 打印出应用程序的位置,linux里面的HOME
245                 Call Console.WriteLine(App.ExecutablePath)
246
247                 Return 0
248
249             ElseIf String.Equals(commandName, "man"Then
250                 ' 默认是分段打印帮助信息,假若加上了  --print参数的话,则才会一次性的打印所有的信息出来
251                 Dim doc As String = SDKdocs()
252                 Dim output$ = CLI("/out"Or "./"
253
254                 If Not CLI.GetBoolean("--file"Then
255                     If CLI.GetBoolean("--print"Then
256                         Call Console.WriteLine(doc)
257                     Else
258                         Call SDKManual.LaunchManual(CLI:=Me)
259                     End If
260                 Else
261                     ' 只会写文件而不会在终端打开帮助窗口
262                 End If
263
264                 Return doc.SaveTo($"{output}/{App.AssemblyName}.md", Encoding.UTF8).CLICode
265
266             ElseIf String.Equals(commandName, "/linux-bash"StringComparison.OrdinalIgnoreCase) Then
267                 Return BashShell()
268
269             ElseIf String.Equals(commandName, "/CLI.dev"StringComparison.OrdinalIgnoreCase) Then
270                 Dim namespace$ = CLI("/namespace"Or "CLI"
271                 Dim vb$ = New VB(App:=Me, [namespace]:=[namespace]).GetSourceCode
272
273                 If CLI.IsTrue("---echo"Then
274                     Console.WriteLine(vb)
275                     Return 0
276                 Else
277                     Dim sourcefile$ = $"{App.HOME}/{Type.Assembly.CodeBase.BaseName}.vb"
278                     Return vb _
279                         .SaveTo(sourcefile, Encodings.UTF8WithoutBOM.CodePage) _
280                         .CLICode
281                 End If
282
283             Else
284                 ' 命令行的名称和上面的都不符合,但是可以在文件系统之中找得到一个相应的文件,则执行文件句柄
285                 If (commandName.FileExists OrElse commandName.DirectoryExists) AndAlso Not Me.ExecuteFile Is Nothing Then
286                     Try
287                         App.InputFile = commandName
288                         Return ExecuteFile()(path:=commandName, args:=DirectCast(argvs(Scan0), CommandLine))
289                     Catch ex As Exception
290                         ex = New Exception("Execute file failure!", ex)
291                         ex = New Exception(argvs(Scan0).ToString, ex)
292                         Call App.LogException(ex)
293                         Call ex.PrintException
294                     End Try
295
296                     Return -120
297                 ElseIf Not ExecuteNotFound Is Nothing Then
298                     Try
299                         Return ExecuteNotFound()(DirectCast(argvs(Scan0), CommandLine))
300                     Catch ex As Exception
301                         ex = New Exception("Execute not found failure!", ex)
302                         ex = New Exception(argvs(Scan0).ToString, ex)
303                         Call App.LogException(ex)
304                         Call ex.PrintException
305                     End Try
306
307                     Return -1000
308                 Else
309                     Dim list$() = Me.ListingRelated(commandName)
310
311                     If list.IsNullOrEmpty Then
312
313                         Call Console.WriteLine(BAD_COMMAND_NAME, commandName)
314                         Call Console.WriteLine()
315                         Call Console.WriteLine(PS1.Fedora12.ToString & " " & DirectCast(argvs(Scan0), CommandLine).ToString)
316
317                     Else
318                         Call listingCommands(list, commandName)
319                     End If
320                 End If
321             End If
322
323             Return -1
324         End Function
325
326         Const BAD_COMMAND_NAME$ = "Bad command, no such a command named ""{0}"", ? for command list or ""man"" for all of the commandline detail informations."
327
328         ''' <summary>
329         ''' Generate the sdk document for the target program assembly.(生成目标应用程序的命令行帮助文档,markdown格式的)
330         ''' </summary>
331         ''' <returns></returns>
332         ''' <remarks></remarks>
333         ''' 
334         <MethodImpl(MethodImplOptions.AggressiveInlining)>
335         Public Function SDKdocs() As String
336             Return Me.MarkdownDoc
337         End Function
338
339         ''' <summary>
340         ''' Process the command option arguments of the main function:
341         ''' <code>Public Function Main(argvs As String()) As Integer
342         ''' </code>
343         ''' </summary>
344         ''' <param name="CommandLineArgs">The cli command line parameter string value collection.</param>
345         ''' <returns></returns>
346         ''' <remarks></remarks>
347         Public Function Execute(CommandLineArgs As String()) As Integer
348             Dim CommandName As String = CommandLineArgs.First
349             Dim argvs As String() = CommandLineArgs.Skip(1).ToArray
350             Dim i As Integer = apiInvoke(CommandName, argvs, help_argvs:=argvs)
351
352 #If DEBUG Then
353             If Not App.GetVariable("pause.disable").ParseBoolean = True Then
354                 Call Pause()
355             End If
356 #Else
357             If Stack.TextEquals("Main"Then
358                 If AutoPaused Then
359                     Call Pause()
360                 End If
361             End If
362 #End If
363             Return i
364         End Function
365
366         Public Function Execute(CommandName As String, args As String()) As Integer
367             Return apiInvoke(CommandName.ToLower, args, help_argvs:=args)
368         End Function
369
370         ''' <summary>
371         ''' Add a command in current cli interpreter.(x向当前的这个CLI命令行解释器之中添加一个命令)
372         ''' </summary>
373         ''' <param name="Command"></param>
374         ''' <remarks></remarks>
375         Public Sub AddCommand(Command As APIEntryPoint)
376             Dim key$ = Command.Name.ToLower
377
378             If Not __API_table.ContainsKey(key) Then
379                 Call __API_table.Add(key, Command)
380             End If
381         End Sub
382
383         ''' <summary>
384         ''' Gets the help information of a specific command using its name property value.(获取某一个命令的帮助信息)
385         ''' </summary>
386         ''' <param name="CommandName">If the paramteer command name value is a empty string then this function
387         ''' will list all of the commands' help information.(假若本参数为空则函数会列出所有的命令的帮助信息)</param>
388         ''' <returns>Error code, ZERO for no error</returns>
389         ''' <remarks></remarks>
390         <ExportAPI("?", Usage:="? [CommandName]"Info:="Show Application help", Example:="? example_commandName")>
391         Public Function Help(CommandName As StringAs Integer
392             If String.IsNullOrEmpty(CommandName) Then
393                 ' List all commands when command name is empty.
394                 Call Console.WriteLine(HelpSummary(False))
395             Else
396                 ' listing the help for specific command name
397                 Call PrintCommandHelp(CommandName)
398             End If
399
400             Return 0
401         End Function
402
403         ''' <summary>
404         ''' Returns the command entry info list array.
405         ''' </summary>
406         ''' <value></value>
407         ''' <returns></returns>
408         ''' <remarks></remarks>
409         Public ReadOnly Property ListCommandInfo As EntryPoints.APIEntryPoint()
410             <MethodImpl(MethodImplOptions.AggressiveInlining)>
411             Get
412                 Return __API_table.Values.ToArray
413             End Get
414         End Property
415
416         Public ReadOnly Property Stack As String
417         Public ReadOnly Property Info As [Namespace]
418
419         ''' <summary>
420         '''
421         ''' </summary>
422         ''' <param name="type">A module or a class which contains some shared method for the command entry.
423         ''' (包含有若干使用<see cref="Reflection.ExportAPIAttribute"></see>进行标记的命令行执行入口点的Module或者Class对象类型,
424         ''' 可以使用 Object.GetType/GetType 关键词操作来获取所需要的类型信息)</param>
425         ''' <remarks></remarks>
426         Sub New(type As Type, <CallerMemberName> Optional caller As String = Nothing)
427             For Each cInfo As APIEntryPoint In __getsAllCommands(type, False)
428                 If __API_table.ContainsKey(cInfo.Name.ToLower) Then
429                     Throw New Exception(cInfo.Name & " is duplicated with other command!")
430                 Else
431                     Call __API_table.Add(cInfo.Name.ToLower, cInfo)
432                 End If
433             Next
434
435             Me.__rootNamespace = type.Namespace
436             Me._Stack = caller
437             Me._Type = type
438             Me._Info = type.NamespaceEntry(True)
439         End Sub
440
441         ''' <summary>
442         ''' The CLI API container Module/Class type information.(申明这个解释器的命令行API容器类型)
443         ''' </summary>
444         ''' <returns></returns>
445         Public ReadOnly Property Type As Type
446
447         ''' <summary>
448         ''' 导出所有符合条件的静态方法
449         ''' </summary>
450         ''' <param name="Type"></param>
451         ''' <param name="[Throw]"></param>
452         ''' <returns></returns>
453         Protected Overridable Function __getsAllCommands(Type As Type, Optional [Throw] As Boolean = TrueAs List(Of EntryPoints.APIEntryPoint)
454             Return GetAllCommands(Type, [Throw])
455         End Function
456
457         ''' <summary>
458         ''' 导出所有符合条件的静态方法,请注意,在这里已经将外部的属性标记和所属的函数的入口点进行连接了
459         ''' </summary>
460         ''' <param name="type"></param>
461         ''' <param name="[Throw]"></param>
462         ''' <returns></returns>
463         Public Shared Function GetAllCommands(type As Type, Optional [Throw] As Boolean = TrueAs List(Of EntryPoints.APIEntryPoint)
464             If type Is Nothing Then
465                 Return New List(Of APIEntryPoint)
466             End If
467
468             Dim methods As MethodInfo() = type.GetMethods(BindingFlags.Public Or BindingFlags.Static)
469             Dim commandAttribute As Type = GetType(ExportAPIAttribute)
470             Dim commandsInfo = LinqAPI.MakeList(Of APIEntryPoint) <=
471  _
472                 From methodInfo As MethodInfo
473                 In methods
474                 Let commandInfo As APIEntryPoint =
475                     getAPI(methodInfo, commandAttribute, [Throw])
476                 Where Not commandInfo Is Nothing
477                 Select commandInfo
478                 Order By commandInfo.Name Ascending
479
480             Return commandsInfo
481         End Function
482
483         ''' <summary>
484         ''' 从方法定义<see cref="MethodInfo"/>之中解析出命令行的定义
485         ''' </summary>
486         ''' <param name="methodInfo"></param>
487         ''' <param name="commandAttribute"></param>
488         ''' <param name="[throw]"></param>
489         ''' <returns></returns>
490         Private Shared Function getAPI(methodInfo As MethodInfo, commandAttribute As Type, [throw] As BooleanAs APIEntryPoint
491             Dim cmdAttr As ExportAPIAttribute = Nothing
492             Dim commandInfo As APIEntryPoint
493
494             Try
495                 Dim attrs As Object() = methodInfo.GetCustomAttributes(commandAttribute, False)
496                 If attrs.IsNullOrEmpty Then
497                     Return Nothing
498                 End If
499
500                 cmdAttr = DirectCast(attrs(0), ExportAPIAttribute)
501                 commandInfo = New APIEntryPoint(cmdAttr, methodInfo, [throw]) ' 在这里将外部的属性标记和所属的函数的入口点进行连接
502                 If cmdAttr.Info.StringEmpty Then
503                     cmdAttr.Info = methodInfo.Description ' 帮助信息的获取兼容系统的Description方法
504                 End If
505                 If cmdAttr.Usage.StringEmpty Then
506                     cmdAttr.Usage = methodInfo.Usage
507                 End If
508                 If cmdAttr.Example.StringEmpty Then
509                     cmdAttr.Example = methodInfo.ExampleInfo
510                 End If
511
512                 Return commandInfo
513             Catch ex As Exception
514                 If Not cmdAttr Is Nothing Then
515                     ex = New Exception("This command API can not be imports: " & cmdAttr.GetJson, ex)
516                     ex = New Exception(CheckNotice, ex)
517                 End If
518                 Call App.LogException(New Exception(methodInfo.FullName(True), ex))
519                 Call ex.PrintException
520
521                 Return Nothing
522             End Try
523         End Function
524
525         Const CheckNotice As String = "Please checks for the export api definition on your CLI interface function."
526
527         ''' <summary>
528         ''' Create an empty cli command line interpreter object which contains no commands entry.
529         ''' (创建一个没有包含有任何命令入口点的空的CLI命令行解释器)
530         ''' </summary>
531         ''' <returns></returns>
532         ''' <remarks></remarks>
533         ''' 
534         <MethodImpl(MethodImplOptions.AggressiveInlining)>
535         Public Shared Function CreateEmptyCLIObject() As Interpreter
536             Return New Interpreter(GetType(Interpreter))
537         End Function
538
539         ''' <summary>
540         ''' Create a new interpreter instance from a specific type information.
541         ''' (从目标类型之中构造出一个命令行解释器)
542         ''' </summary>
543         ''' <param name="Type"></param>
544         ''' <returns></returns>
545         ''' <remarks></remarks>
546         <MethodImpl(MethodImplOptions.AggressiveInlining)>
547         <ExportAPI("CreateObject")>
548         Public Shared Function CreateInstance(type As Type) As Interpreter
549             Return New Interpreter(type)
550         End Function
551
552         ''' <summary>
553         ''' Create a new interpreter instance using the specific type information.
554         ''' (使用所制定的目标类型信息构造出一个CLI命令行解释器)
555         ''' </summary>
556         ''' <typeparam name="T"></typeparam>
557         ''' <returns></returns>
558         ''' <remarks></remarks>
559         <MethodImpl(MethodImplOptions.AggressiveInlining)>
560         Public Shared Function CreateInstance(Of T As Class)() As Interpreter
561             Return New Interpreter(type:=GetType(Type))
562         End Function
563
564 #If NET_40 = 0 Then
565
566         ''' <summary>
567         ''' Create a new interpreter instance from a specific dll/exe path, this program assembly file should be a standard .NET assembly.
568         ''' (从一个标准的.NET程序文件之中构建出一个命令行解释器)
569         ''' </summary>
570         ''' <param name="assmPath">DLL/EXE file path.(标准的.NET程序集文件的文件路径)</param>
571         ''' <returns></returns>
572         ''' <remarks></remarks>
573         '''
574         <ExportAPI("rundll")>
575         Public Shared Function CreateInstance(assmPath As StringAs Interpreter
576             Dim assembly As Assembly = Assembly.LoadFrom(assmPath)
577             Dim dllMain As Type = GetType(RunDllEntryPoint)
578             Dim main As Type = LinqAPI.DefaultFirst(Of Type) _
579  _
580                 () <= From [mod] As Type
581                       In assembly.DefinedTypes
582                       Let attributes As Object() = [mod].GetCustomAttributes(dllMain, inherit:=False)
583                       Where Not attributes Is Nothing AndAlso
584                           attributes.Length = 1
585                       Select [mod]
586
587             If main Is Nothing Then
588                 Return Nothing  ' 没有找到执行入口点
589             Else
590                 Return New Interpreter(main)
591             End If
592         End Function
593 #End If
594
595 #Region "IDisposable Support"
596         Private disposedValue As Boolean To detect redundant calls
597
598         ' IDisposable
599         Protected Sub Dispose(disposing As Boolean)
600             If Not Me.disposedValue Then
601                 If disposing Then
602                     ' TODO: dispose managed state (managed objects).
603                 End If
604
605                 ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
606                 ' TODO: set large fields to null.
607             End If
608             Me.disposedValue = True
609         End Sub
610
611         ' TODO: override Finalize() only if Dispose(      disposing As Boolean) above has code to free unmanaged resources.
612         'Protected Overrides Sub Finalize()
613         '    ' Do not change this code.  Put cleanup code in Dispose(      disposing As Boolean) above.
614         '    Dispose(False)
615         '    MyBase.Finalize()
616         'End Sub
617
618         ' This code added by Visual Basic to correctly implement the disposable pattern.
619         Public Sub Dispose() Implements IDisposable.Dispose
620             Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
621             Dispose(True)
622             GC.SuppressFinalize(Me)
623         End Sub
624 #End Region
625
626 #Region "Implements System.Collections.Generic.IReadOnlyDictionary(Of String, CommandInfo)"
627
628         Public Iterator Function GetEnumerator() As IEnumerator(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)) Implements IEnumerable(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).GetEnumerator
629             For Each key As String In Me.__API_table.Keys
630                 Yield New KeyValuePair(Of String, EntryPoints.APIEntryPoint)(key, Me.__API_table(key))
631             Next
632         End Function
633
634         Public Iterator Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
635             Yield Me.GetEnumerator
636         End Function
637
638         Public Sub Add(item As KeyValuePair(Of String, EntryPoints.APIEntryPoint)) Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Add
639             Call __API_table.Add(item.Key, item.Value)
640         End Sub
641
642         ''' <summary>
643         ''' Clear the hash table of the cli command line interpreter command entry points.(清除本CLI解释器之中的所有的命令行执行入口点的哈希数据信息)
644         ''' </summary>
645         ''' <remarks></remarks>
646         Public Sub Clear() Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Clear
647             Call __API_table.Clear()
648         End Sub
649
650         Public Function Contains(item As KeyValuePair(Of String, EntryPoints.APIEntryPoint)) As Boolean Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Contains
651             Return __API_table.Contains(item)
652         End Function
653
654         Public Sub CopyTo(array() As KeyValuePair(Of String, EntryPoints.APIEntryPoint), arrayIndex As IntegerImplements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).CopyTo
655             Call __API_table.ToArray.CopyTo(array, arrayIndex)
656         End Sub
657
658         ''' <summary>
659         ''' Gets the command counts in current cli interpreter.(返回本CLI命令行解释器之中所包含有的命令的数目)
660         ''' </summary>
661         ''' <value></value>
662         ''' <returns></returns>
663         ''' <remarks></remarks>
664         Public ReadOnly Property Count As Integer Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Count
665             Get
666                 Return Me.__API_table.Count
667             End Get
668         End Property
669
670         Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).IsReadOnly
671             Get
672                 Return False
673             End Get
674         End Property
675
676         Public Function Remove(item As KeyValuePair(Of String, EntryPoints.APIEntryPoint)) As Boolean Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Remove
677             Return __API_table.Remove(item.Key)
678         End Function
679
680         Public Sub Add(key As String, value As EntryPoints.APIEntryPoint) Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Add
681             Call __API_table.Add(key, value)
682         End Sub
683
684         ''' <summary>
685         ''' The target command line command is exists in this cli interpreter using it name property?(判断目标命令行命令是否存在于本CLI命令行解释器之中)
686         ''' </summary>
687         ''' <param name="CommandName">The command name value is not case sensitive.(命令的名称对大小写不敏感的)</param>
688         ''' <returns></returns>
689         ''' <remarks></remarks>
690         Public Function ExistsCommand(CommandName As StringAs Boolean Implements IDictionary(Of String, EntryPoints.APIEntryPoint).ContainsKey
691             Return Me.__API_table.ContainsKey(CommandName.ToLower)
692         End Function
693
694         ''' <summary>
695         '''
696         ''' </summary>
697         ''' <param name="key">调用前需要转换为小写字母的形式</param>
698         ''' <returns></returns>
699         Default Public Overloads Property Item(key As StringAs EntryPoints.APIEntryPoint Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Item
700             Get
701                 Return Me.__API_table(key)
702             End Get
703             Set(value As EntryPoints.APIEntryPoint)
704                 'DO NOTHING
705             End Set
706         End Property
707
708         Public Function GetPossibleCommand(name As Value(Of String)) As APIEntryPoint
709             Dim commands = ListingRelated(name)
710
711             If commands.Length = 0 Then
712                 Return Nothing
713             Else
714                 Return __API_table(commands.First.ToLower)
715             End If
716         End Function
717
718         ''' <summary>
719         ''' 列举出所有可能的命令
720         ''' </summary>
721         ''' <param name="query">模糊匹配</param>
722         ''' <returns></returns>
723         Public Function ListingRelated(query$) As String()
724             Dim key As New LevenshteinString(query.ToLower)
725             Dim LQuery = From x As String
726                          In __API_table.Keys.AsParallel
727                          Let compare = key Like x
728                          Where Not compare Is Nothing AndAlso
729                              compare.Score > 0.3
730                          Select compare.Score,
731                              x
732                          Order By Score Descending
733
734             Dim levenshteins = LQuery _
735                 .Select(Function(x) x.x) _
736                 .AsList
737
738             levenshteins += __API_table _
739                 .Keys _
740                 .Where(Function(s)
741                            Return InStr(s, query, CompareMethod.Text) > 0 OrElse
742                                   InStr(query, s, CompareMethod.Text) > 0
743                        End Function)
744
745             Return levenshteins _
746                 .Distinct _
747                 .Select(Function(name)
748                             Return __API_table(name).Name
749                         End Function) _
750                 .ToArray
751         End Function
752
753         ''' <summary>
754         ''' List all of the command line entry point name which were contains in this cli interpreter.
755         ''' (列举出本CLI命令行解释器之中的所有的命令行执行入口点的名称)
756         ''' </summary>
757         ''' <value></value>
758         ''' <returns></returns>
759         ''' <remarks></remarks>
760         Public ReadOnly Property APINameList As ICollection(Of StringImplements IDictionary(Of String, EntryPoints.APIEntryPoint).Keys
761             Get
762                 Return Me.__API_table.Keys
763             End Get
764         End Property
765
766         Public Function Remove(CommandName As StringAs Boolean Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Remove
767             Return __API_table.Remove(CommandName)
768         End Function
769
770         Public Function TryGetValue(key As StringByRef value As EntryPoints.APIEntryPoint) As Boolean Implements IDictionary(Of String, EntryPoints.APIEntryPoint).TryGetValue
771             Return Me.__API_table.TryGetValue(key, value)
772         End Function
773
774         ''' <summary>
775         ''' 当前的解释器内所容纳的所有的CLI API列表
776         ''' </summary>
777         ''' <returns></returns>
778         Public ReadOnly Property APIList As ICollection(Of EntryPoints.APIEntryPoint) Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Values
779             Get
780                 Return Me.__API_table.Values
781             End Get
782         End Property
783 #End Region
784     End Class
785 End Namespace