initial commit
This commit is contained in:
commit
252dac3143
1516 changed files with 694271 additions and 0 deletions
224
App_Code/Shared/FormulaEvaluator.vb
Normal file
224
App_Code/Shared/FormulaEvaluator.vb
Normal file
|
@ -0,0 +1,224 @@
|
|||
Imports Microsoft.VisualBasic
|
||||
Imports System.IO
|
||||
Imports BaseClasses.Data
|
||||
Imports Ciloci.Flee
|
||||
Imports Persons.UI
|
||||
Imports Persons.Data
|
||||
|
||||
Namespace Persons
|
||||
|
||||
''' <summary>
|
||||
''' The FormulaEvaluator class evaluates a formula passed to the Evaluate function.
|
||||
''' You must set the DataSource and Page variables as part of this.
|
||||
''' </summary>
|
||||
''' <remarks></remarks>
|
||||
Public Class FormulaEvaluator
|
||||
Inherits BaseFormulaEvaluator
|
||||
|
||||
|
||||
''' <summary>
|
||||
''' Record control (or row) from which this evaluator is called. Could be Nothing
|
||||
''' if called from the data access layer.
|
||||
''' </summary>
|
||||
Private _callingControl As Control = Nothing
|
||||
Public Property CallingControl() As Control
|
||||
Get
|
||||
Return _callingControl
|
||||
End Get
|
||||
Set(ByVal value As Control)
|
||||
_callingControl = value
|
||||
End Set
|
||||
End Property
|
||||
|
||||
|
||||
''' <summary>
|
||||
''' Create a new evaluator and prepare for evaluation.
|
||||
''' </summary>
|
||||
Public Sub New(Optional ByVal mainObj As Object = Nothing)
|
||||
|
||||
' pass mainObj to contructor so that formula like = mainObj.<functionName> can be evaluated
|
||||
If mainObj Is Nothing Then
|
||||
_evaluator = New ExpressionContext()
|
||||
Else
|
||||
_evaluator = New ExpressionContext(mainObj)
|
||||
End If
|
||||
|
||||
' The order of adding types is important. First we add our own
|
||||
' formula functions, followed by the generic types.
|
||||
|
||||
|
||||
Evaluator.Imports.AddType(GetType(BaseFormulaUtils))
|
||||
Evaluator.Imports.AddType(GetType(FormulaUtils))
|
||||
|
||||
' ADVANCED. For advanced usage, generic types can also be imported into
|
||||
' the formula evaluator. This is done by adding types of some generic types
|
||||
' such as Math, DateTime, Convert, and String. For example, if you add the
|
||||
' Convert type, you can then use Convert.ToDecimal("string"). The second
|
||||
' parameter to the AddType is the namespace that will be used in the formula.
|
||||
' These functions expect a certain type. For example, Math functions expect
|
||||
' a Double for the most part. If you pass a string, they will throw an exception.
|
||||
' As such, we have written separate functions in FormulaUtils that are more
|
||||
' loosely-typed than the standard libraries available here.
|
||||
' Examples:
|
||||
Evaluator.Imports.AddType(GetType(Math), "Math")
|
||||
Evaluator.Imports.AddType(GetType(DateTime), "DateTime")
|
||||
Evaluator.Imports.AddType(GetType(Convert), "Convert")
|
||||
Evaluator.Imports.AddType(GetType(String), "String")
|
||||
|
||||
' We want a loosely-typed evaluation language - so do not
|
||||
' type-check any variables.
|
||||
Evaluator.Options.Checked = False
|
||||
|
||||
' Our policy is to always treat real numbers as Decimal instead of
|
||||
' Double or Single to make it consistent across the entire
|
||||
' evaluator.
|
||||
Evaluator.Options.RealLiteralDataType = RealLiteralDataType.Decimal
|
||||
|
||||
' The variable event handler handles the variables based on the DataSource.
|
||||
AddHandler _evaluator.Variables.ResolveVariableType, AddressOf variables_ResolveVariableType
|
||||
AddHandler _evaluator.Variables.ResolveVariableValue, AddressOf variables_ResolveVariableValue
|
||||
End Sub
|
||||
|
||||
|
||||
''' <summary>
|
||||
''' Evaluate the expression passed in as the string
|
||||
''' </summary>
|
||||
''' <param name="expression">The input whose absolute value is to be found.</param>
|
||||
''' <returns>The result of the evaluation. Can we be any data type including string, datetime, decimal, etc.</returns>
|
||||
Public Overrides Function Evaluate(ByVal expression As String) As Object
|
||||
If IsNothing(expression) Then Return Nothing
|
||||
If expression = "" Then Return ""
|
||||
|
||||
' Strip of the = in the front of the forumula - the Expression evaluator
|
||||
' does not need it. Also, make sure to trim the result to remove any
|
||||
' spaces in the front and the back.
|
||||
expression = expression.TrimStart(New Char() {"="c, " "c}).Trim()
|
||||
|
||||
' Add all realted controls of this control. This includes the calling control, its children and parents.
|
||||
AddRelatedControlVariables()
|
||||
|
||||
' If there are any exceptions when parsing or evaluating, they are
|
||||
' thrown so that the end user can see the error. As such, there is no
|
||||
' Try-Catch block here.
|
||||
Try
|
||||
Dim eDynamic As IDynamicExpression = _evaluator.CompileDynamic(expression)
|
||||
Return eDynamic.Evaluate()
|
||||
Catch ex As Exception
|
||||
Return "ERROR: " & ex.Message
|
||||
End Try
|
||||
End Function
|
||||
|
||||
|
||||
|
||||
|
||||
#Region "Private functions"
|
||||
''' <summary>
|
||||
''' Adds related record and table controls.
|
||||
''' This has to navigate up to the page level and add any table or record controls.
|
||||
''' And we have to navigate within to add any record/table controls.
|
||||
''' And we have to go sideways to also add any controls.
|
||||
''' Finally we have to add the page control.
|
||||
''' This allows the expression to use any record or table control on the page
|
||||
''' as long as it is accessible without being in a repeater.
|
||||
''' </summary>
|
||||
Private Sub AddRelatedControlVariables()
|
||||
If IsNothing(CallingControl) Then Return
|
||||
|
||||
Try
|
||||
' STEP 1: Our strategy is to first add the current control and
|
||||
' all of its parents. This way, we maintain the full context of where
|
||||
' we are. For example, if you are in a row within a table, within another row
|
||||
' that is within another table, then by going up the hierarchy looking for parents
|
||||
' will preserve all of the context.
|
||||
' Later in Step 2 we will go through the other branches.
|
||||
Dim ctl As Control = CallingControl
|
||||
While Not (IsNothing(ctl))
|
||||
If TypeOf ctl Is BaseApplicationRecordControl OrElse TypeOf ctl Is BaseApplicationTableControl Then
|
||||
AddControlAndChildren(ctl)
|
||||
End If
|
||||
' Navigate up.
|
||||
ctl = ctl.Parent
|
||||
End While
|
||||
|
||||
' STEP 2: Go through the other branches on the page and add all other table and
|
||||
' record controls on the page.
|
||||
AddControlAndChildren(CallingControl.Page)
|
||||
|
||||
' STEP 3: Add more variable for ASCX control.
|
||||
AddVariableNameWithoutUnderscore()
|
||||
|
||||
' STEP 4: Finally add the Page itself.
|
||||
Evaluator.Variables.Add("Page", CallingControl.Page)
|
||||
|
||||
Catch ex As Exception
|
||||
' Ignore and continue in case of a problem.
|
||||
End Try
|
||||
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Add this control and all child controls of the given control.
|
||||
''' We only add the Record and Table Controls. No other controls need
|
||||
''' to be added.
|
||||
''' This function is smart enough not to add or descend down a control
|
||||
''' that was previously added by checking whether the Id is already contained
|
||||
''' in the Evaluator variables. This avoids unnecessary traversal.
|
||||
''' This function is called recursively to add any child controls.
|
||||
''' </summary>
|
||||
Private Sub AddControlAndChildren(ByVal ctl As Control)
|
||||
' We quit immediately if a control is already in the list of variables,
|
||||
' because we have convered that branch already.
|
||||
Try
|
||||
If IsNothing(ctl) Then Return
|
||||
|
||||
If Not (IsNothing(ctl.ID)) AndAlso Evaluator.Variables.ContainsKey(ctl.ID) Then Return
|
||||
|
||||
' If this is a record or table control, add it.
|
||||
If TypeOf ctl Is BaseApplicationRecordControl OrElse TypeOf ctl Is BaseApplicationTableControl Then
|
||||
If Not (IsNothing(ctl.ID)) Then
|
||||
Evaluator.Variables.Add(ctl.ID, ctl)
|
||||
End If
|
||||
End If
|
||||
|
||||
For Each child As Control In ctl.Controls
|
||||
' We do not want to go into a repeater because there will be multiple rows.
|
||||
' So we will call AddChildControls only for those controls that are NOT repeaters.
|
||||
If Not (TypeOf child Is System.Web.UI.WebControls.Repeater) Then
|
||||
AddControlAndChildren(child)
|
||||
End If
|
||||
Next
|
||||
Catch ex As Exception
|
||||
' Ignore - we do not want to give an exception if we cannot add all variables.
|
||||
End Try
|
||||
|
||||
End Sub
|
||||
|
||||
|
||||
''' <summary>
|
||||
''' If the current is not ASPX page but ASCX controls, the controls in this ASCX control has id starting with underscore.
|
||||
''' To avoid confusion in formula, we also define variable name without underscore.
|
||||
''' </summary>
|
||||
''' <remarks></remarks>
|
||||
Private Sub AddVariableNameWithoutUnderscore()
|
||||
Dim vars As New Collections.Generic.Dictionary(Of String, Object)
|
||||
Dim enumerator As Collections.Generic.IEnumerator(Of Collections.Generic.KeyValuePair(Of String, Object)) = Me.Evaluator.Variables.GetEnumerator()
|
||||
While enumerator.MoveNext
|
||||
If enumerator.Current.Key.StartsWith("_") Then
|
||||
Dim varNameWitoutUnderscore As String = enumerator.Current.Key.Substring(1)
|
||||
If Not Me.Evaluator.Variables.ContainsKey(varNameWitoutUnderscore) Then
|
||||
vars.Add(varNameWitoutUnderscore, enumerator.Current.Value)
|
||||
End If
|
||||
End If
|
||||
End While
|
||||
|
||||
Dim enumerator2 As Collections.Generic.Dictionary(Of String, Object).Enumerator = vars.GetEnumerator()
|
||||
While enumerator2.MoveNext
|
||||
Me.Evaluator.Variables.Add(enumerator2.Current.Key, enumerator2.Current.Value)
|
||||
End While
|
||||
End Sub
|
||||
#End Region
|
||||
|
||||
|
||||
End Class
|
||||
|
||||
End Namespace
|
Loading…
Add table
Add a link
Reference in a new issue