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);
}
}
}
}