5.3 Implementation Details: Back-end
5.3.4 Conversion Class
This class has four methods that perform interesting functions. The CreateConversion method, which uses the Replacer method covers two of them, and the other two, which also work together, are the Perform and PerformConversion methods.
CreateConversion, Replacer
The CreateConversion method works in two modes. Firstly, the dimension of the two units is ascertained, and the graph is consulted to attempt to find a route between the two. If a route is found, this route is stored as a list of edges privately in the Conversion object— no other part of the program needs this information. If the two units are of the same dimension, and that dimension is a “basic” dimension1, then this is all that can be done.
If a route is not found, then in the basic case the two units cannot be converted between. In the case where the dimension is not basic (speed, area, and so on), the second mode, then it is not the end of the story if a route is not found. If a route cannot be found, the dimension is split into its constituent parts from its definition, and then each of the units are split into their component parts, and verified to match the corresponding part of the dimension. If they do, a new conversion object is created for each corresponding constituent part. An array of these Conversion objects is stored in the Conversion object, and two OpenMathApplication objects are declared, one specifying how the units were split apart, and another specifying how to put them back together, as depending on the units these may be different. A difficulty with this arises if one unit takes several of the constituent dimensions: if converting between volumes, where the dimension’s definition is in terms of length × length × length, which is easily mapped to a unit which is defined in terms of three length units (cubic metres, for example), but does not work correctly if one
1A basic dimension is one which is not defined in terms of any other dimension: speed is defined in terms
of length and time, which in turn are not defined in terms of any others—they are base quantities—so the latter two are both basic dimensions, but the former is not.
CHAPTER 5. DETAILED DESIGN AND IMPLEMENTATION 53 unit takes up two or more parts, for example if using litre as a volume, the litre takes up all three of the lengths. This is where the Replacer method comes in. It splits the unit that takes up several parts into individual units, so individual parts can be converted between. The CreateConversion method sets up the decombine and recombine parts. The decombine part is the OMA used to perform any additional calculation on the input before starting: when converting between acres and square metres, for example, as well as needing to be split into square yards, the acre also has a multiplication to do (1 acre = 4840 square yards). This goes into the decombine stage, and the inverse of it occurs before the conversion—in this example, the input is divided by 4840. The recombine stage specifies how to put the intermediate conversions back together to make the whole, typically by multiplying them all together, or dividing the distance part by the time part in the case of a speed conversion. A difficulty with the CreateConversion method as described here occurs when we convert a compound unit to a prefixed named unit, for example, a force unit divided by an area unit to kilopascals. Then, because kilopascals is defined in terms of Pascals only, the conversion cannot be found, because the dimensions of none of the constituent units (only the whole) of the force over area unit can be converted to a pressure. To avoid this, a special case was added where if the second unit appears to be a prefixed unit (it is impossible to tell programmatically, but a heuristic is used) and the first unit is not prefixed, then a conversion is attempted between the source unit and the non-prefixed version of the destination unit, with an additional step added to then reapply the prefix, by reversing its effect (if converting to kilo-, which is 103, the result needs to be 10−3 times the size of the unprefixed unit).
Perform, PerformConversion
The Perform method passes the input, initialised as part of the Conversion object’s con- structor, through the decombine stage if this is not set to null. It then sees if the conversions list has anything in it; if it does, it gives the first item in the list the result of passing the input through the decombine stage, and any other Conversions in the array get given the input value 1. Then Perform is called on each Conversion in turn. Once they are all completed, the recombine part is used to combine the values, by replacing each successive OpenMathSymbol in the recombination with the results of the conversions, and then calling Simplify on this to get a value out.
If the Conversions array is empty, then the method checks to see if the route list of Edges is null. If it is not, PerformConversion is called, on the decombined input, and the result is stored in the Output field of this Conversion object. PerformConversion goes through the list of Edges, determines which way to go through the conversion (that is, whether the current unit is the first unit in the edge or the second. It calls one of the Replace methods in OpenMathApplication to replace the OTHER unit with the current value in the conversion for this edge. It then calls Simplify on the conversion, and updates the currentValue and current unit. It continues to do this until the list of edges for this conversion has been traversed, and then stores the result in Output. Although it may seem counter-intuitive to replace the other unit by the value, meaning that the conversion OMA has both the
CHAPTER 5. DETAILED DESIGN AND IMPLEMENTATION 54 current unit name and the current value (which is in place of the other unit name), when the result is unwrapped, this yields the correct result, and was found to be necessary for temperature conversions in particular.
If both Conversions and route are null, after the CreateConversion method has been called, then the conversion cannot be performed.