1
+ // Copyright(c) 2007 Andreas Gullberg Larsen
2
+ // https://github.com/anjdreas/UnitsNet
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in
12
+ // all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ // THE SOFTWARE.
21
+
22
+ using System ;
23
+ using System . Collections . Generic ;
24
+ using System . Diagnostics . CodeAnalysis ;
25
+ using System . Globalization ;
26
+ using System . Linq ;
27
+ using System . Text . RegularExpressions ;
28
+ using JetBrains . Annotations ;
29
+
30
+ namespace UnitsNet
31
+ {
32
+ internal delegate TUnit ParseUnit < out TUnit > ( string value , string unit , IFormatProvider formatProvider = null ) ;
33
+
34
+ internal static class UnitParser
35
+ {
36
+ [ SuppressMessage ( "ReSharper" , "UseStringInterpolation" ) ]
37
+ internal static TUnit ParseUnit < TUnit > ( [ NotNull ] string str ,
38
+ [ CanBeNull ] IFormatProvider formatProvider ,
39
+ [ NotNull ] ParseUnit < TUnit > parseUnit ,
40
+ [ NotNull ] Func < TUnit , TUnit , TUnit > add )
41
+ {
42
+ if ( str == null ) throw new ArgumentNullException ( nameof ( str ) ) ;
43
+ if ( parseUnit == null ) throw new ArgumentNullException ( nameof ( parseUnit ) ) ;
44
+ if ( add == null ) throw new ArgumentNullException ( nameof ( add ) ) ;
45
+
46
+ var numFormat = formatProvider != null
47
+ ? ( NumberFormatInfo ) formatProvider . GetFormat ( typeof ( NumberFormatInfo ) )
48
+ : NumberFormatInfo . CurrentInfo ;
49
+
50
+ string numRegex = string . Format ( @"[\d., {0}{1}]*\d" ,
51
+ // allows digits, dots, commas, and spaces in the quantity (must end in digit)
52
+ numFormat . NumberGroupSeparator , // adds provided (or current) culture's group separator
53
+ numFormat . NumberDecimalSeparator ) ; // adds provided (or current) culture's decimal separator
54
+
55
+ const string exponentialRegex = @"(?:[eE][-+]?\d+)?)" ;
56
+
57
+ string regexString = string . Format ( @"(?:\s*(?<value>[-+]?{0}{1}{2}{3})?{4}{5}" ,
58
+ numRegex , // capture base (integral) Quantity value
59
+ exponentialRegex , // capture exponential (if any), end of Quantity capturing
60
+ @"\s?" , // ignore whitespace (allows both "1kg", "1 kg")
61
+ @"(?<unit>[^\s\d,]+)" , // capture Unit (non-whitespace) input
62
+ @"(and)?,?" , // allow "and" & "," separators between quantities
63
+ @"(?<invalid>[a-z]*)?" ) ; // capture invalid input
64
+
65
+ List < TUnit > quantities = ParseWithRegex ( regexString , str , parseUnit , formatProvider ) ;
66
+ if ( quantities . Count == 0 )
67
+ {
68
+ throw new ArgumentException (
69
+ "Expected string to have at least one pair of quantity and unit in the format"
70
+ + " \" <quantity> <unit>\" . Eg. \" 5.5 m\" or \" 1ft 2in\" " ) ;
71
+ }
72
+ return quantities . Aggregate ( add ) ;
73
+ }
74
+
75
+ /// <summary>
76
+ /// Parse a string given a particular regular expression.
77
+ /// </summary>
78
+ /// <exception cref="UnitsNetException">Error parsing string.</exception>
79
+ private static List < TUnit > ParseWithRegex < TUnit > ( string regexString , string str , ParseUnit < TUnit > parseUnit ,
80
+ IFormatProvider formatProvider = null )
81
+ {
82
+ var regex = new Regex ( regexString ) ;
83
+ MatchCollection matches = regex . Matches ( str . Trim ( ) ) ;
84
+ var converted = new List < TUnit > ( ) ;
85
+
86
+ foreach ( Match match in matches )
87
+ {
88
+ GroupCollection groups = match . Groups ;
89
+
90
+ var valueString = groups [ "value" ] . Value ;
91
+ var unitString = groups [ "unit" ] . Value ;
92
+ if ( groups [ "invalid" ] . Value != "" )
93
+ {
94
+ var newEx = new UnitsNetException ( "Invalid string detected: " + groups [ "invalid" ] . Value ) ;
95
+ newEx . Data [ "input" ] = str ;
96
+ newEx . Data [ "matched value" ] = valueString ;
97
+ newEx . Data [ "matched unit" ] = unitString ;
98
+ newEx . Data [ "formatprovider" ] = formatProvider ? . ToString ( ) ;
99
+ throw newEx ;
100
+ }
101
+ if ( valueString == "" && unitString == "" ) continue ;
102
+
103
+ try
104
+ {
105
+ converted . Add ( parseUnit ( valueString , unitString , formatProvider ) ) ;
106
+ }
107
+ catch ( AmbiguousUnitParseException )
108
+ {
109
+ throw ;
110
+ }
111
+ catch ( Exception ex )
112
+ {
113
+ var newEx = new UnitsNetException ( "Error parsing string." , ex ) ;
114
+ newEx . Data [ "input" ] = str ;
115
+ newEx . Data [ "matched value" ] = valueString ;
116
+ newEx . Data [ "matched unit" ] = unitString ;
117
+ newEx . Data [ "formatprovider" ] = formatProvider ? . ToString ( ) ;
118
+ throw newEx ;
119
+ }
120
+ }
121
+ return converted ;
122
+ }
123
+ }
124
+ }
0 commit comments