| 1 | #Region "Microsoft.VisualBasic::fbbb4af951ee7f357716c975b2a40855, Microsoft.VisualBasic.Core\CommandLine\CommandLine.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 CommandLine |
| 35 | ' |
| 36 | ' Properties: BoolFlags, cli, Count, EnvironmentVariables, IsNothing |
| 37 | ' IsNullOrEmpty, IsReadOnly, Keys, Name, ParameterList |
| 38 | ' Parameters, SingleValue, Tokens |
| 39 | ' |
| 40 | ' Function: Assert, CheckMissingRequiredArguments, CheckMissingRequiredParameters, Contains, ContainsParameter |
| 41 | ' GetBoolean, GetByte, GetBytes, GetChar, GetChars |
| 42 | ' GetCommandsOverview, GetDateTime, GetDecimal, GetDictionary, GetDouble |
| 43 | ' GetEnumerator, GetEnumerator1, GetFloat, GetFullDIRPath, GetFullFilePath |
| 44 | ' GetGuid, GetInt16, GetInt32, GetInt64, GetObject |
| 45 | ' GetOrdinal, GetString, GetValue, HavebFlag, IsNull |
| 46 | ' IsTrue, OpenHandle, OpenStreamInput, OpenStreamOutput, ReadInput |
| 47 | ' (+2 Overloads) Remove, ToArgumentVector, ToString |
| 48 | ' |
| 49 | ' Sub: (+2 Overloads) Add, Clear, CopyTo |
| 50 | ' |
| 51 | ' Operators: (+4 Overloads) -, ^, +, <, (+2 Overloads) <= |
| 52 | ' >, (+2 Overloads) >= |
| 53 | ' |
| 54 | ' |
| 55 | ' /********************************************************************************/ |
| 56 | |
| 57 | #End Region |
| 58 | |
| 59 | Imports System.IO |
| 60 | Imports System.Runtime.CompilerServices |
| 61 | Imports System.Text |
| 62 | Imports Microsoft.VisualBasic.CommandLine.Parsers |
| 63 | Imports Microsoft.VisualBasic.ComponentModel.Collection.Generic |
| 64 | Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel |
| 65 | Imports Microsoft.VisualBasic.Language |
| 66 | Imports Microsoft.VisualBasic.Language.Default |
| 67 | Imports Microsoft.VisualBasic.Language.UnixBash.FileSystem |
| 68 | Imports Microsoft.VisualBasic.Linq |
| 69 | Imports Microsoft.VisualBasic.Scripting |
| 70 | Imports Microsoft.VisualBasic.Scripting.Expressions |
| 71 | Imports Microsoft.VisualBasic.Serialization |
| 72 | Imports Microsoft.VisualBasic.Text |
| 73 | |
| 74 | Namespace CommandLine |
| 75 | |
| 76 | ''' <summary> |
| 77 | ''' A command line object that parse from the user input commandline string. |
| 78 | ''' (从用户所输入的命令行字符串之中解析出来的命令行对象,标准的命令行格式为: |
| 79 | ''' <example><EXE> <CLI_Name> ["Parameter" "Value"]</example>) |
| 80 | ''' </summary> |
| 81 | ''' <remarks></remarks> |
| 82 | ''' |
| 83 | Public Class CommandLine : Inherits BaseClass |
| 84 | Implements ICollection(Of NamedValue(Of String)) |
| 85 | Implements INamedValue |
| 86 | |
| 87 | Friend __arguments As New List(Of NamedValue(Of String)) |
| 88 | ''' <summary> |
| 89 | ''' 原始的命令行字符串 |
| 90 | ''' </summary> |
| 91 | Friend cliCommandArgvs As String |
| 92 | |
| 93 | Dim _name As String |
| 94 | |
| 95 | ''' <summary> |
| 96 | ''' The command name that parse from the input command line. |
| 97 | ''' (从输入的命令行中所解析出来的命令的名称) |
| 98 | ''' </summary> |
| 99 | ''' <value></value> |
| 100 | ''' <returns></returns> |
| 101 | ''' <remarks></remarks> |
| 102 | Public Property Name As String Implements INamedValue.Key |
| 103 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 104 | Get |
| 105 | Return _name |
| 106 | End Get |
| 107 | Protected Friend Set(value As String) |
| 108 | _name = value |
| 109 | End Set |
| 110 | End Property |
| 111 | |
| 112 | ''' <summary> |
| 113 | ''' The command tokens that were parsed from the input commandline. |
| 114 | ''' (从所输入的命令行之中所解析出来的命令参数单元) |
| 115 | ''' </summary> |
| 116 | ''' <value></value> |
| 117 | ''' <returns></returns> |
| 118 | ''' <remarks></remarks> |
| 119 | Public Property Tokens As String() |
| 120 | |
| 121 | ''' <summary> |
| 122 | ''' Listing all of the parameter value collection that parsed from the commandline string. |
| 123 | ''' </summary> |
| 124 | ''' <returns></returns> |
| 125 | Public ReadOnly Property ParameterList As NamedValue(Of String)() |
| 126 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 127 | Get |
| 128 | Return __arguments.ToArray |
| 129 | End Get |
| 130 | End Property |
| 131 | |
| 132 | ''' <summary> |
| 133 | ''' 得到当前的命令行对象之中的所有的参数的名称的列表 |
| 134 | ''' </summary> |
| 135 | ''' <returns></returns> |
| 136 | Public ReadOnly Property Keys As String() |
| 137 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 138 | Get |
| 139 | Return __arguments.Select(Function(v) v.Name).ToArray |
| 140 | End Get |
| 141 | End Property |
| 142 | |
| 143 | ''' <summary> |
| 144 | ''' The parameters in the commandline without the first token of the command name. |
| 145 | ''' (将命令行解析为词元之后去掉命令的名称之后所剩下的所有的字符串列表) |
| 146 | ''' </summary> |
| 147 | ''' <value></value> |
| 148 | ''' <returns></returns> |
| 149 | ''' <remarks></remarks> |
| 150 | <DumpNode> Public ReadOnly Property Parameters As String() |
| 151 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 152 | Get |
| 153 | Return Tokens.Skip(1).ToArray |
| 154 | End Get |
| 155 | End Property |
| 156 | |
| 157 | ''' <summary> |
| 158 | ''' 对于参数而言,都是--或者-或者/或者\开头的,下一个单词为单引号或者非上面的字符开头的,例如/o <path> |
| 159 | ''' 对于开关而言,与参数相同的其实符号,但是后面不跟参数而是其他的开关,通常开关用来进行简要表述一个逻辑值 |
| 160 | ''' </summary> |
| 161 | ''' <returns></returns> |
| 162 | Public Property BoolFlags As String() |
| 163 | |
| 164 | ''' <summary> |
| 165 | ''' 获取得到通过``/@set``参数所传入的环境变量 |
| 166 | ''' </summary> |
| 167 | ''' <returns></returns> |
| 168 | Public ReadOnly Property EnvironmentVariables As Dictionary(Of String, String) |
| 169 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 170 | Get |
| 171 | ' 在命令行之中没有任何其他的参数,但是存在一个/@set的话,这个标记会被当作为命令名称 |
| 172 | ' 则在读取环境变量的时候就会失败 |
| 173 | If Name.TextEquals("/@set") Then |
| 174 | Return DictionaryParser.TryParse(Parameters(Scan0)) |
| 175 | Else |
| 176 | Return GetDictionary("/@set") |
| 177 | End If |
| 178 | End Get |
| 179 | End Property |
| 180 | |
| 181 | ''' <summary> |
| 182 | ''' Get the original command line string.(获取所输入的命令行对象的原始的字符串) |
| 183 | ''' </summary> |
| 184 | ''' <value></value> |
| 185 | ''' <returns></returns> |
| 186 | ''' <remarks></remarks> |
| 187 | Public ReadOnly Property cli As String |
| 188 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 189 | Get |
| 190 | Return cliCommandArgvs |
| 191 | End Get |
| 192 | End Property |
| 193 | |
| 194 | ''' <summary> |
| 195 | ''' The parameter name is not case sensitive. |
| 196 | ''' (开关的名称是不区分大小写的,进行字符串插值脚本化处理的时候,是使用的<see cref="App.GetVariable"/>函数来获取环境变量值) |
| 197 | ''' </summary> |
| 198 | ''' <param name="paramName"> |
| 199 | ''' The argument name in the commandline. |
| 200 | ''' |
| 201 | ''' ##### 2018-09-10 |
| 202 | ''' 为了兼容VB的!字典取值语法,这个属性是会自动处理开关参数的前缀的 |
| 203 | ''' 即会自动的将开关参数的/\--等前缀删除尝试进行取值 |
| 204 | ''' 这个自动转换不会应用于逻辑开关参数上面 |
| 205 | ''' </param> |
| 206 | ''' <value></value> |
| 207 | ''' <returns></returns> |
| 208 | ''' <remarks></remarks> |
| 209 | Default Public ReadOnly Property Item(paramName As String) As DefaultString |
| 210 | Get |
| 211 | Dim LQuery As NamedValue(Of String) = |
| 212 | __arguments _ |
| 213 | .Where(Function(x) |
| 214 | Return String.Equals(x.Name, paramName, StringComparison.OrdinalIgnoreCase) OrElse |
| 215 | String.Equals(x.Name.Trim("\", "/", "-"), paramName, StringComparison.OrdinalIgnoreCase) |
| 216 | End Function) _ |
| 217 | .FirstOrDefault |
| 218 | ' 是值类型,不会出现空引用的情况 |
| 219 | Dim value As String = LQuery.Value |
| 220 | |
| 221 | If value.StringEmpty Then |
| 222 | ' 2018-1-22 |
| 223 | ' |
| 224 | ' 如果是需要获取逻辑值的话,直接查找__arguments值列表是获取不到结果的 |
| 225 | ' 在这里使用IsTrue来判断,如果开关存在则返回TRUE字符串 |
| 226 | ' 否则返回空字符串表示不存在 |
| 227 | |
| 228 | If HavebFlag(paramName) Then |
| 229 | value = "TRUE" |
| 230 | Else |
| 231 | value = "" |
| 232 | End If |
| 233 | Else |
| 234 | ' 尝试进行字符串插值,从而实现命令行部分脚本化 |
| 235 | |
| 236 | ' 2017-3-13 |
| 237 | ' 对于Windows文件路径而言, 不推荐转义 |
| 238 | ' 因为Windows的文件路径分隔符为\,很容易引起误解,例如C:\tsv会被误转义为C:<TAB>sv而导致错误 |
| 239 | ' 所以在这里关闭escape参数选项 |
| 240 | value = value.Interpolate(__envir, escape:=False) |
| 241 | End If |
| 242 | |
| 243 | Return New DefaultString(value) |
| 244 | End Get |
| 245 | End Property |
| 246 | |
| 247 | ReadOnly __envir As Func(Of String, String) = AddressOf App.GetVariable |
| 248 | |
| 249 | Public Property SingleValue As String |
| 250 | |
| 251 | ''' <summary> |
| 252 | ''' See if the target logical flag argument is exists in the commandline? |
| 253 | ''' (查看命令行之中是否存在某一个逻辑开关) |
| 254 | ''' </summary> |
| 255 | ''' <param name="name"></param> |
| 256 | ''' <returns></returns> |
| 257 | Public Function HavebFlag(name As String) As Boolean |
| 258 | If Me.BoolFlags.IsNullOrEmpty Then |
| 259 | Return False |
| 260 | Else |
| 261 | ' boolflags 已经全部都被转换为小写形式了 |
| 262 | Return Array.IndexOf(BoolFlags, name.ToLower) > -1 |
| 263 | End If |
| 264 | End Function |
| 265 | |
| 266 | ''' <summary> |
| 267 | ''' Returns the original cli command line argument string.(返回所传入的命令行的原始字符串) |
| 268 | ''' </summary> |
| 269 | ''' <returns></returns> |
| 270 | ''' <remarks></remarks> |
| 271 | ''' |
| 272 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 273 | Public Overrides Function ToString() As String |
| 274 | Return CLICommandArgvs |
| 275 | End Function |
| 276 | |
| 277 | ''' <summary> |
| 278 | ''' Get specific argument value as full directory path. |
| 279 | ''' </summary> |
| 280 | ''' <param name="name">parameter name</param> |
| 281 | ''' <returns></returns> |
| 282 | ''' |
| 283 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 284 | Public Function GetFullDIRPath(name As String) As String |
| 285 | Return FileIO.FileSystem.GetDirectoryInfo(Me(name)).FullName |
| 286 | End Function |
| 287 | |
| 288 | ''' <summary> |
| 289 | ''' Get specific argument value as full file path.(这个函数还会同时修正file://协议的头部) |
| 290 | ''' </summary> |
| 291 | ''' <param name="name">parameter name</param> |
| 292 | ''' <returns></returns> |
| 293 | Public Function GetFullFilePath(name As String) As String |
| 294 | Dim path$ = Me(name) |
| 295 | path = FixPath(path) |
| 296 | Return FileIO.FileSystem.GetFileInfo(path).FullName |
| 297 | End Function |
| 298 | |
| 299 | ''' <summary> |
| 300 | ''' Gets the brief summary information of current cli command line object. |
| 301 | ''' (获取当前的命令行对象的参数摘要信息) |
| 302 | ''' </summary> |
| 303 | ''' <returns></returns> |
| 304 | ''' <remarks></remarks> |
| 305 | Public Function GetCommandsOverview() As String |
| 306 | Dim sb As New StringBuilder(vbCrLf, 1024) |
| 307 | Call sb.AppendLine($"Commandline arguments overviews{vbCrLf}Command Name -- ""{Me.Name}""") |
| 308 | Call sb.AppendLine() |
| 309 | Call sb.AppendLine("---------------------------------------------------------") |
| 310 | Call sb.AppendLine() |
| 311 | |
| 312 | If __arguments.Count = 0 Then |
| 313 | Call sb.AppendLine("No parameter was define in this commandline.") |
| 314 | Return sb.ToString |
| 315 | End If |
| 316 | |
| 317 | Dim MaxSwitchName As Integer = (From item As NamedValue(Of String) |
| 318 | In __arguments |
| 319 | Select Len(item.Name)).Max |
| 320 | For Each sw As NamedValue(Of String) In __arguments |
| 321 | Call sb.AppendLine($" {sw.Name} {New String(" "c, MaxSwitchName - Len(sw.Name))}= ""{sw.Value}"";") |
| 322 | Next |
| 323 | |
| 324 | Return sb.ToString |
| 325 | End Function |
| 326 | |
| 327 | ''' <summary> |
| 328 | ''' Checking for the missing required parameter, this function will returns the missing parameter |
| 329 | ''' in the current cli command line object using a specific parameter name list. |
| 330 | ''' (检查<paramref name="list"></paramref>之中的所有参数是否存在,函数会返回不存在的参数名) |
| 331 | ''' </summary> |
| 332 | ''' <param name="list"></param> |
| 333 | ''' <returns></returns> |
| 334 | ''' <remarks></remarks> |
| 335 | Public Function CheckMissingRequiredParameters(list As IEnumerable(Of String)) As String() |
| 336 | Dim LQuery$() = LinqAPI.Exec(Of String) _ |
| 337 | _ |
| 338 | () <= From p As String |
| 339 | In list |
| 340 | Where String.IsNullOrEmpty(Me(p)) |
| 341 | Select p |
| 342 | |
| 343 | Return LQuery |
| 344 | End Function |
| 345 | |
| 346 | ''' <summary> |
| 347 | ''' Gets a list of missing required argument name. |
| 348 | ''' </summary> |
| 349 | ''' <param name="args"></param> |
| 350 | ''' <returns></returns> |
| 351 | ''' |
| 352 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 353 | Public Function CheckMissingRequiredArguments(ParamArray args As String()) As String() |
| 354 | Return CheckMissingRequiredParameters(list:=args) |
| 355 | End Function |
| 356 | |
| 357 | ''' <summary> |
| 358 | ''' Does this cli command line object contains any parameter argument information. |
| 359 | ''' (查看本命令行参数对象之中是否存在有参数信息) |
| 360 | ''' </summary> |
| 361 | ''' <value></value> |
| 362 | ''' <returns></returns> |
| 363 | ''' <remarks></remarks> |
| 364 | Public ReadOnly Property IsNullOrEmpty As Boolean |
| 365 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 366 | Get |
| 367 | Return Tokens.IsNullOrEmpty OrElse (Tokens.Length = 1 AndAlso String.IsNullOrEmpty(Tokens.First)) |
| 368 | End Get |
| 369 | End Property |
| 370 | |
| 371 | ''' <summary> |
| 372 | ''' <see cref="String.IsNullOrEmpty"/> of <see cref="Name"/> AndAlso <see cref="IsNullOrEmpty"/> |
| 373 | ''' </summary> |
| 374 | ''' <returns></returns> |
| 375 | Public ReadOnly Property IsNothing As Boolean |
| 376 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 377 | Get |
| 378 | Return String.IsNullOrEmpty(Me.Name) AndAlso IsNullOrEmpty |
| 379 | End Get |
| 380 | End Property |
| 381 | |
| 382 | ''' <summary> |
| 383 | ''' Does the specific argument exists in this commandline? argument name is not case sensitity. |
| 384 | ''' (参数名称字符串大小写不敏感) |
| 385 | ''' </summary> |
| 386 | ''' <param name="parameterName"></param> |
| 387 | ''' <returns></returns> |
| 388 | Public Function ContainsParameter(parameterName As String, trim As Boolean) As Boolean |
| 389 | Dim namer As String = If(trim, parameterName.TrimParamPrefix, parameterName) |
| 390 | Dim LQuery = LinqAPI.DefaultFirst(Of Integer) _ |
| 391 | _ |
| 392 | () <= From para As NamedValue(Of String) |
| 393 | In Me.__arguments ' 名称都是没有处理过的 |
| 394 | Where String.Equals(namer, para.Name, StringComparison.OrdinalIgnoreCase) |
| 395 | Select 100 |
| 396 | |
| 397 | Return LQuery > 50 |
| 398 | End Function |
| 399 | |
| 400 | ''' <summary> |
| 401 | ''' Parsing the commandline string as object model |
| 402 | ''' </summary> |
| 403 | ''' <param name="CommandLine"></param> |
| 404 | ''' <returns></returns> |
| 405 | ''' |
| 406 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 407 | Public Shared Widening Operator CType(CommandLine As String) As CommandLine |
| 408 | Return TryParse(CommandLine) |
| 409 | End Operator |
| 410 | |
| 411 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 412 | Public Shared Widening Operator CType(CLI As Func(Of String)) As CommandLine |
| 413 | Return TryParse(CLI()) |
| 414 | End Operator |
| 415 | |
| 416 | ''' <summary> |
| 417 | ''' Determined that the specific Boolean flag is exists or not? |
| 418 | ''' if not then returns <paramref name="failure"/>, if exists such flag, then returns the <paramref name="name"/>. |
| 419 | ''' </summary> |
| 420 | ''' <param name="name">Boolean flag name</param> |
| 421 | ''' <param name="failure"></param> |
| 422 | ''' <returns></returns> |
| 423 | Public Function Assert(name As String, Optional failure As String = "") As String |
| 424 | If GetBoolean(name) Then |
| 425 | Return name |
| 426 | Else |
| 427 | Return failure |
| 428 | End If |
| 429 | End Function |
| 430 | |
| 431 | ''' <summary> |
| 432 | ''' If the target parameter is not presents in the CLI, then this function will returns nothing. |
| 433 | ''' (键值对之间使用分号分隔) |
| 434 | ''' </summary> |
| 435 | ''' <param name="name$"></param> |
| 436 | ''' <returns></returns> |
| 437 | Public Function GetDictionary(name$, Optional default$ = Nothing) As Dictionary(Of String, String) |
| 438 | Dim s$ = Me(name$) |
| 439 | |
| 440 | If String.IsNullOrEmpty(s$) Then |
| 441 | If [default].StringEmpty Then |
| 442 | Return Nothing |
| 443 | Else |
| 444 | Return DictionaryParser.TryParse([default]) |
| 445 | End If |
| 446 | Else |
| 447 | Return DictionaryParser.TryParse(s$) |
| 448 | End If |
| 449 | End Function |
| 450 | |
| 451 | Public Function IsTrue(parameter$) As Boolean |
| 452 | If Me.HavebFlag(parameter) Then |
| 453 | Return True |
| 454 | End If |
| 455 | Return Me(parameter).DefaultValue.ParseBoolean |
| 456 | End Function |
| 457 | |
| 458 | #Region "Pipeline" |
| 459 | |
| 460 | ''' <summary>About <paramref name="s"/>: |
| 461 | ''' |
| 462 | ''' + If the file path is not a value path, then is the value is not null, the argument value will be returned from this parameter. |
| 463 | ''' + If the value is nothing, then this function will open the standard input as input. |
| 464 | ''' + If the file path is valid as input file, then a local file system pointer will be returned. |
| 465 | ''' |
| 466 | ''' [管道函数] 假若参数名存在并且所指向的文件也存在,则返回本地文件的文件指针,否则返回标准输入的指针 |
| 467 | ''' </summary> |
| 468 | ''' <param name="param"></param> |
| 469 | ''' <param name="s"> |
| 470 | ''' + If the file path is not a value path, then is the value is not null, the argument value will be returned from this parameter. |
| 471 | ''' + If the value is nothing, then this function will open the standard input as input. |
| 472 | ''' + If the file path is valid as input file, then a local file system pointer will be returned. |
| 473 | ''' </param> |
| 474 | ''' <returns></returns> |
| 475 | Public Function OpenStreamInput(param As String, Optional ByRef s As String = Nothing) As StreamReader |
| 476 | Dim path As String = Me(param) |
| 477 | |
| 478 | If path.FileExists Then |
| 479 | Return New StreamReader(New FileStream(path, FileMode.Open, access:=FileAccess.Read)) |
| 480 | ElseIf Not String.IsNullOrEmpty(path) Then |
| 481 | s = path |
| 482 | Return Nothing |
| 483 | Else |
| 484 | Return New StreamReader(Console.OpenStandardInput) |
| 485 | End If |
| 486 | End Function |
| 487 | |
| 488 | ''' <summary> |
| 489 | ''' If the <see cref="StreamWriter.BaseStream"/> is <see cref="FileStream"/>, then it means not a ``std_out`` pointer. |
| 490 | ''' ([管道函数] 假若参数名存在,则返回本地文件的文件指针,否则返回标准输出的指针) |
| 491 | ''' </summary> |
| 492 | ''' <param name="param"></param> |
| 493 | ''' <returns></returns> |
| 494 | Public Function OpenStreamOutput(param$, Optional encoding As Encodings = Encodings.UTF8) As StreamWriter |
| 495 | Dim path$ = Me(param) |
| 496 | |
| 497 | If path.StringEmpty Then |
| 498 | Return New StreamWriter(Console.OpenStandardOutput, encoding.CodePage) |
| 499 | Else |
| 500 | Return path.OpenWriter(encoding) |
| 501 | End If |
| 502 | End Function |
| 503 | |
| 504 | ''' <summary> |
| 505 | ''' Read all of the text input from the file or ``std_in`` |
| 506 | ''' </summary> |
| 507 | ''' <remarks> |
| 508 | ''' 这个函数会首先尝试使用<see cref="OpenStreamInput"/>打开本地文件和标准输出流 |
| 509 | ''' 如果失败的话<see cref="OpenStreamInput"/>函数会返回空值,这个时候参数字符串将会直接被 |
| 510 | ''' 返回作为结果,如果打开成功的话,会将得到的输入流之中的所有字符串读出来返回 |
| 511 | ''' </remarks> |
| 512 | ''' <param name="param"></param> |
| 513 | ''' <returns></returns> |
| 514 | Public Function ReadInput(param As String) As String |
| 515 | Dim s As String = Nothing |
| 516 | Dim read As StreamReader = OpenStreamInput(param, s) |
| 517 | |
| 518 | If read Is Nothing Then |
| 519 | Return s |
| 520 | Else |
| 521 | Return read.ReadToEnd |
| 522 | End If |
| 523 | End Function |
| 524 | #End Region |
| 525 | |
| 526 | #Region "IDataRecord Methods" |
| 527 | |
| 528 | ''' <summary> |
| 529 | ''' Gets the value Of the specified column As a Boolean. |
| 530 | ''' (这个函数也同时包含有开关参数的,开关参数默认为逻辑值类型,当包含有开关参数的时候,其逻辑值为True,反之函数会检查参数列表,参数不存在则为空值字符串,则也为False) |
| 531 | ''' </summary> |
| 532 | ''' <param name="parameter">可以包含有开关参数</param> |
| 533 | ''' <returns></returns> |
| 534 | ''' |
| 535 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 536 | Public Function GetBoolean(parameter As String) As Boolean |
| 537 | Return Me.IsTrue(parameter) |
| 538 | End Function |
| 539 | |
| 540 | ''' <summary> |
| 541 | ''' Gets the 8-bit unsigned Integer value Of the specified column. |
| 542 | ''' </summary> |
| 543 | ''' <param name="parameter"></param> |
| 544 | ''' <returns></returns> |
| 545 | ''' |
| 546 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 547 | Public Function GetByte(parameter As String) As Byte |
| 548 | Return CByte(Val(Me(parameter))) |
| 549 | End Function |
| 550 | |
| 551 | ''' <summary> |
| 552 | ''' Reads a stream Of bytes from the specified column offset into the buffer As an array, starting at the given buffer offset. |
| 553 | ''' </summary> |
| 554 | ''' <returns></returns> |
| 555 | Public Function GetBytes(parameter As String) As Byte() |
| 556 | Dim tokens As String() = Me(parameter).DefaultValue.Split(","c) |
| 557 | Return (From s As String In tokens Select CByte(Val(s))).ToArray |
| 558 | End Function |
| 559 | |
| 560 | ''' <summary> |
| 561 | ''' Gets the character value Of the specified column. |
| 562 | ''' </summary> |
| 563 | ''' <returns></returns> |
| 564 | Public Function GetChar(parameter As String) As Char |
| 565 | Dim s As String = Me(parameter) |
| 566 | |
| 567 | If String.IsNullOrEmpty(s) Then |
| 568 | Return ASCII.NUL |
| 569 | Else |
| 570 | Return s.First |
| 571 | End If |
| 572 | End Function |
| 573 | |
| 574 | ''' <summary> |
| 575 | ''' Reads a stream Of characters from the specified column offset into the buffer As an array, starting at the given buffer offset. |
| 576 | ''' </summary> |
| 577 | ''' <returns></returns> |
| 578 | ''' |
| 579 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 580 | Public Function GetChars(parameter As String) As Char() |
| 581 | Return Me(parameter) |
| 582 | End Function |
| 583 | |
| 584 | ''' <summary> |
| 585 | ''' Gets the Date And time data value Of the specified field. |
| 586 | ''' </summary> |
| 587 | ''' <returns></returns> |
| 588 | ''' |
| 589 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 590 | Public Function GetDateTime(parameter As String) As DateTime |
| 591 | Return Me(parameter).DefaultValue.ParseDateTime |
| 592 | End Function |
| 593 | |
| 594 | ''' <summary> |
| 595 | ''' Gets the fixed-position numeric value Of the specified field. |
| 596 | ''' </summary> |
| 597 | ''' <returns></returns> |
| 598 | ''' |
| 599 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 600 | Public Function GetDecimal(parameter As String) As Decimal |
| 601 | Return CDec(Val(Me(parameter).DefaultValue)) |
| 602 | End Function |
| 603 | |
| 604 | ''' <summary> |
| 605 | ''' Gets the Double-precision floating point number Of the specified field. |
| 606 | ''' </summary> |
| 607 | ''' <returns></returns> |
| 608 | ''' |
| 609 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 610 | Public Function GetDouble(parameter As String) As Double |
| 611 | Return Val(Me(parameter).DefaultValue) |
| 612 | End Function |
| 613 | |
| 614 | ''' <summary> |
| 615 | ''' Gets the Single-precision floating point number Of the specified field. |
| 616 | ''' </summary> |
| 617 | ''' <returns></returns> |
| 618 | ''' |
| 619 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 620 | Public Function GetFloat(parameter As String) As Single |
| 621 | Return CSng(Val(Me(parameter).DefaultValue)) |
| 622 | End Function |
| 623 | |
| 624 | ''' <summary> |
| 625 | ''' Returns the GUID value Of the specified field. |
| 626 | ''' </summary> |
| 627 | ''' <returns></returns> |
| 628 | ''' |
| 629 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 630 | Public Function GetGuid(parameter As String) As Guid |
| 631 | Return Guid.Parse(Me(parameter)) |
| 632 | End Function |
| 633 | |
| 634 | ''' <summary> |
| 635 | ''' Gets the 16-bit signed Integer value Of the specified field. |
| 636 | ''' </summary> |
| 637 | ''' <returns></returns> |
| 638 | ''' |
| 639 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 640 | Public Function GetInt16(parameter As String) As Int16 |
| 641 | Return CType(Val(Me(parameter).DefaultValue), Int16) |
| 642 | End Function |
| 643 | |
| 644 | ''' <summary> |
| 645 | ''' Gets the 32-bit signed Integer value Of the specified field. |
| 646 | ''' </summary> |
| 647 | ''' <returns></returns> |
| 648 | ''' |
| 649 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 650 | Public Function GetInt32(parameter As String) As Int32 |
| 651 | Return CInt(Val(Me(parameter).DefaultValue)) |
| 652 | End Function |
| 653 | |
| 654 | ''' <summary> |
| 655 | ''' Gets the 64-bit signed Integer value Of the specified field. |
| 656 | ''' </summary> |
| 657 | ''' <returns></returns> |
| 658 | ''' |
| 659 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 660 | Public Function GetInt64(parameter As String) As Int64 |
| 661 | Return CLng(Val(Me(parameter).DefaultValue)) |
| 662 | End Function |
| 663 | |
| 664 | ''' <summary> |
| 665 | ''' Return the index Of the named field. If the name is not exists in the parameter list, then a -1 value will be return. |
| 666 | ''' </summary> |
| 667 | ''' <returns></returns> |
| 668 | Public Function GetOrdinal(parameter As String) As Integer |
| 669 | Dim i% = LinqAPI.DefaultFirst(Of Integer)(-1) _ |
| 670 | _ |
| 671 | <= From entry As NamedValue(Of String) |
| 672 | In Me.__arguments |
| 673 | Where String.Equals(parameter, entry.Name, StringComparison.OrdinalIgnoreCase) |
| 674 | Select __arguments.IndexOf(entry) |
| 675 | |
| 676 | Return i |
| 677 | End Function |
| 678 | |
| 679 | ''' <summary> |
| 680 | ''' Gets the String value Of the specified field. |
| 681 | ''' </summary> |
| 682 | ''' <returns></returns> |
| 683 | ''' |
| 684 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 685 | Public Function GetString(parameter As String) As String |
| 686 | Return Me(parameter) |
| 687 | End Function |
| 688 | |
| 689 | ''' <summary> |
| 690 | ''' Return whether the specified field Is Set To null. |
| 691 | ''' </summary> |
| 692 | ''' <returns></returns> |
| 693 | ''' |
| 694 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 695 | Public Function IsNull(parameter As String) As Boolean |
| 696 | Return Not Me.ContainsParameter(parameter, False) |
| 697 | End Function |
| 698 | |
| 699 | ''' <summary> |
| 700 | ''' |
| 701 | ''' </summary> |
| 702 | ''' <typeparam name="T"></typeparam> |
| 703 | ''' <param name="parameter">Command parameter name in the command line inputs.</param> |
| 704 | ''' <param name="__getObject"></param> |
| 705 | ''' <returns></returns> |
| 706 | Public Function GetObject(Of T)(parameter$, Optional __getObject As Func(Of String, T) = Nothing) As T |
| 707 | Dim value$ = Me(parameter) |
| 708 | Dim obj = (__getObject Or StringParser(GetType(T)))(arg:=value) |
| 709 | Dim x As T = DirectCast(obj, T) |
| 710 | Return x |
| 711 | End Function |
| 712 | |
| 713 | ''' <summary> |
| 714 | ''' If the given parameter is not exists in the user input arguments, then a developer specific default value will be return. |
| 715 | ''' </summary> |
| 716 | ''' <typeparam name="T"></typeparam> |
| 717 | ''' <param name="name">The optional argument parameter name</param> |
| 718 | ''' <param name="[default]">The default value for returns when the parameter is not exists in the user input.</param> |
| 719 | ''' <param name="__ctype">The custom string parser for the CLI argument value</param> |
| 720 | ''' <returns></returns> |
| 721 | Public Function GetValue(Of T)(name$, [default] As T, Optional __ctype As Func(Of String, T) = Nothing) As T |
| 722 | If Not Me.ContainsParameter(name, False) Then |
| 723 | If GetType(T).Equals(GetType(Boolean)) Then |
| 724 | If HavebFlag(name) Then |
| 725 | Return DirectCast(DirectCast(GetBoolean(name), Object), T) |
| 726 | End If |
| 727 | End If |
| 728 | |
| 729 | Return [default] |
| 730 | End If |
| 731 | |
| 732 | Dim str As String = Me(name).DefaultValue |
| 733 | |
| 734 | If __ctype Is Nothing Then |
| 735 | Dim value As Object = InputHandler.CTypeDynamic(str, GetType(T)) |
| 736 | Return DirectCast(value, T) |
| 737 | Else |
| 738 | Return __ctype(str) |
| 739 | End If |
| 740 | End Function |
| 741 | |
| 742 | ''' <summary> |
| 743 | ''' Open a file handle by using the parameter value |
| 744 | ''' </summary> |
| 745 | ''' <param name="name">The parameter name, and its argument value should be a valid file path</param> |
| 746 | ''' <param name="[default]">Default file path if the argument value is not exists</param> |
| 747 | ''' <returns></returns> |
| 748 | Public Function OpenHandle(name$, Optional default$ = "") As int |
| 749 | Dim file As String = Me(name) |
| 750 | If String.IsNullOrEmpty(file) Then |
| 751 | file = [default] |
| 752 | End If |
| 753 | Return New int(FileHandles.OpenHandle(file)) |
| 754 | End Function |
| 755 | #End Region |
| 756 | |
| 757 | #Region "Implements IReadOnlyCollection(Of KeyValuePair(Of String, String))" |
| 758 | |
| 759 | ''' <summary> |
| 760 | ''' 这个枚举函数也会将开关给包含进来,与<see cref="ToArgumentVector"/>方法所不同的是,这个函数里面的逻辑值开关的名称没有被修饰剪裁 |
| 761 | ''' </summary> |
| 762 | ''' <returns></returns> |
| 763 | Public Iterator Function GetEnumerator() As IEnumerator(Of NamedValue(Of String)) Implements IEnumerable(Of NamedValue(Of String)).GetEnumerator |
| 764 | Dim source As New List(Of NamedValue(Of String))(Me.__arguments) |
| 765 | |
| 766 | If Not Me.BoolFlags.IsNullOrEmpty Then |
| 767 | source += From name As String |
| 768 | In BoolFlags |
| 769 | Select New NamedValue(Of String)(name, "true") |
| 770 | End If |
| 771 | |
| 772 | For Each x As NamedValue(Of String) In source |
| 773 | Yield x |
| 774 | Next |
| 775 | End Function |
| 776 | |
| 777 | Public Iterator Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator |
| 778 | Yield GetEnumerator() |
| 779 | End Function |
| 780 | |
| 781 | ''' <summary> |
| 782 | ''' Adds an item to the System.Collections.Generic.ICollection`1. |
| 783 | ''' </summary> |
| 784 | ''' <param name="item"></param> |
| 785 | ''' |
| 786 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 787 | Public Sub Add(item As NamedValue(Of String)) Implements ICollection(Of NamedValue(Of String)).Add |
| 788 | Call __arguments.Add(item) |
| 789 | End Sub |
| 790 | |
| 791 | ''' <summary> |
| 792 | ''' Add a parameter with name and its value. |
| 793 | ''' </summary> |
| 794 | ''' <param name="key"></param> |
| 795 | ''' <param name="value"></param> |
| 796 | Public Sub Add(key$, value$, |
| 797 | Optional allowDuplicated As Boolean = False, |
| 798 | <CallerMemberName> Optional stack$ = Nothing) |
| 799 | |
| 800 | Dim item As New NamedValue(Of String) With { |
| 801 | .Name = key.ToLower, |
| 802 | .Value = value, |
| 803 | .Description = stack & "->" & NameOf(Add) |
| 804 | } |
| 805 | |
| 806 | If Not allowDuplicated Then |
| 807 | For i As Integer = 0 To __arguments.Count - 1 |
| 808 | With __arguments(i) |
| 809 | If .Name.TextEquals(key) Then |
| 810 | __arguments(i) = item |
| 811 | Return |
| 812 | End If |
| 813 | End With |
| 814 | Next |
| 815 | |
| 816 | ' 没有查找到需要被替换掉的下标,则直接在下面的代码之中进行添加 |
| 817 | End If |
| 818 | |
| 819 | __arguments += item |
| 820 | End Sub |
| 821 | |
| 822 | ''' <summary> |
| 823 | ''' Clear the inner list buffer |
| 824 | ''' </summary> |
| 825 | ''' |
| 826 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 827 | Public Sub Clear() Implements ICollection(Of NamedValue(Of String)).Clear |
| 828 | Call __arguments.Clear() |
| 829 | End Sub |
| 830 | |
| 831 | ''' <summary> |
| 832 | ''' 只是通过比较名称来判断是否存在,值没有进行比较 |
| 833 | ''' </summary> |
| 834 | ''' <param name="item"></param> |
| 835 | ''' <returns></returns> |
| 836 | Public Function Contains(item As NamedValue(Of String)) As Boolean Implements ICollection(Of NamedValue(Of String)).Contains |
| 837 | Dim LQuery% = LinqAPI.DefaultFirst(-1) _ |
| 838 | _ |
| 839 | <= From obj As NamedValue(Of String) |
| 840 | In Me.__arguments |
| 841 | Where String.Equals(obj.Name, item.Name, StringComparison.OrdinalIgnoreCase) |
| 842 | Select 100 |
| 843 | |
| 844 | Return LQuery > 50 |
| 845 | End Function |
| 846 | |
| 847 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 848 | Public Sub CopyTo(array() As NamedValue(Of String), arrayIndex As Integer) Implements ICollection(Of NamedValue(Of String)).CopyTo |
| 849 | Call __arguments.ToArray.CopyTo(array, arrayIndex) |
| 850 | End Sub |
| 851 | |
| 852 | ''' <summary> |
| 853 | ''' Get the switch counts in this commandline object.(获取本命令行对象中的所定义的开关的数目) |
| 854 | ''' </summary> |
| 855 | ''' <value></value> |
| 856 | ''' <returns></returns> |
| 857 | ''' <remarks></remarks> |
| 858 | Public ReadOnly Property Count As Integer Implements ICollection(Of NamedValue(Of String)).Count |
| 859 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 860 | Get |
| 861 | Return Me.__arguments.Count |
| 862 | End Get |
| 863 | End Property |
| 864 | |
| 865 | Private ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of NamedValue(Of String)).IsReadOnly |
| 866 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 867 | Get |
| 868 | Return True |
| 869 | End Get |
| 870 | End Property |
| 871 | |
| 872 | ''' <summary> |
| 873 | ''' Removes a parameter by name |
| 874 | ''' </summary> |
| 875 | ''' <param name="paramName"></param> |
| 876 | ''' <returns></returns> |
| 877 | Public Function Remove(paramName As String) As Boolean |
| 878 | Dim LQuery = LinqAPI.DefaultFirst(Of NamedValue(Of String)) _ |
| 879 | _ |
| 880 | () <= From obj As NamedValue(Of String) |
| 881 | In Me.__arguments |
| 882 | Where String.Equals(obj.Name, paramName, StringComparison.OrdinalIgnoreCase) |
| 883 | Select obj |
| 884 | |
| 885 | If LQuery.IsEmpty Then |
| 886 | Return False |
| 887 | Else |
| 888 | Call __arguments.Remove(LQuery) |
| 889 | Return True |
| 890 | End If |
| 891 | End Function |
| 892 | |
| 893 | ''' <summary> |
| 894 | ''' Removes a parameter by <see cref="NamedValue(Of String).Name"/> |
| 895 | ''' </summary> |
| 896 | ''' <param name="item"></param> |
| 897 | ''' <returns></returns> |
| 898 | ''' |
| 899 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 900 | Public Function Remove(item As NamedValue(Of String)) As Boolean Implements ICollection(Of NamedValue(Of String)).Remove |
| 901 | Return Remove(item.Name) |
| 902 | End Function |
| 903 | #End Region |
| 904 | |
| 905 | ''' <summary> |
| 906 | ''' 将当前的这个命令行对象之中的所有的参数值都合并到一个向量之中返回. |
| 907 | ''' (``ToArray``拓展好像是有BUG的,所以请使用这个函数来获取所有的参数信息。 |
| 908 | ''' 请注意,逻辑值开关的名称会被去掉前缀) |
| 909 | ''' </summary> |
| 910 | ''' <returns></returns> |
| 911 | Public Function ToArgumentVector() As NamedValue(Of String)() |
| 912 | Dim list As New List(Of NamedValue(Of String)) |
| 913 | |
| 914 | list += From arg As NamedValue(Of String) |
| 915 | In __arguments.SafeQuery |
| 916 | Select New NamedValue(Of String) With { |
| 917 | .Name = arg.Name, |
| 918 | .Value = arg.Value |
| 919 | } |
| 920 | list += From bs As String |
| 921 | In BoolFlags.SafeQuery |
| 922 | Select New NamedValue(Of String) With { |
| 923 | .Name = bs, |
| 924 | .Value = "True" |
| 925 | } |
| 926 | |
| 927 | Return list |
| 928 | End Function |
| 929 | |
| 930 | ''' <summary> |
| 931 | ''' Open a handle for a file system object. |
| 932 | ''' </summary> |
| 933 | ''' <param name="args"></param> |
| 934 | ''' <param name="fs"></param> |
| 935 | ''' <returns></returns> |
| 936 | Public Overloads Shared Operator +(args As CommandLine, fs$) As Integer |
| 937 | Dim path As String = args(fs) |
| 938 | Return FileHandles.OpenHandle(path) |
| 939 | End Operator |
| 940 | |
| 941 | ''' <summary> |
| 942 | ''' Gets the CLI parameter value. |
| 943 | ''' </summary> |
| 944 | ''' <param name="args"></param> |
| 945 | ''' <param name="name"></param> |
| 946 | ''' <returns></returns> |
| 947 | Public Overloads Shared Operator <=(args As CommandLine, name$) As String |
| 948 | If args Is Nothing Then |
| 949 | Return Nothing |
| 950 | Else |
| 951 | Return args(name) |
| 952 | End If |
| 953 | End Operator |
| 954 | |
| 955 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 956 | Public Shared Operator <=(opt As String, args As CommandLine) As CommandLine |
| 957 | Return TryParse(args(opt)) |
| 958 | End Operator |
| 959 | |
| 960 | Public Shared Operator ^(args As CommandLine, [default] As String) As String |
| 961 | If args Is Nothing OrElse String.IsNullOrEmpty(args.CLICommandArgvs) Then |
| 962 | Return [default] |
| 963 | Else |
| 964 | Return args.CLICommandArgvs |
| 965 | End If |
| 966 | End Operator |
| 967 | |
| 968 | Public Shared Operator >=(opt As String, args As CommandLine) As CommandLine |
| 969 | Throw New NotSupportedException |
| 970 | End Operator |
| 971 | |
| 972 | ''' <summary> |
| 973 | ''' Try get parameter value. |
| 974 | ''' </summary> |
| 975 | ''' <param name="args"></param> |
| 976 | ''' <param name="name"></param> |
| 977 | ''' <returns></returns> |
| 978 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 979 | Public Overloads Shared Operator -(args As CommandLine, name As String) As String |
| 980 | Return args(name) |
| 981 | End Operator |
| 982 | |
| 983 | ''' <summary> |
| 984 | ''' Try get parameter value. |
| 985 | ''' </summary> |
| 986 | ''' <param name="args"></param> |
| 987 | ''' <returns></returns> |
| 988 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 989 | Public Overloads Shared Operator -(args As CommandLine, null As CommandLine) As CommandLine |
| 990 | Return args |
| 991 | End Operator |
| 992 | |
| 993 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 994 | Public Overloads Shared Operator -(args As CommandLine, name As IEnumerable(Of String)) As String |
| 995 | Return args.GetValue(name.First, name.Last) |
| 996 | End Operator |
| 997 | |
| 998 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 999 | Public Shared Operator -(args As CommandLine) As CommandLine |
| 1000 | Return args |
| 1001 | End Operator |
| 1002 | |
| 1003 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 1004 | Public Shared Operator >(args As CommandLine, name As String) As String |
| 1005 | Return args(name) |
| 1006 | End Operator |
| 1007 | |
| 1008 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
| 1009 | Public Shared Operator <(args As CommandLine, name As String) As String |
| 1010 | Return args(name) |
| 1011 | End Operator |
| 1012 | |
| 1013 | Public Shared Operator >=(args As CommandLine, name As String) As String |
| 1014 | Throw New NotSupportedException |
| 1015 | End Operator |
| 1016 | End Class |
| 1017 | End Namespace |