1+ using System . ComponentModel ;
2+ using System . Reflection ;
3+
4+ namespace parsley
5+ {
6+ public class Parser : IParser
7+ {
8+ protected char Delimiter { get ; set ; }
9+
10+ public Parser ( ) : this ( ',' )
11+ {
12+ }
13+
14+ public Parser ( char delimiter )
15+ {
16+ Delimiter = delimiter ;
17+ }
18+
19+ public T [ ] Parse < T > ( string filepath ) where T : IFileLine , new ( )
20+ {
21+ if ( string . IsNullOrEmpty ( filepath ) || ! File . Exists ( filepath ) )
22+ return [ ] ;
23+
24+ var lines = ReadToLines ( filepath ) ;
25+
26+ return Parse < T > ( lines ) ;
27+ }
28+
29+ public T [ ] Parse < T > ( string [ ] lines ) where T : IFileLine , new ( )
30+ {
31+ if ( lines == null || lines . Length == 0 )
32+ return [ ] ;
33+
34+ var list = new T [ lines . Length ] ;
35+
36+ var objLock = new object ( ) ;
37+
38+ var index = 0 ;
39+ var inputs = lines . Select ( line => new { Line = line , Index = index ++ } ) ;
40+
41+ Parallel . ForEach ( inputs , ( ) => new List < T > ( ) ,
42+ ( obj , loopstate , localStorage ) =>
43+ {
44+ var parsed = ParseLine < T > ( obj . Line ) ;
45+
46+ parsed . Index = obj . Index ;
47+
48+ localStorage . Add ( parsed ) ;
49+ return localStorage ;
50+ } ,
51+ finalStorage =>
52+ {
53+ if ( finalStorage == null )
54+ return ;
55+
56+ lock ( objLock )
57+ finalStorage . ForEach ( f => list [ f . Index ] = f ) ;
58+ } ) ;
59+
60+ return list ;
61+ }
62+
63+ private string [ ] ReadToLines ( string path )
64+ {
65+ var lines = new List < string > ( ) ;
66+
67+ foreach ( var line in File . ReadLines ( path ) )
68+ {
69+ if ( line != null )
70+ lines . Add ( line ) ;
71+ }
72+
73+ return lines . ToArray ( ) ;
74+ }
75+
76+ private T ParseLine < T > ( string line ) where T : IFileLine , new ( )
77+ {
78+ var obj = new T ( ) ;
79+
80+ var values = GetDelimiterSeparatedValues ( line ) ;
81+
82+ if ( values . Length == 0 || values . Length == 1 )
83+ {
84+ obj . SetError ( Resources . InvalidLineFormat ) ;
85+ return obj ;
86+ }
87+
88+ var propInfos = GetLineClassPropertyInfos < T > ( ) ;
89+
90+ if ( propInfos . Length == 0 )
91+ {
92+ obj . SetError ( string . Format ( Resources . NoColumnAttributesFoundFormat , typeof ( T ) . Name ) ) ;
93+ return obj ;
94+ }
95+
96+ if ( propInfos . Length != values . Length )
97+ {
98+ obj . SetError ( Resources . InvalidLengthErrorFormat ) ;
99+ return obj ;
100+ }
101+
102+ foreach ( var propInfo in propInfos )
103+ try
104+ {
105+ var attribute = ( ColumnAttribute ) propInfo . GetCustomAttributes ( typeof ( ColumnAttribute ) , true ) . First ( ) ;
106+
107+ var pvalue = values [ attribute . Index ] ;
108+
109+ if ( string . IsNullOrWhiteSpace ( pvalue ) && attribute . DefaultValue != null )
110+ pvalue = attribute . DefaultValue . ToString ( ) ;
111+
112+ if ( propInfo . PropertyType . IsEnum )
113+ {
114+ if ( string . IsNullOrWhiteSpace ( pvalue ) )
115+ {
116+ obj . SetError ( string . Format ( Resources . InvalidEnumValueErrorFormat , propInfo . Name ) ) ;
117+ continue ;
118+ }
119+
120+ if ( long . TryParse ( pvalue , out var enumLong ) )
121+ {
122+ var numeric = Enum . ToObject ( propInfo . PropertyType , enumLong ) ;
123+ propInfo . SetValue ( obj , numeric , null ) ;
124+ continue ;
125+ }
126+
127+ var val = Enum . Parse ( propInfo . PropertyType , pvalue , true ) ;
128+ propInfo . SetValue ( obj , val , null ) ;
129+ continue ;
130+ }
131+
132+ var converter = TypeDescriptor . GetConverter ( propInfo . PropertyType ) ;
133+ var value = converter . ConvertFrom ( pvalue ) ;
134+
135+ propInfo . SetValue ( obj , value , null ) ;
136+ }
137+ catch ( Exception e )
138+ {
139+ obj . SetError ( string . Format ( Resources . LineExceptionFormat , propInfo . Name , e . Message ) ) ;
140+ }
141+
142+ return obj ;
143+ }
144+
145+ private static PropertyInfo [ ] GetLineClassPropertyInfos < T > ( ) where T : IFileLine , new ( )
146+ {
147+ var propInfos = typeof ( T ) . GetProperties ( )
148+ . Where ( p => p . GetCustomAttributes ( typeof ( ColumnAttribute ) , true ) . Any ( ) && p . CanWrite )
149+ . ToArray ( ) ;
150+ return propInfos ;
151+ }
152+
153+ private string [ ] GetDelimiterSeparatedValues ( string line )
154+ {
155+ var values = line . Split ( Delimiter )
156+ . Select ( x => ! string . IsNullOrWhiteSpace ( x ) ? x . Trim ( ) : x )
157+ . ToArray ( ) ;
158+ return values ;
159+ }
160+ }
161+ }
0 commit comments