1
1
// Licensed under MIT No Attribution, see LICENSE file at the root.
2
2
// Copyright 2013 Andreas Gullberg Larsen ([email protected] ). Maintained at https://github.com/angularsen/UnitsNet.
3
3
4
- using System ;
5
- using System . Collections . Generic ;
6
4
using System . IO ;
7
5
using System . Linq ;
8
6
using System . Text ;
9
7
using CodeGen . Generators . UnitsNetGen ;
10
- using CodeGen . Helpers ;
11
8
using CodeGen . JsonTypes ;
12
- using Newtonsoft . Json ;
13
9
using Serilog ;
14
10
15
11
namespace CodeGen . Generators
16
12
{
13
+ /// <summary>
14
+ /// Code generator for UnitsNet and UnitsNet.Tests projects.
15
+ /// </summary>
17
16
internal static class UnitsNetGenerator
18
17
{
19
18
private const int AlignPad = 35 ;
20
19
21
- private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
22
- {
23
- // Don't override the C# default assigned values if no value is set in JSON
24
- NullValueHandling = NullValueHandling . Ignore
25
- } ;
26
-
27
- public static void Generate ( DirectoryInfo repositoryRoot )
20
+ /// <summary>
21
+ /// Generate source code for UnitsNet project for the given parsed quantities.
22
+ /// Outputs files relative to the given root dir to these locations:
23
+ /// <list type="bullet">
24
+ /// <item>
25
+ /// <description>UnitsNet/GeneratedCode (quantity and unit types, Quantity, UnitAbbreviationCache)</description>
26
+ /// </item>
27
+ /// <item>
28
+ /// <description>UnitsNet.Tests/GeneratedCode (tests)</description>
29
+ /// </item>
30
+ /// <item>
31
+ /// <description>UnitsNet.Tests/CustomCode (test stubs, one for each quantity if not already created)</description>
32
+ /// </item>
33
+ /// </list>
34
+ /// </summary>
35
+ /// <param name="rootDir">Path to repository root directory.</param>
36
+ /// <param name="quantities">The parsed quantities.</param>
37
+ public static void Generate ( string rootDir , Quantity [ ] quantities )
28
38
{
29
- if ( repositoryRoot == null ) throw new ArgumentNullException ( nameof ( repositoryRoot ) ) ;
30
- var root = repositoryRoot . FullName ;
31
-
32
- var templatesDir = Path . Combine ( root , "Common/UnitDefinitions" ) ;
33
- var jsonFiles = Directory . GetFiles ( templatesDir , "*.json" ) ;
34
- var quantities = jsonFiles . Select ( ParseQuantityFile ) . ToArray ( ) ;
39
+ var outputDir = $ "{ rootDir } /UnitsNet/GeneratedCode";
40
+ var testProjectDir = $ "{ rootDir } /UnitsNet.Tests";
35
41
36
- foreach ( Quantity quantity in quantities )
42
+ foreach ( var quantity in quantities )
37
43
{
38
44
var sb = new StringBuilder ( $ "{ quantity . Name } :". PadRight ( AlignPad ) ) ;
39
- GenerateQuantity ( sb , quantity , $ "{ root } /UnitsNet/GeneratedCode /Quantities/{ quantity . Name } .NetFramework.g.cs") ; // TODO Remove NetFramework suffix
40
- GenerateUnitType ( sb , quantity , $ "{ root } /UnitsNet/GeneratedCode /Units/{ quantity . Name } Unit.g.cs") ;
41
- GenerateUnitTestBaseClass ( sb , quantity , $ "{ root } /UnitsNet.Tests /GeneratedCode/{ quantity . Name } TestsBase.g.cs") ;
42
- GenerateUnitTestClassIfNotExists ( sb , quantity , $ "{ root } /UnitsNet.Tests /CustomCode/{ quantity . Name } Tests.cs") ;
45
+ GenerateQuantity ( sb , quantity , $ "{ outputDir } /Quantities/{ quantity . Name } .NetFramework.g.cs") ; // TODO Remove NetFramework suffix
46
+ GenerateUnitType ( sb , quantity , $ "{ outputDir } /Units/{ quantity . Name } Unit.g.cs") ;
47
+ GenerateUnitTestBaseClass ( sb , quantity , $ "{ testProjectDir } /GeneratedCode/{ quantity . Name } TestsBase.g.cs") ;
48
+ GenerateUnitTestClassIfNotExists ( sb , quantity , $ "{ testProjectDir } /CustomCode/{ quantity . Name } Tests.cs") ;
43
49
Log . Information ( sb . ToString ( ) ) ;
44
50
}
45
51
46
52
Log . Information ( "" ) ;
47
- GenerateUnitAbbreviationsCache ( quantities , $ "{ root } /UnitsNet/GeneratedCode /UnitAbbreviationsCache.g.cs") ;
48
- GenerateQuantityType ( quantities , $ "{ root } /UnitsNet/GeneratedCode /QuantityType.g.cs") ;
49
- GenerateStaticQuantity ( quantities , $ "{ root } /UnitsNet/GeneratedCode /Quantity.g.cs") ;
53
+ GenerateUnitAbbreviationsCache ( quantities , $ "{ outputDir } /UnitAbbreviationsCache.g.cs") ;
54
+ GenerateQuantityType ( quantities , $ "{ outputDir } /QuantityType.g.cs") ;
55
+ GenerateStaticQuantity ( quantities , $ "{ outputDir } /Quantity.g.cs") ;
50
56
51
57
var unitCount = quantities . SelectMany ( q => q . Units ) . Count ( ) ;
52
58
Log . Information ( "" ) ;
53
59
Log . Information ( $ "Total of { unitCount } units and { quantities . Length } quantities.") ;
54
60
Log . Information ( "" ) ;
55
61
}
56
62
57
- private static Quantity ParseQuantityFile ( string jsonFile )
58
- {
59
- try
60
- {
61
- var quantity = JsonConvert . DeserializeObject < Quantity > ( File . ReadAllText ( jsonFile , Encoding . UTF8 ) , JsonSerializerSettings ) ;
62
- AddPrefixUnits ( quantity ) ;
63
- FixConversionFunctionsForDecimalValueTypes ( quantity ) ;
64
- OrderUnitsByName ( quantity ) ;
65
- return quantity ;
66
- }
67
- catch ( Exception e )
68
- {
69
- throw new Exception ( $ "Error parsing quantity JSON file: { jsonFile } ", e ) ;
70
- }
71
- }
72
-
73
63
private static void GenerateUnitTestClassIfNotExists ( StringBuilder sb , Quantity quantity , string filePath )
74
64
{
75
65
if ( File . Exists ( filePath ) )
@@ -78,133 +68,51 @@ private static void GenerateUnitTestClassIfNotExists(StringBuilder sb, Quantity
78
68
return ;
79
69
}
80
70
81
- string content = new UnitTestStubGenerator ( quantity ) . Generate ( ) ;
71
+ var content = new UnitTestStubGenerator ( quantity ) . Generate ( ) ;
82
72
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
83
73
sb . Append ( "test stub(OK) " ) ;
84
74
}
85
75
86
76
private static void GenerateQuantity ( StringBuilder sb , Quantity quantity , string filePath )
87
77
{
88
- string content = new QuantityGenerator ( quantity ) . Generate ( ) ;
78
+ var content = new QuantityGenerator ( quantity ) . Generate ( ) ;
89
79
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
90
80
sb . Append ( "quantity(OK) " ) ;
91
81
}
92
82
93
83
private static void GenerateUnitType ( StringBuilder sb , Quantity quantity , string filePath )
94
84
{
95
- string content = new UnitTypeGenerator ( quantity ) . Generate ( ) ;
85
+ var content = new UnitTypeGenerator ( quantity ) . Generate ( ) ;
96
86
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
97
87
sb . Append ( "unit(OK) " ) ;
98
88
}
99
89
100
90
private static void GenerateUnitTestBaseClass ( StringBuilder sb , Quantity quantity , string filePath )
101
91
{
102
- string content = new UnitTestBaseClassGenerator ( quantity ) . Generate ( ) ;
92
+ var content = new UnitTestBaseClassGenerator ( quantity ) . Generate ( ) ;
103
93
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
104
94
sb . Append ( "test base(OK) " ) ;
105
95
}
106
96
107
97
private static void GenerateUnitAbbreviationsCache ( Quantity [ ] quantities , string filePath )
108
98
{
109
- string content = new UnitAbbreviationsCacheGenerator ( quantities ) . Generate ( ) ;
99
+ var content = new UnitAbbreviationsCacheGenerator ( quantities ) . Generate ( ) ;
110
100
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
111
101
Log . Information ( "UnitAbbreviationsCache.g.cs: " . PadRight ( AlignPad ) + "(OK)" ) ;
112
102
}
113
103
114
104
private static void GenerateQuantityType ( Quantity [ ] quantities , string filePath )
115
105
{
116
- string content = new QuantityTypeGenerator ( quantities ) . Generate ( ) ;
106
+ var content = new QuantityTypeGenerator ( quantities ) . Generate ( ) ;
117
107
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
118
108
Log . Information ( "QuantityType.g.cs: " . PadRight ( AlignPad ) + "(OK)" ) ;
119
109
}
120
110
121
111
private static void GenerateStaticQuantity ( Quantity [ ] quantities , string filePath )
122
112
{
123
- string content = new StaticQuantityGenerator ( quantities ) . Generate ( ) ;
113
+ var content = new StaticQuantityGenerator ( quantities ) . Generate ( ) ;
124
114
File . WriteAllText ( filePath , content , Encoding . UTF8 ) ;
125
115
Log . Information ( "Quantity.g.cs: " . PadRight ( AlignPad ) + "(OK)" ) ;
126
116
}
127
-
128
- private static void OrderUnitsByName ( Quantity quantity )
129
- {
130
- quantity . Units = quantity . Units . OrderBy ( u => u . SingularName ) . ToArray ( ) ;
131
- }
132
-
133
- private static void FixConversionFunctionsForDecimalValueTypes ( Quantity quantity )
134
- {
135
- foreach ( Unit u in quantity . Units )
136
- {
137
- // Use decimal for internal calculations if base type is not double, such as for long or int.
138
- if ( string . Equals ( quantity . BaseType , "decimal" , StringComparison . OrdinalIgnoreCase ) )
139
- {
140
- // Change any double literals like "1024d" to decimal literals "1024m"
141
- u . FromUnitToBaseFunc = u . FromUnitToBaseFunc . Replace ( "d" , "m" ) ;
142
- u . FromBaseToUnitFunc = u . FromBaseToUnitFunc . Replace ( "d" , "m" ) ;
143
- }
144
- }
145
- }
146
-
147
- private static void AddPrefixUnits ( Quantity quantity )
148
- {
149
- var unitsToAdd = new List < Unit > ( ) ;
150
- foreach ( Unit unit in quantity . Units )
151
- {
152
- // "Kilo", "Nano" etc.
153
- foreach ( Prefix prefix in unit . Prefixes )
154
- {
155
- try
156
- {
157
- PrefixInfo prefixInfo = PrefixInfo . Entries [ prefix ] ;
158
-
159
- unitsToAdd . Add ( new Unit
160
- {
161
- SingularName = $ "{ prefix } { unit . SingularName . ToCamelCase ( ) } ", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter"
162
- PluralName = $ "{ prefix } { unit . PluralName . ToCamelCase ( ) } ", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter"
163
- BaseUnits = null , // Can we determine this somehow?
164
- FromBaseToUnitFunc = $ "({ unit . FromBaseToUnitFunc } ) / { prefixInfo . Factor } ",
165
- FromUnitToBaseFunc = $ "({ unit . FromUnitToBaseFunc } ) * { prefixInfo . Factor } ",
166
- Localization = GetLocalizationForPrefixUnit ( unit . Localization , prefixInfo ) ,
167
- } ) ;
168
- }
169
- catch ( Exception e )
170
- {
171
- throw new Exception ( $ "Error parsing prefix { prefix } for unit { quantity . Name } .{ unit . SingularName } .", e ) ;
172
- }
173
- }
174
- }
175
-
176
- quantity . Units = quantity . Units . Concat ( unitsToAdd ) . ToArray ( ) ;
177
- }
178
-
179
- /// <summary>
180
- /// Create unit abbreviations for a prefix unit, given a unit and the prefix.
181
- /// The unit abbreviations are either prefixed with the SI prefix or an explicitly configured abbreviation via <see cref="AbbreviationsForPrefixes"/>.
182
- /// </summary>
183
- private static Localization [ ] GetLocalizationForPrefixUnit ( IEnumerable < Localization > localizations , PrefixInfo prefixInfo )
184
- {
185
- return localizations . Select ( loc =>
186
- {
187
- if ( loc . TryGetAbbreviationsForPrefix ( prefixInfo . Prefix , out string [ ] unitAbbreviationsForPrefix ) )
188
- {
189
- // Use explicitly defined prefix unit abbreviations
190
- return new Localization
191
- {
192
- Culture = loc . Culture ,
193
- Abbreviations = unitAbbreviationsForPrefix ,
194
- } ;
195
- }
196
-
197
- // No prefix unit abbreviations are specified, so fall back to prepending the default SI prefix to each unit abbreviation:
198
- // kilo ("k") + meter ("m") => kilometer ("km")
199
- string prefix = prefixInfo . Abbreviation ;
200
- unitAbbreviationsForPrefix = loc . Abbreviations . Select ( unitAbbreviation => $ "{ prefix } { unitAbbreviation } ") . ToArray ( ) ;
201
-
202
- return new Localization
203
- {
204
- Culture = loc . Culture ,
205
- Abbreviations = unitAbbreviationsForPrefix ,
206
- } ;
207
- } ) . ToArray ( ) ;
208
- }
209
117
}
210
118
}
0 commit comments