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