-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUnitParser.java
435 lines (348 loc) · 22.2 KB
/
UnitParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
package Parts;
import java.util.Scanner;
import InternalUnitParser.CSharpAdaptation.*;
import UnitParser.*;
import NumberParser.*;
import NumberParser.Number;
public class UnitParser
{
public static void main(String[] args)
{
StartTest();
}
public static void StartTest()
{
System.out.println("-------------- UnitParser --------------");
System.out.println();
//------ The base class is UnitP. There are multiple ways to instantiate a UnitP variable.
PrintSampleItem("Inst1", new UnitP("1 N")); //Unit symbol. Case does matter.
PrintSampleItem("Inst2", new UnitP(1.0, UnitSymbols.Newton));
PrintSampleItem("Inst3", new UnitP(1.0, "nEwTon")); //Unit secondary String representation. Case doesn't matter.
PrintSampleItem("Inst4", new UnitP(Units.Newton)); //Value assumed to be 1.
PrintSampleItem("Inst5", new UnitP()); //Value and unit assumed to be 1 and unitless, respectively.
//--- All the public classes support (un)equality comparisons accounting for their more relevant variables.
if
(
new UnitP("1 N").equals(new UnitP(1.0, UnitSymbols.Newton)) &&
new UnitP(1.0, UnitSymbols.Newton).equals(new UnitP(1.0, "nEwTon")) &&
new UnitP(1.0, "nEwTon").equals(new UnitP(Units.Newton))
)
{
//This condition is true.
}
//------ UnitP variables can be seen as abstract concepts including many specific types.
//--- Same type variables can be added/subtracted.
PrintSampleItem("Add1", UnitP.Addition(new UnitP("1 N"), new UnitP(1.0, UnitSymbols.Newton))); //Both variables have the same type (force).
//--- Different type variables can be multiplied/divided, but only when the resulting output belongs to a supported type.
PrintSampleItem("Mult1", UnitP.Multiplication(new UnitP("1 N"), new UnitP("m"))); //N*m = J, what is a supported type (energy).
//--- Any operation outputting unsupported types triggers an error.
PrintSampleItem("Mult2", UnitP.Multiplication(UnitP.Multiplication(new UnitP("1 N"), new UnitP("1 m")), new UnitP("1 m"))); //N*m2 doesn't match any valid type.
//------ Multiplication/division with decimal/double values is also supported.
PrintSampleItem("Mult3", UnitP.Multiplication(new UnitP("1 N"), 1.23456)); //Multiplication involving UnitP and double variables.
PrintSampleItem("Div1", UnitP.Division(new UnitP("1 N"), 7.891011)); //Division involving UnitP and decimal variables.
//--- Dividing a number by a UnitP variable does affect the given unit.
try
{
PrintSampleItem("Div2", UnitP.Division(7.891011, new UnitP("1 N")));
}
catch(Exception e)
{
//Error because 1/N doesn't represent a supported type.
//The reasons for the exception are explained below.
System.out.println("Div2 - Caught Exception.");
}
//------ Compounds, unit parts and individual units.
PrintSampleItem("Ind1", new UnitP("1 sec")); //s, a valid SI time unit. It is an individual unit (i.e., 1 single unit part whose exponent is 1).
PrintSampleItem("Comp1", new UnitP("1 m/s")); //m/s, a valid SI velocity unit. It is a compound (i.e., various parts or one with a different-than-1 exponent).
PrintSampleItem("Comp2", new UnitP("1 N")); //N, a valid SI force unit. It is a compound with an official name.
//--- Compounds can be formed through String parsing or arithmetic operations.
PrintSampleItem("Comp3", UnitP.Division(new UnitP("1 m"), new UnitP("s"))); //m/s, a valid SI velocity unit, created by dividing two UnitP variables.
PrintSampleItem("Comp4", new UnitP("kg*m/s2")); //N (= kg*m/s2), a valid SI force unit, created via String parsing.
if (new UnitP("1 m/sec").equals(UnitP.Division(new UnitP("1 m"), new UnitP("s"))) && new UnitP("1 N").equals(new UnitP("1 kg*m/s2")))
{
//This condition is true.
}
//--- It is recommendable to create compounds via strings rather than operations.
PrintSampleItem("Comp5", UnitP.Division(new UnitP("1 m"), new UnitP("1 s2"))); //Error because s2 doesn't represent a valid type (type check for each UnitP variable).
PrintSampleItem("Comp6", new UnitP("m/s2")); //m/s2, a valid SI acceleration unit (one type check after all the unit operations/simplifications were performed).
//--- The unit parts are automatically populated when instantiating a valid UnitP variable.
if (Linq.FirstOrDefault(new UnitP("1 N").getUnitParts(), x -> !new UnitP("1 kg*m/s2").getUnitParts().contains(x), null) == null)
{
//This condition is true.
}
//--- When various units have the same constituent parts, the String-based recognition might not match the input unit.
PrintSampleItem("Comp7", new UnitP("V/m")); //Understood as NewtonPerCoulomb. Although V/m is a valid SI electric field strength unit, N/C has the same parts and is the default unit under these conditions.
PrintSampleItem("Comp8", new UnitP(Units.VoltPerMetre)); //Understood as VoltPerMetre. There is no String parsing/unit-part analysis and, consequently, no possible confusion.
//------ Format of input String units.
//--- UnitP constructors without numeric inputs expect strings formed by a number (it might be missing) and a unit.
PrintSampleItem("Str1", new UnitP("10 m")); //10 metre (length).
PrintSampleItem("Str2", new UnitP("1m")); //1 metre (length). Since UnitParser.dll v.1.0.6301.23655, there is no need to include a blank space between value and unit.
PrintSampleItem("Str3", new UnitP("m")); //1 metre (length).
//--- Multi-part strings are expected to be formed by units, multiplication/division symbols and integer exponents.
PrintSampleItem("Str4", new UnitP("1 J/s")); //1 joule per second (power unit).
PrintSampleItem("Str5", new UnitP("1 Jxs")); //1 joule second (angular momentum).
PrintSampleItem("Str6", new UnitP("1 J⋅s2")); //1 kilogram square metre (moment of inertia).
PrintSampleItem("Str7", new UnitP("J÷s-2")); //1 kilogram square metre (moment of inertia).
//--- Only one division sign is expected. It separates the numerator and denominator parts.
PrintSampleItem("Str8", new UnitP("1 J*J/s*J2*J-1*s*s-1")); //1 watt (power).
PrintSampleItem("Str9", new UnitP("J*J/(s*J2*s)*J*s")); //Error. It is understood as J*J/(s*J2*s*J*s).
//--- Not-supported-but-commonly-used characters are plainly ignored.
PrintSampleItem("Str10", new UnitP(1.0, "ft.")); //1 foot (length).
PrintSampleItem("Str11", new UnitP(1.0, "ft^2")); //1 square foot (area).
PrintSampleItem("Str12", new UnitP(1.0, "ft*(ft*ft)")); //1 cubic foot (volume).
//--- Ideally, no blank spaces should be included. The parser can deal with them anyway.
PrintSampleItem("Str13", new UnitP(1.0, "AU/min")); //1 astronomical unit per minute (velocity).
PrintSampleItem("Str14", new UnitP(1.0, "A U/ min")); //1 astronomical unit per minute (velocity).
//------ Format of input String numbers.
//--- The used culture is always CultureInfo.InvariantCulture (e.g., "." as decimal separator).
PrintSampleItem("StrNum1", new UnitP("1.1 m")); //Always 1.1 m, independently upon the applicable culture.
PrintSampleItem("StrNum2", new UnitP("1,1 s")); //Always 11 s, independently upon the applicable culture.
//--- The differences between double/decimal types are managed internally.
PrintSampleItem("StrNum3", new UnitP("1.0000000000000001 ft")); //1.0000000000000001 ft.
PrintSampleItem("StrNum4", new UnitP("1000000000000000000000000000000000000000000000000000000000000 mi")); //1000000*10^54 mi.
//--- It is also possible to input beyond-double numbers via strings. The exponential format follows the .NET rules.
PrintSampleItem("StrNum5", new UnitP("9999.99999E1000")); //999999.99*10^998 unitless.
PrintSampleItem("StrNum6", new UnitP("1234E1.5")); //Error. Equivalently to what happens with .NET numeric parsing, only integer exponents are supported.
//------ Errors and exceptions.
//--- All the error information is stored under UnitPVariable.Error.
if (new UnitP("1 m/s").getError().getType() == UnitP.ErrorTypes.None)
{
//This condition is true.
}
if (new UnitP("wrong").getError().getType() != UnitP.ErrorTypes.None)
{
//This condition is true.
}
//--- By default, errors don't trigger exceptions.
PrintSampleItem("Err1", new UnitP("wrong")); //No exception is triggered.
//--- The default behaviour can be modified when instantiating the variable.
try
{
PrintSampleItem("Err2", new UnitP("wrong", UnitP.ExceptionHandlingTypes.AlwaysTriggerException));
}
catch(Exception e)
{
//An exception is triggered.
System.out.println("Err2 - Caught Exception.");
}
//--- In case of incompatibility, the configuration of the first operand is applied.
PrintSampleItem
(
"Err3", //No exception is triggered.
UnitP.Multiplication
(
new UnitP("wrong"), new UnitP("1 m", UnitP.ExceptionHandlingTypes.AlwaysTriggerException)
)
);
try
{
PrintSampleItem
(
"Err4", //An exception is triggered.
UnitP.Multiplication
(
new UnitP("1 m", UnitP.ExceptionHandlingTypes.AlwaysTriggerException), new UnitP("wrong")
)
);
}
catch(Exception e)
{
System.out.println("Err4 - Caught Exception.");
}
//--- When the first operand is a number, an exception is always triggered.
try
{
//An exception is triggered.
PrintSampleItem("Err5", UnitP.Multiplication(5.0, new UnitP("wrong")));
}
catch(Exception e)
{
System.out.println("Err5 - Caught Exception.");
}
//------ Unit prefixes.
//--- Two types of prefixes are supported: SI and binary.
PrintSampleItem("Pref1", new UnitP(1.0, "km")); //SI prefix kilo + metre.
PrintSampleItem("Pref2", new UnitP("1 Kibit")); //Binary prefix Kibi + bit.
//--- All the prefix-related information is stored under UnitPVariable.UnitPrefix.
if (new UnitP(1.0, "km").getUnitPrefix().equals(new UnitP(1.0, "ks").getUnitPrefix()))
{
//This condition is true.
}
//--- Prefixes are automatically updated/simplified in any operation.
PrintSampleItem("Pref3", new UnitP("1 kJ")); //1 kJ.
PrintSampleItem("Pref4", new UnitP("555 mJ")); //555 mJ.
PrintSampleItem("Pref5", new UnitP("0.00000001 MJ")); //0.01 J.
PrintSampleItem
(
"Pref6", UnitP.Addition
(
UnitP.Addition(new UnitP("1 kJ"), new UnitP("555 mJ")), new UnitP("0.00000001 MJ")
)
); //1.000565 kJ.
//--- Prefix symbols are case sensitive, but String representations are not.
PrintSampleItem("Pref7", new UnitP(1.0, "Km")); //Error.
PrintSampleItem("Pref8", new UnitP(1.0, "mEGam")); //SI prefix mega + metre.
//--- By default, prefixes can only be used with units which officially/commonly support them.
PrintSampleItem("Pref9", new UnitP("1 Mft")); //Error because the unit foot doesn't support SI prefixes.
PrintSampleItem("Pref10", new UnitP("Eim")); //Error because the unit metre doesn't support binary prefixes.
//--- The default behaviour can be modified when instantiating the variable.
PrintSampleItem("Pref11", new UnitP("Mft", PrefixUsageTypes.AllUnits)); //SI prefix mega + foot.
PrintSampleItem("Pref12", new UnitP("1 Eim", PrefixUsageTypes.AllUnits)); //Binary prefix exbi + metre.
//--- Same rules apply to officially-named compounds. Non-named compounds recognise prefixes, but don't use them.
PrintSampleItem("Pref13", new UnitP("1 GN")); //SI prefix giga + newton.
PrintSampleItem("Pref14", new UnitP(1.0, SIPrefixSymbols.Giga + Units.MetrePerSecond)); //1000000*10^3 m/s.
//--- In certain situations, PrefixUsageTypes.AllUnits doesn't allow to use prefixes.
PrintSampleItem("Pref15", new UnitP(1.0, SIPrefixSymbols.Giga + Units.MetrePerSecond, PrefixUsageTypes.AllUnits)); //Compounds with no official name don't support prefixes.
PrintSampleItem("Pref16", new UnitP("100000000000 unitless", PrefixUsageTypes.AllUnits)); //Unitless variables don't support prefixes.
//--- The unit parts can also have prefixes, which might be compensated with the main prefix.
PrintSampleItem("Pref17", new UnitP(1.0, SIPrefixSymbols.Kilo + UnitSymbols.Newton)); //kN. SI prefix kilo directly affecting the unit newton.
PrintSampleItem("Pref18", new UnitP("1 Mg*m/s2")); //kN. SI prefix kilo indirectly affecting newton, via one of its constituent parts (kilo-kg = Mg).
if (new UnitP(1.0, SIPrefixSymbols.Kilo + UnitSymbols.Newton).equals(new UnitP("1 Mg*m/s2")))
{
//This condition is true.
//Note that both UnitP variables being equal implies identical prefixes.
}
//------ Systems of units.
//--- The system is automatically determined at variable instantiation. Each unit can belong to just one system.
PrintSampleItem("Sys1", new UnitP(Units.MetrePerSquareSecond)); //SI acceleration unit (m/s2).
PrintSampleItem("Sys2", new UnitP("cm/s2")); //CGS acceleration unit (Gal).
PrintSampleItem("Sys3", new UnitP(1.0, UnitSymbols.Rod + "/h2")); //Imperial acceleration unit (rd/h2).
PrintSampleItem("Sys4", new UnitP(1.0, UnitSymbols.SurveyRod + "/s2")); //USCS acceleration unit (surrd/s2).
PrintSampleItem("Sys5", new UnitP(1.0, "AU/min2")); //Acceleration unit not belonging to any system (AU/min2).
//------ Automatic unit conversions.
//--- Automatic conversions (to the system of the first operand) happen in operations between a big proportion of different-system units.
PrintSampleItem("Conv1", UnitP.Multiplication(new UnitP(1.0, Units.Metre), new UnitP("1 ft"))); //After converting ft to metre, SI area unit m2.
PrintSampleItem("Conv2", UnitP.Addition(new UnitP(Units.PoundForce), new UnitP(5.0, "N"))); //After converting N to lbf, Imperial/USCS force unit lbf.
//--- Same rules apply to compounds whose unit parts belong to different systems.
PrintSampleItem("Conv3", new UnitP(1.0, "m*lb/s2")); //After converting lb to kg, SI force unit N.
PrintSampleItem("Conv4", new UnitP(1.0, "surin3/in")); //After converting in to surin, USCS area unit surin2.
//------ Numeric support.
//--- UnitP variables support two different numeric types: decimal and double.
PrintSampleItem("Num1", new UnitP(1.23456, "m")); //The UnitP constructor overloads only support decimal type.
PrintSampleItem("Num2", UnitP.Multiplication(new UnitP("ft"), 7.891011)); //Decimal variables can be used in multiplications/divisions.
PrintSampleItem("Num3", UnitP.Multiplication(new UnitP("s"), 1213141516.0)); //Double variables can be used in multiplications/divisions.
//--- All the numeric inputs are converted into decimal type. UnitPVariable.BaseTenExponent avoids eventual type-conversion overflow problems.
PrintSampleItem
(
//UnitP variable with a Numeric value notably above decimal.MaxValue.
"Num4", UnitP.Division
(
new UnitP(9999999999999999.0, "YAU2", PrefixUsageTypes.AllUnits),
new UnitP("0.000000000000001 yf", PrefixUsageTypes.AllUnits)
)
);
PrintSampleItem
(
//UnitP variable with a Numeric value notably below decimal.MinValue.
"Num5", UnitP.Division
(
UnitP.Multiplication
(
0.0000000000000000000000000000000000000000000000001,
new UnitP(0.000000000000000000001, "ym2")
),
new UnitP("999999999999999999999 Ym")
)
);
//------ No unit, unitless & unnamed units.
//--- No unit (Units.None).
PrintSampleItem("No1", new UnitP(1.0, Units.None)); //Units.None cannot be used as an input.
PrintSampleItem("No2", new UnitP("1 wrong")); //Units.None is the unit associated with all the errors.
//--- Unitless (Units.Unitless).
PrintSampleItem("No3", new UnitP(1.0, Units.Unitless)); //Units.Unitless can be used as an input.
PrintSampleItem("No4", new UnitP("5e1234")); //Units.Unitless is the unit for purely-numeric calculations.
PrintSampleItem("No5", UnitP.Division(new UnitP("5 km"), new UnitP(1.0, Units.Unitless))); //Units.Unitless can be used together with other valid units without triggering an error.
PrintSampleItem("No6", new UnitP("1 ft/m")); //Units.Unitless is associated with the output of operations where all the units cancel each other (with or without automatic conversions).
//--- Unnamed units (Units.Valid[system]Unit).
PrintSampleItem("No7", new UnitP("yd/s")); //All the parsed compounds not matching any named unit are automatically included in this category.
PrintSampleItem("No8", new UnitP(1.0, Units.ValidCGSUnit)); //Error. Unnamed units cannot be used as inputs.
//------ Public functions.
//--- All the functions have static/UnitP and non-static/UnitPVariable versions.
System.out.println
(
"Func1 - " + Units.Abampere.toString() + " -> " + CSharpOther.StringJoin
(
",", UnitP.GetStringsForUnit(Units.Abampere, true) //Static method returning all the String representations associated with the input unit.
)
);
System.out.println
(
"Func2 - " + Units.Abampere.toString() + " -> " + CSharpOther.StringJoin
(
",", new UnitP(1.0, Units.Abampere).GetStringsForCurrentUnit(true) //Non-static method returning all the String representations associated with the current unit.
)
);
//--- The most relevant function is the one performing unit conversions.
PrintSampleItem("Func3", UnitP.ConvertTo(new UnitP("1 m"), Units.Foot)); //Static version of the unit conversion method.
PrintSampleItem("Func4", new UnitP("1 m/s").ConvertCurrentUnitTo("ft/h")); //Non-static version of the unit conversion method.
PrintSampleItem("Func5", new UnitP("1 m").ConvertCurrentUnitTo(Units.Gram)); //Error. No conversion is possible between different-type units.
//------ Other FlexibleParser parts.
//All the FlexibleParser parts are independent among each other and only the corresponding DLL file needs to be referred.
//On the other, codes relying on various parts can take advantage of certain compatibility among their main classes.
//--- NumberParser.
PrintSampleItem("NP1", new UnitP(new Number(123.0, 5), Units.Abampere)); //12.3 MabA.
PrintSampleItem("NP2", new UnitP(new NumberD(0.000000000001, -5), "Gs")); //0.01 µs.
PrintSampleItem("NP3", new UnitP(new NumberP("Error"), "m/s")); //Error.
//-------------------------------------------------------------
System.out.println();
System.out.println();
System.out.println("Do you want to print all the named units? Y/N");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
if (input.toLowerCase().trim().equals("y"))
{
//--- Printing all the supported named units out.
PrintAllNamedUnits();
}
System.out.println();
System.out.println("------------------------------------------");
System.out.println();
scanner.next();
scanner.close();
}
static void PrintSampleItem(String sampleId, UnitP unitP)
{
System.out.println
(
sampleId + " - " +
(
unitP.getError().getType() != UnitP.ErrorTypes.None ?
"Error. " + unitP.getError().getMessage() :
unitP.getValueAndUnitString() + " (" +
unitP.getUnit().toString() + ", " +
unitP.getUnitType().toString() + ", " +
unitP.getUnitSystem().toString() + ")."
)
);
}
//This method prints the main String representations and some basic information for all the named units.
//In any case, note that UnitParser supports a wide range of variations which aren't referred here.
//Examples: plurals of String representation other than symbols or ignoring certain invalid characters
//(e.g., blank spaces or conventionally-used characters like "^").
//Additionally, bear in mind that these are just the members of the Units enum, a small fraction of
//all the units supported by UnitParser. Any unit belonging to a supported type (UnitTypes enum) which
//is formed by the combination of one or more named units (Units enum) is also supported. For example,
//the named unit Units.Foot can be part of many other unnamed units like ft/h (velocity), rood*ft (volume)
//or tn*ft/s2 (force).
static void PrintAllNamedUnits()
{
for (Units unit: Units.values())
{
if (unit == Units.None || unit == Units.Unitless) continue;
UnitTypes type = UnitP.GetUnitType(unit);
UnitSystems system = UnitP.GetUnitSystem(unit);
if (type == UnitTypes.None) continue;
System.out.println("Unit: " + unit.toString());
System.out.println("Type: " + type.toString());
System.out.println("System: " + system.toString());
String representations = "";
for (String representation: UnitP.GetStringsForUnit(unit, true))
{
if (representations != "") representations += ", ";
representations += representation;
}
System.out.println("Representations: " + representations);
System.out.println();
}
}
}