1 #Region "Microsoft.VisualBasic::85142a2f0041cc8218b7114fa9ad9b2f, Microsoft.VisualBasic.Core\ApplicationServices\Terminal\PrintAsTable.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     '     Module PrintAsTable
35     
36     '         Function: (+2 Overloads) Print
37     
38     '         Sub: Print, PrintTable
39     '         Delegate Sub
40     
41     '             Sub: (+3 Overloads) Print, printInternal
42     
43     
44     
45     ' /********************************************************************************/
46
47 #End Region
48
49 Imports System.IO
50 Imports System.Runtime.CompilerServices
51 Imports System.Text
52 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
53 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel.SchemaMaps
54 Imports Microsoft.VisualBasic.Language
55 Imports Microsoft.VisualBasic.Linq
56
57 Namespace ApplicationServices.Terminal
58
59     Public Module PrintAsTable
60
61         ''' <summary>
62         ''' Returns a text string 
63         ''' </summary>
64         ''' <typeparam name="T"></typeparam>
65         ''' <param name="source"></param>
66         ''' <param name="addBorder"></param>
67         ''' <returns></returns>
68         <Extension>
69         Public Function Print(Of T)(source As IEnumerable(Of T), Optional addBorder As Boolean = TrueAs String
70             Dim out As New StringBuilder
71             Dim dev As New StringWriter(out)
72             Call source.Print(dev, addBorder)
73             Return out.ToString
74         End Function
75
76         <Extension>
77         Public Function Print(source As IEnumerable(Of String()), Optional addBorder As Boolean = TrueAs String
78             Dim out As New StringBuilder
79             Dim dev As New StringWriter(out)
80
81             If addBorder Then
82                 Call source.PrintTable(dev, sep:=" "c)
83             Else
84                 Call source.Print(dev, sep:=" "c)
85             End If
86
87             Return out.ToString
88         End Function
89
90         ''' <summary>
91         ''' Print the object collection as table on the console 
92         ''' </summary>
93         ''' <typeparam name="T"></typeparam>
94         ''' <param name="source"></param>
95         ''' <param name="dev"></param>
96         ''' <param name="addBorder"></param>
97         <Extension>
98         Public Sub Print(Of T)(source As IEnumerable(Of T), dev As TextWriter, Optional addBorder As Boolean = True)
99             Dim schema = LinqAPI.Exec(Of BindProperty(Of DataFrameColumnAttribute)) _
100  _
101                 () <= From x As BindProperty(Of DataFrameColumnAttribute)
102                       In DataFrameColumnAttribute _
103                           .LoadMapping(Of T)(mapsAll:=True) _
104                           .Values
105                       Where x.IsPrimitive
106                       Select x
107
108             Dim titles As String() = schema.Select(Function(x) x.Identity).ToArray
109             Dim contents = LinqAPI.Exec(Of Dictionary(Of StringString)) _
110  _
111                 () <= From x As T
112                       In source
113                       Select (From p As BindProperty(Of DataFrameColumnAttribute)
114                               In schema
115                               Select p,
116                                   s = p.GetValue(x)) _
117                           .ToDictionary(Function(o) o.p.Identity,
118                                         Function(o) Scripting.ToString(o.s))
119
120             Dim table As List(Of String()) =
121                 contents _
122                 .Select(Function(line)
123                             Return titles.Select(Function(name) line(name)).ToArray
124                         End Function) _
125                 .AsList
126
127             If addBorder Then
128                 Call (titles + table).PrintTable(dev, sep:=" "c)
129             Else
130                 Call (titles + table).Print(dev, sep:=" "c)
131             End If
132         End Sub
133
134         ''' <summary>
135         ''' 与函数<see cref="Print"/>所不同的是,这个函数还会添加边框
136         ''' </summary>
137         ''' <param name="source"></param>
138         ''' <param name="dev"></param>
139         ''' <param name="sep"></param>
140         <Extension>
141         Public Sub PrintTable(source As IEnumerable(Of String()),
142                               Optional dev As TextWriter = Nothing,
143                               Optional sep As Char = " "c,
144                               Optional title$() = Nothing,
145                               Optional trilinearTable As Boolean = False,
146                               Optional leftMargin% = 0)
147
148             Dim printHead As Boolean = False
149             Dim table$()() = source.ToArray
150             Dim printOfHead As printOnDevice =
151                 Sub(row, width, maxLen, device)
152
153                     If leftMargin > 0 Then
154                         Call device.Write(New String(sep, leftMargin))
155                     End If
156
157                     Call device.Write("+")
158                     Call device.Write(maxLen.Select(Function(l) New String("-"c, l)).JoinBy("+"))
159                     Call device.Write("+")
160                     Call device.WriteLine()
161                 End Sub
162
163             If Not title Is Nothing Then
164                 table = title.Join(table).ToArray
165             End If
166
167             Call table.printInternal(
168                 dev, 2, Sub(row, width, maxLen, device)
169                             Dim offset% = 0
170
171                             If Not printHead Then
172                                 Call printOfHead(Nothing, width, maxLen, device)
173                             End If
174                             If leftMargin > 0 Then
175                                 Call device.Write(New String(sep, leftMargin))
176                             End If
177                             If Not trilinearTable Then
178                                 Call device.Write("|")
179                             End If
180
181                             For i As Integer = 0 To width - 1
182                                 If row(i) Is Nothing Then
183                                     row(i) = ""
184                                 End If
185
186                                 If trilinearTable Then
187                                     device.Write(" ")
188                                 End If
189
190                                 offset = maxLen(i) - row(i).Length - 1
191                                 device.Write(" " & row(i) & New String(sep, offset))
192
193                                 If Not trilinearTable Then
194                                     Call device.Write("|")
195                                 End If
196                             Next
197
198                             Call device.WriteLine()
199
200                             If Not printHead Then
201                                 Call printOfHead(Nothing, width, maxLen, device)
202                                 printHead = True
203                             End If
204                         End Sub, printOfHead)
205         End Sub
206
207         Private Delegate Sub printOnDevice(row$(), width%, maxLen%(), device As TextWriter)
208
209         <Extension>
210         Private Sub printInternal(table$()(), dev As TextWriter, distance%, printLayout As printOnDevice, Optional final As printOnDevice = Nothing)
211             With dev Or Console.Out.AsDefault
212                 Dim width% = table.Max(Function(row) row.Length)
213                 Dim index%
214                 Dim maxLen%() = New Integer(width - 1) {}
215
216                 ' 按照列计算出layout偏移量
217                 For i As Integer = 0 To width - 1
218                     index = i
219                     maxLen(index) = table _
220                         .Select(Function(row) row.ElementAtOrDefault(index)) _
221                         .Select(Function(s)
222                                     If String.IsNullOrEmpty(s) Then
223                                         Return 0
224                                     Else
225                                         Return s.Length
226                                     End If
227                                 End Function) _
228                         .Max + distance
229                 Next
230
231                 For Each row As String() In table
232                     Call printLayout(row, width, maxLen, .ByRef)
233                 Next
234
235                 If Not final Is Nothing Then
236                     Call final(Nothing, width, maxLen, .ByRef)
237                 End If
238
239                 Call .Flush()
240             End With
241         End Sub
242
243         ''' <summary>
244         ''' Print the string matrix collection <paramref name="source"/> in table layout.
245         ''' </summary>
246         ''' <param name="source">The string matrix collection.</param>
247         ''' <param name="dev">The output device</param>
248         ''' <param name="sep"></param>
249         <MethodImpl(MethodImplOptions.AggressiveInlining)>
250         <Extension>
251         Public Sub Print(source As IEnumerable(Of String()), Optional dev As TextWriter = NothingOptional sep As Char = " "c, Optional distance% = 2)
252             Call source _
253                 .ToArray _
254                 .printInternal(
255                     dev, distance, Sub(row, width, maxLen, device)
256                                        Dim offset% = 0
257
258                                        For i As Integer = 0 To width - 1
259                                            If row(i) Is Nothing Then
260                                                row(i) = ""
261                                            End If
262
263                                            device.Write(New String(sep, offset) & row(i))
264                                            offset = maxLen(i) - row(i).Length
265                                        Next
266
267                                        Call device.WriteLine()
268                                    End Sub)
269         End Sub
270
271         ''' <summary>
272         ''' Print the string dictionary as table
273         ''' </summary>
274         ''' <param name="table"></param>
275         ''' <param name="dev"></param>
276         ''' <param name="sep"></param>
277         <MethodImpl(MethodImplOptions.AggressiveInlining)>
278         <Extension>
279         Public Sub Print(table As Dictionary(Of StringString),
280                          Optional dev As TextWriter = Nothing,
281                          Optional sep As Char = " "c,
282                          Optional distance% = 2)
283             Call {
284                 New String() {"Item""Value"}
285             } _
286             .Join(table.Select(Function(map) {map.Key, map.Value})) _
287             .Print(dev, sep, distance)
288         End Sub
289
290         <MethodImpl(MethodImplOptions.AggressiveInlining)>
291         <Extension>
292         Public Sub Print(data As IEnumerable(Of NamedValue(Of String)),
293                          Optional dev As TextWriter = Nothing,
294                          Optional sep As Char = " "c,
295                          Optional trilinearTable As Boolean = False,
296                          Optional leftMargin% = 0)
297             Call {
298                 New String() {"Name""Value""Description"}
299             } _
300             .Join(data.Select(Function(item)
301                                   Return {item.Name, item.Value, item.Description}
302                               End Function)) _
303             .PrintTable(
304                 dev,
305                 sep,
306                 trilinearTable:=trilinearTable,
307                 leftMargin:=leftMargin)
308         End Sub
309     End Module
310 End Namespace