package gw.rating.worksheet uses gw.entity.TypeKey uses gw.lang.reflect.IType uses gw.lang.reflect.TypeSystem uses gw.rating.RateRoutineExecutionLogger uses gw.rating.RateRoutineExecutionOperator uses gw.rating.flow.domain.CostDataRoundingStatement uses gw.rating.worksheet.domain.WorksheetArgument uses gw.rating.worksheet.domain.WorksheetCalculation uses gw.rating.worksheet.domain.WorksheetConditional uses gw.rating.worksheet.domain.WorksheetEntry uses gw.rating.worksheet.domain.WorksheetEntryContainer uses gw.rating.worksheet.domain.WorksheetEntryWithResult uses gw.rating.worksheet.domain.WorksheetFunction uses gw.rating.worksheet.domain.WorksheetFunctionReturn uses gw.rating.worksheet.domain.WorksheetInstanceSubroutine uses gw.rating.worksheet.domain.WorksheetIteration uses gw.rating.worksheet.domain.WorksheetLoop uses gw.rating.worksheet.domain.WorksheetNote uses gw.rating.worksheet.domain.WorksheetOperand uses gw.rating.worksheet.domain.WorksheetOperandContainer uses gw.rating.worksheet.domain.WorksheetRoutine uses gw.rating.worksheet.domain.WorksheetStaticSubroutine /** * Captures all runtime information to build rating worksheets */ @SuppressWarnings("unused") class WorksheetLogger implements RateRoutineExecutionLogger { var _rootContainer : WorksheetEntryContainer var _entryContainer : WorksheetEntryContainer var _operandContainer : WorksheetOperandContainer final var _first : WorksheetTermWrapper as readonly Term final var _add : WorksheetTermWrapper as readonly Addition final var _div : WorksheetTermWrapper as readonly Division final var _mult : WorksheetTermWrapper as readonly Multiplication final var _sub : WorksheetTermWrapper as readonly Subtraction final var _eq : WorksheetTermWrapper as readonly EqualTo final var _lt : WorksheetTermWrapper as readonly LessThan final var _gt : WorksheetTermWrapper as readonly GreaterThan final var _lte : WorksheetTermWrapper as readonly LessThanEqual final var _gte : WorksheetTermWrapper as readonly GreaterThanEqual final var _ne : WorksheetTermWrapper as readonly NotEqual final var _or : WorksheetTermWrapper as readonly LogicalOr final var _and : WorksheetTermWrapper as readonly LogicalAnd final var _in : WorksheetTermWrapper as readonly TypekeyIn final var _ni : WorksheetTermWrapper as readonly TypekeyNotIn static final var _threadLocal = ThreadLocal.withInitial(\ -> new WorksheetLogger()) protected construct() { _first = new WorksheetTermWrapper(this, Blank) _add = new WorksheetTermWrapper(this, Plus) _div = new WorksheetTermWrapper(this, Divide) _mult = new WorksheetTermWrapper(this, Multiply) _sub = new WorksheetTermWrapper(this, Minus) _eq = new WorksheetTermWrapper(this, EqualsEquals) _lt = new WorksheetTermWrapper(this, RateRoutineExecutionOperator.LessThan) _gt = new WorksheetTermWrapper(this, RateRoutineExecutionOperator.GreaterThan) _lte = new WorksheetTermWrapper(this, LessOrEquals) _gte = new WorksheetTermWrapper(this, GreaterOrEquals) _ne = new WorksheetTermWrapper(this, NotEquals) _or = new WorksheetTermWrapper(this, OrOperator) _and = new WorksheetTermWrapper(this, AndOperator) _in = new WorksheetTermWrapper(this, InOperator) _ni = new WorksheetTermWrapper(this, NotInOperator) } static function get() : WorksheetLogger { return _threadLocal.get() } static function get(worksheetContainer: WorksheetEntryContainer): WorksheetLogger { var logger = get() logger.WorksheetEntryContainer = worksheetContainer return logger } /** * Called before rating to acquire any necessary thread-local resources. */ static function beginRating() { } /** * Called after rating to release any thread-local resources. Should be * called within a finally block. */ static function endRating() { _threadLocal.remove() } /** * Log the beginning of a new rateflow routine * * @param book Book code * @param bookEdition Edition * @param routineCode Routine code * @param routineVersion Routine version */ override function begin(book : String, bookEdition : String, routineCode : String, routineVersion : String){ var routine = new WorksheetRoutine() { :RateBookCode = book, :RateBookEdition = bookEdition, :RoutineCode = routineCode, :RoutineVersion = routineVersion } addTheWorksheetEntry(routine) _entryContainer = routine } /** * Log the end of a rateflow routine */ override function end(){ if(_entryContainer == null or not(_entryContainer typeis WorksheetEntry)){ throw new IllegalStateException("Calling worksheet 'end' without corresponding 'begin'") } _entryContainer = (_entryContainer as WorksheetEntry).Parent } /** * Logs the assignment of a constant to a new variable * * @param variableName Variable name * @param value The value * @return the result of the expression */ override reified function let(variableName : String, value : T ) : T { return this.let(variableName, \-> this.Term.co(value)) } /** * Logs the assignment of an expression to a new variable * * @param variableName Variable name * @param code The expression * @return the result of the expression * */ override reified function let(variableName : String, code : block() : T ) : T { return logCalculation(variableName, code, true) } /** * Logs the assignment of an expression to an existing variable * * @param variableName the variable name * @param code The expression * @return the result of the expression */ override reified function store(variableName : String, code : block() : T ) : T { return logCalculation(variableName, code, false) } /** * Logs the return of a value of a function * * @param variableName the variable name * @param value The constant value * @return the result of the expression */ override reified function retrn(value : T) : T { return retrn(\-> _first.co(value)) } /** * Logs the return of a value of a function * * @param variableName the variable name * @param code The code block * @return the result of the expression */ override reified function retrn(code : block() : T) : T { return logEntry( code, \v -> new WorksheetFunctionReturn() {:Result = v, :ResultType = getValueType(v)} ) } /** * Logs the assignment of an expression to an object property * * @param objectName the object name * @param obj the object we're setting the property on * @param propertyName the property name * @param code The expression * @return the result of the expression */ override reified function set(objectName: String, obj: Object, propertyName: String, code: block(): T ) : T { return logCalculation(objectName, obj, propertyName, code) } /** * Logs the invocation of a static function with no return value * * @param clazz The class containing the function * @param subroutineName The function's name * @param code the subroutine expression * */ override function subr(clazz : IType, functionName : String, code : block() ) { subr(clazz, functionName, null, code) } /** * Logs the invocation of a static function with no return value * * @param clazz The class containing the function * @param functionName The subroutine's name * @param type Arbitrary string value; may be useful as a rendering hint * @param code the subroutine expression * */ override function subr(clazz : IType, functionName : String, type : String, code : block() ) { var entry = new WorksheetStaticSubroutine() { :FunctionName = functionName, :Type = type, :ClassName = clazz.DisplayName } logEntry(code, \ -> entry) } /** * Logs the invocation of a function with no arguments and no return value. * * @param functionName the function name * @param code the subroutine expression */ override function method(functionName: String, code: block()) { var entry = new WorksheetInstanceSubroutine() { :FunctionName = functionName} logEntry(code, \-> entry) } /** * Logs the invocation of a function on an object with no return value * * @param objectName the object's variable name * @param object The object on which the function is being invoked * @param functionName The function name * @param code the subroutine expression * */ override function method(objectName : String, object : Object, functionName : String, code : block() ) { method(objectName, object, functionName, null, code) } /** * Logs a function call on an object with no return value * * @param objectName the object's variable name * @param object The object on which the function is being invoked. * @param functionName The function name * @param type the type of subroutine, such as "Rounding" or any other type string wish to capture * @param code the subroutine expression * */ override function method(objectName : String, object : Object, functionName : String, type : String, code : block() ) { var entry = new WorksheetInstanceSubroutine() { :FunctionName = functionName, :Type = type, :Object = object, :ObjectName = objectName, :ObjectType = (typeof object).DisplayName } logEntry(code, \-> entry) _entryContainer = entry.Parent } /** * Logs a message before loop start * * @param iterableName the Iterable/array being looped over * @param iterableTypeName type of the Iterable/array * @param loopVarName loop variable * @param loopVarTypeName type of the loop variable, i.e., type of elements in the Iterable/array * @param loopIndexVarName loop index variable */ override function loop(iterableName: String, iterableTypeName: String, loopVarName: String, loopVarTypeName: String, loopIndexVarName: String, iterableSize: int) { var entry = new WorksheetLoop() {:IterableName = iterableName, :IterableTypeName = iterableTypeName, :IterableSize = iterableSize, :LoopVariableName = loopVarName, :LoopVariableTypeName = loopVarTypeName, :LoopIndexVariableName = loopIndexVarName} addTheWorksheetEntry(entry) entry.ParentOperandContainer = _operandContainer _entryContainer = entry _operandContainer = entry } override function endloop() { var w = _entryContainer as WorksheetLoop _entryContainer = w.Parent _operandContainer = w.ParentOperandContainer } /** * Logs a message at the start of each loop iteration * * @param iterableName the Iterable/array being looped over * @param iterableTypeName type of the Iterable/array * @param loopvarName loop variable * @param loopvarTypeName type of the loop variable, i.e., type of elements in the Iterable/array * @param loopIndexVarName loop index variable name * @param loopIndexVarValue value of the loop index variable */ override function iteration(iterableName: String, iterableTypeName: String, loopvarName: String, loopvarTypeName: String, loopIndexVarName: String, loopIndexVarValue: int) { var entry = new WorksheetIteration() { :IterableName = iterableName, :IterableTypeName = iterableTypeName, :ElementName = loopvarName, :ElementTypeName = loopvarTypeName, :LoopIndexVariableName = loopIndexVarName, :LoopIndexVariableValue = loopIndexVarValue } addTheWorksheetEntry(entry) } override function conditionals(code : block()) { var originalContainer = _entryContainer code() addTheWorksheetEntry(new WorksheetConditional() { :Type = WorksheetConditional.ConditionalType.EndIfCondition, :Result = true }) _entryContainer = originalContainer } /** * Logs an if statement * @param code the expression within the if statement * @return the result of the conditional statement */ override function iff(code : block() : boolean ) : boolean { beginConditional(true) var result = code() setConditionalResult(result) return result } /** * Logs an else if statement * @param code the expression within the else if statement * @return the result of the conditional statement * */ override function elsif(code : block() : boolean ) : boolean { endConditional(false) beginConditional(false) var result = code() setConditionalResult(result) return result } /** * Logs an else statement * @param code the expression within the else statement * @return the result of the conditional statement * */ override function els() { var entry = new WorksheetConditional() { :Type = WorksheetConditional.ConditionalType.ElseCondition, :Result = true } addTheWorksheetEntry(entry) _entryContainer = entry } /** * The condition of an if or else-if. * @param initialIf if true then this condition is of the first if */ override function beginConditional(initialIf: boolean) { var entry = new WorksheetConditional() { :Type = initialIf ? WorksheetConditional.ConditionalType.IfCondition : WorksheetConditional.ConditionalType.ElseIfCondition } addTheWorksheetEntry(entry) entry.ParentOperandContainer = _operandContainer _entryContainer = entry _operandContainer = entry } /** * Logs the value of a condition of an if or else-if * @param value result of evaluating the condition */ override function setConditionalResult(value: boolean) { (_entryContainer as WorksheetConditional).Result = value } /** * End of a branch of an if statement. * @param isEndif true if this is the last branch */ override function endConditional(isEndif: boolean) { var w = _entryContainer as WorksheetConditional _entryContainer = w.Parent _operandContainer = w.ParentOperandContainer if (isEndif) { addTheWorksheetEntry(new WorksheetConditional() { :Type = WorksheetConditional.ConditionalType.EndIfCondition, :Parent = _entryContainer, :Result = true }) } } /** * Logs a note * @param note The note * */ override function note(note : String) { addTheWorksheetEntry(new WorksheetNote() { :Description = note } ) } /** * Logs a note of a specific type * * @param type Note type * @param note Note text * */ override function note(type : String, note : String) { addTheWorksheetEntry(new WorksheetNote() { :Description = note, :Type = type } ) } /** * Logs an empty line * */ override function br() { addTheWorksheetEntry(new WorksheetNote() {}) } /** * Logs a named function argument of a constant value. * * @param name The argument name * @param value The constant value * @return the return value of the expression */ override reified function arg(name : String, value : T) : T{ return arg(name, \ -> _first.co(value) ) } /** * Logs a named function argument * * @param name The argument name * @param code the expression representing the value of the argument * @return the return value of the expression */ override reified function arg(name : String, code : block() : T) : T{ var arg = new WorksheetArgument() { :Name = name } return pushExecPop(arg, \-> code()) } /** * Logs a named function argument that comes from a local variable. * * @param name The argument name * @param code the expression representing the value of the argument * @return the return value of the expression */ override reified function argVar(name : String, localVariableName : String, localVariableValue : T) : T{ var arg = new WorksheetArgument() { :Name = name } return pushExecPop(arg, \ -> _first.vr(localVariableName, localVariableValue)) } override reified function loggedRateQuery(op: String, rateBook: RateBook, table: String): WorksheetLoggedRateQuery { return loggedRateQuery(op, 0, 0, rateBook, table) } override reified function loggedRateQuery(op : String, leftParens : int, rightParens : int, rateBook : RateBook, table : String) :WorksheetLoggedRateQuery { return new WorksheetLoggedRateQuery(this, op, leftParens, rightParens, rateBook, table) } /** * @return the worksheet entry container * */ property get WorksheetEntryContainer() : WorksheetEntryContainer{ return _rootContainer } /** * Sets the worksheet entry container * @param container the container * */ property set WorksheetEntryContainer(container : WorksheetEntryContainer){ _rootContainer = container _entryContainer = container _operandContainer = null } /** * Clear the logger */ override function clear(){ _rootContainer = null _entryContainer = null _operandContainer = null } private function getValueType(returnVal : Object) : String { // Typekey base entities confuse deserialization if (returnVal typeis TypeKey) { // implicit cast not working for type vars - PL-28298 return (returnVal as TypeKey).IntrinsicType.Name } if (returnVal typeis List) { if (returnVal.Empty) { var t = typeof returnVal if (t.ParameterizedType) { return "java.util.List<" + t.TypeParameters[0].Name + ">" } else { return "java.util.List" } } else { var t = (typeof returnVal.first()).Name return "java.util.List<" + t + ">" } } return TypeSystem.getFromObject(returnVal).Name } protected function pushExecPop(operand : WorksheetOperand, code : block() : T) : T{ return push(operand).pop(code()) } protected function push(operand : WorksheetOperand) : WorksheetLoggerPushFrame { // Push this operand onto the "stack" var parentOperand = _operandContainer parentOperand.addWorksheetOperand(operand) // Execute the code and capture return value _operandContainer = operand var prevEntryContainer : WorksheetEntryContainer if (_operandContainer typeis WorksheetEntryContainer) { prevEntryContainer = _entryContainer _entryContainer = _operandContainer } return new WorksheetLoggerPushFrame(operand, prevEntryContainer, parentOperand) } final class WorksheetLoggerPushFrame implements PushFrame { private final var operand : WorksheetOperand private final var prevEntryContainer : WorksheetEntryContainer private final var parentOperand : WorksheetOperandContainer construct(worksheetOperand : WorksheetOperand, previousContainer : WorksheetEntryContainer, parentContainer : WorksheetOperandContainer) { this.operand = worksheetOperand this.prevEntryContainer = previousContainer this.parentOperand = parentContainer } function pop(returnVal : T) : T { operand.Value = returnVal operand.ValueType = getValueType(returnVal) // Pop the current operand if (prevEntryContainer != null) { _entryContainer = prevEntryContainer } _operandContainer = parentOperand return returnVal } } protected function pushVfn(entry : WorksheetInstanceSubroutine) : WorksheetPushVoidFunctionFrame { // Push this operand onto the "stack" var prevOperandContainer = _operandContainer _operandContainer = entry return new WorksheetPushVoidFunctionFrame(entry, prevOperandContainer) } private function logCalculation(s : String, code : block() : T, variableDeclaration : boolean) : T { return logEntry( code, \ v -> new WorksheetCalculation() { :StoreLocation = s, :Result = v, :ResultType = getValueType(v), :VariableDeclaration = variableDeclaration } ) } private function logCalculation(objectName : String, obj : Object, propertyName : String, code : block() : T) : T { return logEntry( code, \ v -> new WorksheetCalculation() { :StoreObjectName = objectName, :StoreObjectType = getValueType(obj), :StorePropertyName = propertyName, :Result = v, :ResultType = getValueType(v) } ) } private function logEntry(code : block() : T, initializeEntryBlock : block(returnVal : T) : WorksheetEntry) : T{ var returnVal : T // capture the return value in the code block invocation and pass it to the initializeEntryBlock // which will then record it in the appropriate worksheet object property logEntry( \ -> { returnVal = code() }, \ -> initializeEntryBlock(returnVal) ) return returnVal } private function logEntry(code : block(), initEntryBlock : block() : WorksheetEntry){ var _prevOperandContainer = _operandContainer // Create an operand container to store the operand _operandContainer = new WorksheetFunction() { :Parent = _prevOperandContainer, :WorksheetOperands = new ArrayList() } var operandContainer = _operandContainer try { // exec code code() } finally { // Create the worksheet calc entry with the store location and return value var workSheetEntry = initEntryBlock() // Add all of the operands accumulated in the execution of the code block workSheetEntry.addWorksheetOperands(operandContainer.WorksheetOperands) // Restore the pointer to the previous operand container _operandContainer = _prevOperandContainer // Add this calculation step to the list of calculations addTheWorksheetEntry(workSheetEntry) } } private function addTheWorksheetEntry(entry : WorksheetEntry){ if( _entryContainer != null ) { _entryContainer.addWorksheetEntry(entry) } } override function toString() : String{ return _rootContainer == null ? "" : _rootContainer.worksheetEntriesString() } override public function startAssignment(variableName : String, variableDeclaration : boolean) : AssignmentFrame { return new WorksheetAssignmentFrame(this, new WorksheetCalculation() { :StoreLocation = variableName, :VariableDeclaration = variableDeclaration }) } override public function startSet(objectName : String, obj : Object, propertyName : String) : AssignmentFrame { return new WorksheetAssignmentFrame(this, new WorksheetCalculation() { :StoreObjectType = getValueType(obj), :StoreObjectName = objectName, :StorePropertyName = propertyName }) } override public function startCostDataRounding() : AssignmentFrame { return new WorksheetAssignmentFrame(this, new WorksheetInstanceSubroutine() { :FunctionName = "setRounding", :Type = "CostDataRounding", :Object = CostDataRoundingStatement.CostDataSymbolKey, :ObjectName = CostDataRoundingStatement.CostDataSymbolKey, :ObjectType = String.DisplayName }) } public static final class WorksheetAssignmentFrame implements AssignmentFrame { private final var _previousOperandContainer : WorksheetOperandContainer private final var _worksheetLogger : WorksheetLogger private final var _worksheetEntry : WorksheetEntry private construct(worksheetLogger : WorksheetLogger, worksheetEntry : WorksheetEntry) { _previousOperandContainer = worksheetLogger._operandContainer _worksheetLogger = worksheetLogger _worksheetEntry = worksheetEntry _worksheetLogger._operandContainer = new WorksheetFunction() // Add this calculation step to the list of calculations _worksheetLogger.addTheWorksheetEntry(_worksheetEntry) } override public function finishAssignment(result : T) { var w = _worksheetEntry as WorksheetEntryWithResult w.Result = result w.ResultType = _worksheetLogger.getValueType(result) // T.Name instead? finish() } override public function finish() { // Add all of the operands accumulated in the execution of the code block _worksheetEntry.addWorksheetOperands(_worksheetLogger._operandContainer.WorksheetOperands) // Restore the pointer to the previous operand container _worksheetLogger._operandContainer = _previousOperandContainer } } public static final class WorksheetOperationFrame implements OperationFrame { private final var frame : WorksheetLoggerPushFrame construct(worksheetLoggerPushFrame : WorksheetLoggerPushFrame) { this.frame = worksheetLoggerPushFrame } @SuppressWarnings("unused") function calcValue(value : T) : T { return frame.pop(value) } } public final class WorksheetPushVoidFunctionFrame implements PushVoidFunctionFrame { private final var prevOperandContainer : WorksheetOperandContainer private final var workSheetEntry : WorksheetInstanceSubroutine construct(entry : WorksheetInstanceSubroutine, prevContainer : WorksheetOperandContainer) { this.workSheetEntry = entry this.prevOperandContainer = prevContainer } function popVfn() { // Add all of the operands accumulated in the execution of the code block addTheWorksheetEntry(workSheetEntry) // Restore the pointer to the previous operand container _operandContainer = prevOperandContainer } } public static final class WorksheetVoidFunctionFrame implements VoidFunctionFrame { private final var frame : WorksheetPushVoidFunctionFrame construct(pushFrame : WorksheetPushVoidFunctionFrame) { this.frame = pushFrame } @SuppressWarnings("unused") function finishVFn() { frame.popVfn() } } }