1 #Region "Microsoft.VisualBasic::7b06ea52dd2da50d815859fef8b03eae, Microsoft.VisualBasic.Core\ComponentModel\Settings\ConfigEngine.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 ConfigEngine
35     
36     '         Properties: AllItems, FilePath, ProfileItemNode, ProfileItemType
37     
38     '         Constructor: (+2 OverloadsSub New
39     
40     '         Function: (+2 Overloads) [Set], __getDefaultPath, ExistsNode, GetName, GetSettings
41     '                   GetSettingsNode, Load, (+2 Overloads) Prints, Save, ToString
42     '                   View
43     
44     '         Sub: Dispose
45     
46     
47     ' /********************************************************************************/
48
49 #End Region
50
51 Imports System.Reflection
52 Imports System.Runtime.CompilerServices
53 Imports System.Text
54 Imports Microsoft.VisualBasic.CommandLine.Reflection
55 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
56 Imports Microsoft.VisualBasic.Language
57 Imports Microsoft.VisualBasic.Linq
58
59 Namespace ComponentModel.Settings
60
61     ''' <summary>
62     ''' 只包含有对数据映射目标对象的属性读写,并不包含有文件数据的读写操作
63     ''' </summary>
64     ''' 
65     Public Class ConfigEngine : Inherits ITextFile
66         Implements IDisposable
67
68         ''' <summary>
69         ''' 所映射的数据源
70         ''' </summary>
71         Protected settingsData As IProfile
72         ''' <summary>
73         ''' 键名都是小写的
74         ''' </summary>
75         Protected profileItemCollection As IDictionary(Of String, BindMapping)
76
77         ''' <summary>
78         ''' List all of the available settings nodes in this profile data session.
79         ''' (枚举出当前配置会话之中的所有可用的配置节点)
80         ''' </summary>
81         ''' <value></value>
82         ''' <returns></returns>
83         ''' <remarks></remarks>
84         Public ReadOnly Property AllItems As BindMapping()
85             <MethodImpl(MethodImplOptions.AggressiveInlining)>
86             Get
87                 Return profileItemCollection _
88                     .Values _
89                     .ToArray
90             End Get
91         End Property
92
93         Public Overrides Property FilePath As String
94             Get
95                 Return settingsData.FilePath
96             End Get
97             Set(value As String)
98                 settingsData.FilePath = value
99             End Set
100         End Property
101
102         Sub New(obj As IProfile)
103             settingsData = obj
104             profileItemCollection = ConfigEngine.Load(Of IProfile)(
105                 obj.GetType,
106                 obj:=settingsData
107             ).ToDictionary(Function(x) x.Name,
108                            Function(x) x.Value)
109         End Sub
110
111         Protected Sub New()
112         End Sub
113
114         Protected Friend Shared Function Load(Of EntityType)(type As Type, obj As EntityType) As NamedValue(Of BindMapping)()
115             Dim LQuery As IEnumerable(Of BindMapping) =
116  _
117                 From [Property] As PropertyInfo
118                 In type.GetProperties
119                 Let attributes = [Property].GetCustomAttributes(attributeType:=ProfileItemType, inherit:=False)
120                 Where attributes.Length > 0
121                 Let attr = DirectCast(attributes(0), ProfileItem)
122                 Select BindMapping.Initialize(attr, [Property], obj) '
123
124             Dim out As List(Of NamedValue(Of BindMapping)) =
125                 LinqAPI.MakeList(Of NamedValue(Of BindMapping)) <=
126  _
127                     From ProfileItem As BindMapping
128                     In LQuery
129                     Let name As String = GetName(ProfileItem, ProfileItem.BindProperty)
130                     Select New NamedValue(Of BindMapping) With {
131                         .Name = name,
132                         .Value = ProfileItem
133                     }
134
135             Dim Nodes = From [property] As PropertyInfo
136                         In type.GetProperties
137                         Let attributes = [property].GetCustomAttributes(attributeType:=ProfileItemNode, inherit:=False)
138                         Where attributes.Length = 1
139                         Select New With {
140                             .[Property] = [property],
141                             .Entity = [property].GetValue(obj, Nothing)
142                         } ' 在这里是用匿名类型而不是直接使用Linq的匿名类型的原因是在后面还需要进行赋值操作,而Linq的匿名类型的属性是ReadOnly的
143
144             Dim innerNodes = Nodes.ToArray
145
146             If innerNodes.Length > 0 Then
147                 For Each x In innerNodes
148
149                     If x.Entity Is Nothing Then
150                         Try
151                             x.Entity = Activator.CreateInstance(type:=x.[Property].PropertyType)
152                         Catch ex As Exception
153                             Dim view As String =
154                                 $"{x.Property.Name} As {x.Property.PropertyType.FullName}"
155                             ex = New Exception(view, ex)
156
157                             Throw ex
158                         Finally
159                             Call x.Property.SetValue(obj, x.Entity, Nothing)
160                         End Try
161                     End If
162
163                     out += Load(x.[Property].PropertyType, x.Entity)
164                 Next
165
166                 Return out.ToArray
167             End If
168
169             Return out.ToArray
170         End Function
171
172         Protected Shared Function GetName(ProfileItem As ProfileItem, [Property] As PropertyInfo) As String
173             If String.IsNullOrEmpty(ProfileItem.Name) Then
174                 ProfileItem.Name = [Property].Name.ToLower
175             End If
176             Return ProfileItem.Name.ToLower
177         End Function
178
179         ''' <summary>
180         ''' 大小写不敏感
181         ''' </summary>
182         ''' <param name="Name"></param>
183         ''' <returns></returns>
184         <MethodImpl(MethodImplOptions.AggressiveInlining)>
185         <ExportAPI("Node.Exists")>
186         Public Overridable Function ExistsNode(Name As StringAs Boolean
187             Return profileItemCollection.ContainsKey(Name.ToLower)
188         End Function
189
190         ''' <summary>
191         ''' 请注意,<paramref name="name"/>必须是小写的
192         ''' </summary>
193         ''' <param name="Name">The name of the configuration entry should be in lower case.</param>
194         ''' <param name="Value"></param>
195         ''' <returns></returns>
196         ''' 
197         <ExportAPI("SetValue")>
198         Public Overridable Function [Set](Name As String, Value As StringAs Boolean
199             Dim keyFind As String = Name.ToLower
200
201             If profileItemCollection.ContainsKey(keyFind) Then
202                 Call profileItemCollection(keyFind).Set(Value)
203             Else
204                 Return False
205             End If
206
207             Return True
208         End Function
209
210         <MethodImpl(MethodImplOptions.AggressiveInlining)>
211         Public Function [Set](var As NamedValue(Of String)) As Boolean
212             Return [Set](var.Name, var.Value)
213         End Function
214
215         <ExportAPI("GetValue")>
216         Public Overridable Function GetSettings(Name As StringAs String
217             Dim keyFind As String = Name.ToLower
218
219             If profileItemCollection.ContainsKey(keyFind) Then
220                 Dim item = profileItemCollection(keyFind)
221                 Dim result = item.Value
222                 Return result
223             Else
224                 Return ""
225             End If
226         End Function
227
228         ''' <summary>
229         '''假若函数参数<paramref name="name"/>为空,则函数输出所有的变量的值,请注意,这个函数并不在终端上面显示任何消息
230         ''' </summary>
231         ''' <param name="name">假若本参数为空,则函数输出所有的变量的值,大小写不敏感的</param>
232         ''' <returns></returns>
233         ''' <remarks></remarks>
234         ''' 
235         <ExportAPI("View")>
236         Public Overridable Function View(Optional name As String = ""As String
237             If String.IsNullOrEmpty(name) Then
238                 Return Prints(Me.profileItemCollection.Values)
239             Else
240                 Return GetSettingsNode(name).AsOutString
241             End If
242         End Function
243
244         <ExportAPI("Prints")>
245         Public Shared Function Prints(data As IEnumerable(Of BindMapping)) As String
246             Dim source As NamedValue(Of String)() =
247                 LinqAPI.Exec(Of NamedValue(Of String)) <=
248  _
249                 From x As BindMapping
250                 In data
251                 Let value As String =
252                     If(String.IsNullOrEmpty(x.Value), "null", x.Value)
253                 Select New NamedValue(Of StringWith {
254                     .Name = x.Name,
255                     .Value = value,
256                     .Description = x.Description
257                 }
258
259             Return Prints(source)
260         End Function
261
262         <ExportAPI("Prints")>
263         Public Shared Function Prints(data As IEnumerable(Of NamedValue(Of String))) As String
264             Dim keys As String() = data.Select(Function(x) x.Name).ToArray
265             Dim maxLen As Integer = keys.Select(AddressOf Len).Max
266             Dim sb As New StringBuilder(New String("-"c, 120))
267
268             Call sb.AppendLine()
269
270             For Each line As NamedValue(Of StringIn data
271                 Dim blank As String = New String(" "c, maxLen - Len(line.Name) + 2)
272                 Dim str As String = String.Format("  {0}{1}  = {2}", line.Name, blank, line.Value)
273
274                 If Not String.IsNullOrEmpty(line.Description) Then
275                     str &= "     // " & line.Description
276                 End If
277
278                 Call sb.AppendLine(str)
279             Next
280
281             Return sb.ToString
282         End Function
283
284         ''' <summary>
285         ''' 大小写不敏感的
286         ''' </summary>
287         ''' <param name="Name"></param>
288         ''' <returns></returns>
289         <MethodImpl(MethodImplOptions.AggressiveInlining)>
290         <ExportAPI("GetNode")>
291         Public Function GetSettingsNode(Name As StringAs BindMapping
292             Return profileItemCollection(Name.ToLower)
293         End Function
294
295         Public Overrides Function ToString() As String
296             Return settingsData.FilePath
297         End Function
298
299         <ExportAPI("Save")>
300         Public Overrides Function Save(Optional FilePath As String = ""Optional Encoding As Encoding = NothingAs Boolean
301             Dim Xml As String = settingsData.GetXml
302             Return Xml.SaveTo(getPath(FilePath), Encoding)
303         End Function
304
305         Protected Friend Shared ReadOnly Property ProfileItemType As Type = GetType(ProfileItem)
306         Protected Friend Shared ReadOnly Property ProfileItemNode As Type = GetType(ProfileNodeItem)
307
308 #Region "IDisposable Support"
309         ' IDisposable
310         Protected Overrides Sub Dispose(disposing As Boolean)
311             If Not Me.disposedValue Then
312                 If disposing Then
313                     ' TODO:  释放托管状态(托管对象)。
314                     Call settingsData.Save()
315                 End If
316
317                 ' TODO:  释放非托管资源(非托管对象)并重写下面的 Finalize()。
318                 ' TODO:  将大型字段设置为 null。
319             End If
320             Me.disposedValue = True
321         End Sub
322 #End Region
323
324         Protected Overrides Function __getDefaultPath() As String
325             Return FilePath
326         End Function
327     End Class
328 End Namespace