| using System; | 
| using System.Collections.Generic; | 
| using System.Linq; | 
| using System.Reflection; | 
| using System.Reflection.Emit; | 
| using System.Text; | 
| using Jace.Operations; | 
| using Jace.Util; | 
|   | 
| namespace Jace.Execution | 
| { | 
|     public class FormulaBuilder | 
|     { | 
|         private readonly CalculationEngine engine; | 
|   | 
|         private string formulaText; | 
|         private bool caseSensitive; | 
|         private DataType? resultDataType; | 
|         private List<ParameterInfo> parameters; | 
|         private IDictionary<string, double> constants; | 
|   | 
|         /// <summary> | 
|         /// Creates a new instance of the FormulaBuilder class. | 
|         /// </summary> | 
|         /// <param name="formulaText"> | 
|         /// A calculation engine instance that can be used for interpreting and executing  | 
|         /// the formula. | 
|         /// </param> | 
|         internal FormulaBuilder(string formulaText, bool caseSensitive, CalculationEngine engine) | 
|         { | 
|             this.parameters = new List<ParameterInfo>(); | 
|             this.constants = new Dictionary<string, double>(); | 
|             this.formulaText = formulaText; | 
|             this.engine = engine; | 
|             this.caseSensitive = caseSensitive; | 
|         } | 
|   | 
|         /// <summary> | 
|         /// Add a new parameter to the formula being constructed. Parameters are | 
|         /// added in the order of which they are defined. | 
|         /// </summary> | 
|         /// <param name="name">The name of the parameter.</param> | 
|         /// <param name="dataType">The date type of the parameter.</param> | 
|         /// <returns>The <see cref="FormulaBuilder"/> instance.</returns> | 
|         public FormulaBuilder Parameter(string name, DataType dataType) | 
|         { | 
|             if (string.IsNullOrEmpty(name)) | 
|                 throw new ArgumentNullException("name"); | 
|   | 
|             if (engine.FunctionRegistry.IsFunctionName(name)) | 
|                 throw new ArgumentException(string.Format("The name \"{0}\" is a function name. Parameters cannot have this name.", name), "name"); | 
|   | 
|             if (parameters.Any(p => p.Name == name)) | 
|                 throw new ArgumentException(string.Format("A parameter with the name \"{0}\" was already defined.", name), "name"); | 
|   | 
|             parameters.Add(new ParameterInfo() {Name = name, DataType = dataType}); | 
|             return this; | 
|         } | 
|   | 
|         /// <summary> | 
|         /// Add a new constant to the formula being constructed. | 
|         /// </summary> | 
|         /// <param name="name">The name of the constant.</param> | 
|         /// <param name="constantValue">The value of the constant. Variables for which a constant value is defined will be replaced at pre-compilation time.</param> | 
|         /// <returns>The <see cref="FormulaBuilder"/> instance.</returns> | 
|         public FormulaBuilder Constant(string name, int constantValue) | 
|         { | 
|             return Constant(name, (double)constantValue); | 
|         } | 
|   | 
|         /// <summary> | 
|         /// Add a new constant to the formula being constructed. The | 
|         /// </summary> | 
|         /// <param name="name">The name of the constant.</param> | 
|         /// <param name="constantValue">The value of the constant.</param> | 
|         /// <returns>The <see cref="FormulaBuilder"/> instance.</returns> | 
|         public FormulaBuilder Constant(string name, double constantValue) | 
|         { | 
|             if (string.IsNullOrEmpty(name)) | 
|                 throw new ArgumentNullException("name"); | 
|   | 
|             if (constants.Any(p => p.Key == name)) | 
|                 throw new ArgumentException(string.Format("A constant with the name \"{0}\" was already defined.", name), "name"); | 
|   | 
|             constants[name] = constantValue; | 
|             return this; | 
|         } | 
|   | 
|         /// <summary> | 
|         /// Define the result data type for the formula. | 
|         /// </summary> | 
|         /// <param name="dataType">The result data type for the formula.</param> | 
|         /// <returns>The <see cref="FormulaBuilder"/> instance.</returns> | 
|         public FormulaBuilder Result(DataType dataType) | 
|         { | 
|             if (resultDataType.HasValue) | 
|                 throw new InvalidOperationException("The result can only be defined once for a given formula."); | 
|   | 
|             resultDataType = dataType; | 
|             return this; | 
|         } | 
|   | 
|         /// <summary> | 
|         /// Build the formula defined. This will create a func delegate matching with the parameters | 
|         /// and the return type specified. | 
|         /// </summary> | 
|         /// <returns>The func delegate for the defined formula.</returns> | 
|         public Delegate Build() | 
|         { | 
|             if (!resultDataType.HasValue) | 
|                 throw new Exception("Please define a result data type for the formula."); | 
|   | 
|             Func<IDictionary<string, double>, double> formula = engine.Build(formulaText, constants); | 
|   | 
|             FuncAdapter adapter = new FuncAdapter(); | 
|             return adapter.Wrap(parameters, variables => { | 
|   | 
|                 if(!caseSensitive) | 
|                     variables = EngineUtil.ConvertVariableNamesToLowerCase(variables); | 
|   | 
|                 engine.VerifyVariableNames(variables); | 
|   | 
|                 // Add the reserved variables to the dictionary | 
|                 foreach (ConstantInfo constant in engine.ConstantRegistry) | 
|                     variables.Add(constant.ConstantName, constant.Value); | 
|   | 
|                 return formula(variables); | 
|             }); | 
|         } | 
|     } | 
| } |