using System; using System.Collections.Generic; using LoreSoft.MathExpressions.Properties; using LoreSoft.MathExpressions.UnitConversion; using System.Globalization; using LoreSoft.MathExpressions.Metadata; using System.Reflection; namespace LoreSoft.MathExpressions { /// /// A class representing unit convertion expressions. /// public class ConvertExpression : ExpressionBase { private static Dictionary convertionCache; private static object cacheLock = new object(); private ConvertionMap current; private string expression; /// The format of a convertion expression. public const string ExpressionFormat = "[{0}->{1}]"; /// Initializes a new instance of the class. /// The convertion expression for this instance. public ConvertExpression(string expression) { VerifyCache(); if (!convertionCache.ContainsKey(expression)) throw new ArgumentException(Resources.InvalidConvertionExpression + expression, "expression"); this.expression = expression; current = convertionCache[expression]; base.Evaluate = new MathEvaluate(Convert); } /// Gets the number of arguments this expression uses. /// The argument count. public override int ArgumentCount { get { return 1; } } /// Convert the numbers to the new unit. /// The numbers used in the convertion. /// The result of the convertion execution. /// When numbers is null. /// When the length of numbers do not equal . public double Convert(double[] numbers) { base.Validate(numbers); double fromValue = numbers[0]; switch (current.UnitType) { case UnitType.Length: return LengthConverter.Convert( (LengthUnit) current.FromUnit, (LengthUnit) current.ToUnit, fromValue); case UnitType.Mass: return MassConverter.Convert( (MassUnit) current.FromUnit, (MassUnit) current.ToUnit, fromValue); case UnitType.Speed: return SpeedConverter.Convert( (SpeedUnit) current.FromUnit, (SpeedUnit) current.ToUnit, fromValue); case UnitType.Temperature: return TemperatureConverter.Convert( (TemperatureUnit) current.FromUnit, (TemperatureUnit) current.ToUnit, fromValue); case UnitType.Time: return TimeConverter.Convert( (TimeUnit) current.FromUnit, (TimeUnit) current.ToUnit, fromValue); case UnitType.Volume: return VolumeConverter.Convert( (VolumeUnit) current.FromUnit, (VolumeUnit) current.ToUnit, fromValue); default: throw new ArgumentOutOfRangeException("numbers"); } } /// /// Determines whether the specified expression name is for unit convertion. /// ///The expression to check. ///true if the specified expression is a unit convertion; otherwise, false. public static bool IsConvertExpression(string expression) { //do basic checks before creating cache if (string.IsNullOrEmpty(expression)) return false; if (expression[0] != '[') return false; VerifyCache(); return convertionCache.ContainsKey(expression); } private static void VerifyCache() { if (convertionCache != null) return; lock (cacheLock) { if (convertionCache != null) return; convertionCache = new Dictionary( StringComparer.OrdinalIgnoreCase); AddToCache(UnitType.Length); AddToCache(UnitType.Mass); AddToCache(UnitType.Speed); AddToCache(UnitType.Temperature); AddToCache(UnitType.Time); AddToCache(UnitType.Volume); } } private static void AddToCache(UnitType unitType) where T : struct, IComparable, IFormattable, IConvertible { Type enumType = typeof(T); int[] a = (int[])Enum.GetValues(enumType); for (int x = 0; x < a.Length; x++) { MemberInfo parentInfo = GetMemberInfo(enumType, Enum.GetName(enumType, x)); string parrentKey = AttributeReader.GetAbbreviation(parentInfo); for (int i = 0; i < a.Length; i++) { if (x == i) continue; MemberInfo info = GetMemberInfo(enumType, Enum.GetName(enumType, i)); string key = string.Format( CultureInfo.InvariantCulture, ExpressionFormat, parrentKey, AttributeReader.GetAbbreviation(info)); convertionCache.Add( key, new ConvertionMap(unitType, x, i)); } } } private static MemberInfo GetMemberInfo(Type type, string name) { MemberInfo[] info = type.GetMember(name); if (info == null || info.Length == 0) return null; return info[0]; } /// /// Returns a that represents the current . /// /// /// A that represents the current . /// /// 2 public override string ToString() { return expression; } private class ConvertionMap { public ConvertionMap(UnitType unitType, int fromUnit, int toUnit) { _unitType = unitType; _fromUnit = fromUnit; _toUnit = toUnit; } private UnitType _unitType; public UnitType UnitType { get { return _unitType; } } private int _fromUnit; public int FromUnit { get { return _fromUnit; } } private int _toUnit; public int ToUnit { get { return _toUnit; } } public override string ToString() { return string.Format( CultureInfo.CurrentCulture, "{0}, [{1}->{2}]", _unitType, _fromUnit, _toUnit); } } } }