package gw.rating.flow uses gw.api.domain.covterm.CovTerm uses gw.api.extension.ExtensionInterfaces uses gw.api.locale.DisplayKey uses gw.api.productmodel.CovTermPattern uses gw.api.productmodel.CovTermPatternLookup uses gw.api.upgrade.Coercions uses gw.api.util.DisplayableException uses gw.api.util.PCNumberFormatUtil uses gw.entity.ITypeList uses gw.lang.reflect.IType uses gw.lang.reflect.TypeSystem uses gw.rating.flow.util.InScopeUsage uses javax.lang.model.type.ErrorType uses java.math.BigDecimal uses java.text.SimpleDateFormat @ReadOnly enhancement CalcStepValueDelegateEnhancement: entity.CalcStepValueDelegate { private static function determineConstantTypeForStringValue(constantValue: String): IType { var t = deduceTypeFromValue(constantValue) if (t == ErrorType) { throw new DisplayableException(DisplayKey.get("Validation.Rating.UntypedConstantValue")) } return t } property get TypeOfConstant(): IType { if (this.OperandType != null and this.OperandType != TC_CONSTANT) { throw "Illegal attempt to access property ConstantType for operand type " + this.OperandType } var inScopeValueType = this.InScopeValueType if (inScopeValueType != null and typeIsInvalid(inScopeValueType)) { throw new DisplayableException(DisplayKey.get("Validation.Rating.UntypedConstantValue")) } if (this.ConstantValue == null) { return null } if (typeIsTypeKey(inScopeValueType)) { return gw.entity.TypeKey } if (typeIsDate(inScopeValueType)) { return Date } if (typeIsBoolean(inScopeValueType)) { return Boolean } if (typeIsInteger(inScopeValueType)) { return Integer } if (typeIsBigDecimal(inScopeValueType)) { return BigDecimal } // TODO: It would be better not to have to do this, but unless/until we write an // upgrade trigger for old rating data, we have no choice. If we *can* get rid of // this, we may still need to check for ConstantValue == "null"/"Null"/"NULL" return determineConstantTypeForStringValue(this.ConstantValue) } property get LocalizedConstantValue(): String { var valueType = this.InScopeValueType ?: deduceTypeFromValue(this.ConstantValue).Name try { if (typeIsBigDecimal(valueType)){ var decConstant = new BigDecimal(this.ConstantValue) var decConstantAfterRender = PCNumberFormatUtil.render(decConstant) var fmt = PCNumberFormatUtil.getNumberFormat() if(decConstantAfterRender.indexOf(fmt.DecimalSymbol)<0 and decConstant.scale()>0) { return decConstantAfterRender.concat(fmt.DecimalSymbol).concat("0".repeat(decConstant.scale())) } return decConstantAfterRender } else if (typeIsInteger(valueType)){ return PCNumberFormatUtil.render(Integer.valueOf(this.ConstantValue)) } else { return this.ConstantValue } } catch (e: java.lang.NumberFormatException) { // This shouldn't really happen, because the setter is putting valid data into the field. throw new DisplayableException(DisplayKey.get("Validation.Rating.UntypedConstantValue")) } } property set LocalizedConstantValue(str : String) { str = str?.trim() var t = deduceTypeFromValue(str) if (t == null) { this.TypeDeclaration = null this.ConstantValue = (str == null or str.Empty) ? null : "null" return } this.TypeDeclaration = t == ErrorType ? InvalidType : t.toString() if (gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(java.lang.Number.Type, t)) { var v = parseNum(str) this.ConstantValue = v.toString() } else if (t == java.lang.Boolean){ this.ConstantValue = str.toLowerCase() } else { this.ConstantValue = str } } static function deduceTypeFromValue(str : String) : IType { str = str?.trim() if (str == null or str.Empty) return null try { var v = parseNum(str) return typeof v } catch (e2: org.apache.commons.beanutils.ConversionException) { } catch (e2: java.text.ParseException) { } // "true/false" and null are not localizable if (str.matches("^(true|True|TRUE|false|False|FALSE)$")) { return java.lang.Boolean } else if (str.matches("^(null|Null|NULL)$")){ return null } else if (str.matches("^\".*\"$")) { // this will accept some illegal strings, e.g. "a"b" (should be "a\"b") // we rely on validation elsewhere to prevent this case, because a correct // regular expression would be overkill. return java.lang.String } return ErrorType } private static function parseNum(str : String) : java.lang.Number { var fmt = PCNumberFormatUtil.getNumberFormat() str = str.trim() if (str.endsWith("bd")) { return PCNumberFormatUtil.parse(str.substring(0, str.length() - 2)) } else if (str.indexOf(fmt.DecimalSymbol) >= 0 or str.toLowerCase().indexOf("e") > 0) { // if there's a decimal point or an "e" treat it as BigDecimal return PCNumberFormatUtil.parse(str) } else { // assume that without a decimal separator OR a 'bd' suffix, it's an int. // What happens in the (very strange!) case where integer format and bigdecimal format look different? // (Will this really happen?) To avoid it, we would need an explicit NumberFormatUtil.parseIntOnly() var v = PCNumberFormatUtil.parse(str) try { return v.intValueExact() as Integer } catch (notInt : java.lang.ArithmeticException) { // rounding was necessary, or overflow occurred. return v } } } /** * This property on the enhancement can go away if we rename InScopeValueType to TypeDeclaration */ property get TypeDeclaration(): String { return this.InScopeValueType } property set TypeDeclaration(typeDecl: String) { this.InScopeValueType = typeDecl } //This function used only to return a date to be displayed on the UI, and set it to Today by default //DateConstantValue is the function to be used in several places to return the "Date Constant Value" from the entity property get DateConstantValueForUI(): java.util.Date { //The date widget won't call the setter on DateConstantValueForUI unless the user selects a different date //To overcome this issue, don't just display current date by default, but also manually set the date to current date if (this.ConstantValue == null or this.ConstantValue.Empty) { this.DateConstantValue = java.util.Date.Today } return this.DateConstantValue } property set DateConstantValueForUI(value: java.util.Date) { this.DateConstantValue = value } //This function used to return the "Date Constant Value" from the entity //Do not use DateConstantValueForUI for that purpose as that would set the constantValue to Today if null property get DateConstantValue(): java.util.Date { var dateParser = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", new Locale("en", "US")) return dateParser.parse(this.ConstantValue) } property set DateConstantValue(value: java.util.Date) { this.ConstantValue = (value == null) ? java.util.Date.Today as String : value.toString() TypeDeclaration = "java.util.Date" } property get IsDate(): boolean { return typeIsDate(TypeDeclaration) } private function typeIsDate(typeDecl: String): boolean { if (typeDecl == null) { return false } try { var t = TypeSystem.getByRelativeName(typeDecl) return gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(java.util.Date.Type, t) } catch (e: java.lang.ClassNotFoundException) { return false } } property get BooleanConstantValue(): Boolean { return Coercions.makeBooleanFrom(this.ConstantValue) } property set BooleanConstantValue(value: Boolean) { this.ConstantValue = value == null ? null : value.toString() TypeDeclaration = "java.lang.Boolean" } property get IsBoolean(): boolean { return typeIsBoolean(TypeDeclaration) } private function typeIsBoolean(typeDecl: String): boolean { if (typeDecl == null) { return false } try { var t = TypeSystem.getByRelativeName(typeDecl) return gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(java.lang.Boolean.Type, t) or java.lang.Boolean.Type.isAssignableFrom(TypeSystem.getBoxType(t)) } catch (e: java.lang.ClassNotFoundException) { return false } } private function typeIsBigDecimal(typeDecl: String) : boolean { if (typeDecl == null) { return false } try { var t = TypeSystem.getByRelativeName(typeDecl) return gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(java.math.BigDecimal.Type, t) or java.math.BigDecimal.Type.isAssignableFrom(TypeSystem.getBoxType(t)) } catch (e: java.lang.ClassNotFoundException) { return false } } private function typeIsInteger(typeDecl: String) : boolean { if (typeDecl == null) { return false } try { var t = TypeSystem.getByRelativeName(typeDecl) return gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(java.lang.Integer.Type, t) or java.lang.Integer.Type.isAssignableFrom(TypeSystem.getBoxType(t)) } catch (e: java.lang.ClassNotFoundException) { return false } } private function typeIsInvalid(typeDecl: String) : boolean { return typeDecl == InvalidType } private static property get InvalidType() : String { return "Invalid Type" } property get TypekeyConstantValue(): gw.entity.TypeKey { return this.ConstantValue == null ? null : getTypekeyValue(TypeDeclaration, this.ConstantValue) } property set TypekeyConstantValue(value: gw.entity.TypeKey) { TypeDeclaration = value.IntrinsicType.Name this.ConstantValue = value.Code } property get IsTypeKey(): boolean { return typeIsTypeKey(TypeDeclaration) } property get IsNumeric(): boolean { var typeOfConstant = TypeOfConstant return typeOfConstant == BigDecimal or typeOfConstant == Integer // or TypeOfConstant == Number } property get IsEditableConstant() : boolean { return this.OperandType == TC_CONSTANT and !(this.IsDate or this.IsTypeKey) } property get IsRounding(): boolean { return this.OperandType == TC_ROUNDING } private function typeIsTypeKey(typeDecl: String): boolean { if (typeDecl == null) { return false } try { var t = TypeSystem.getByRelativeName(typeDecl) if (t.Interfaces.Count > 0) { return t.Interfaces.hasMatch(\i -> i.Name == "gw.entity.TypeKey") } else { return false } } catch (e: java.lang.ClassNotFoundException) { return false } } private function getTypekeyValue(typeDecl: String, code: String): gw.entity.TypeKey { if (typeDecl == null or code == null) { return null } try { var t = TypeSystem.getByRelativeName(typeDecl) as ITypeList return t.getTypeKey(code) } catch (e: java.lang.ClassNotFoundException) { return null } catch (cc: java.lang.ClassCastException) { return null } } function resetInScope() { this.InScopeParam = null this.InScopeValueType = null this.InScopeValue = null this.InScopeValueIsModifier = false } /** * This is what we want to pass in to method maybeClearConstantSubtypeValue when we want to indicate that * we are creating a Typelist Constant, but we don't know the specific typelist yet. */ property get TYPELIST_SUBTYPE(): String { return "gw.entity.TypeList" } // We have issues whenever we switch from one type of constant to another: // date <-> typekey, typekey <-> untyped, untyped <-> date // Eventually this may improve (because you cannot change to an untyped constant w/o // invoking clear) but we STILL will have the problem between typekey and date. function maybeClearConstantSubtypeValue(newTypeDecl: String) { if (newTypeDecl == TYPELIST_SUBTYPE) { if (not IsTypeKey) { this.TypeDeclaration = null this.ConstantValue = null } } else if (newTypeDecl != this.TypeDeclaration) { this.TypeDeclaration = newTypeDecl this.ConstantValue = null } } function clearValueDelegateValues() { resetInScope() this.VariableName = null this.ConstantValue = null this.TypeDeclaration = null } function clearDependentValues() { if (this typeis CalcStepDefinitionArgument) { switch (this.Operand.OperandType) { case TC_RATETABLE: if (this.DependentList.HasElements) { var depList = this.DependentList depList.each(\dl -> { var argSource = this.Operand.ArgumentSources.singleWhere(\c -> c.Parameter == dl.MatchOp.Name) argSource.clear() }) } break default: // Nothing } } } property get StepDefinition(): CalcStepDefinition { var def: CalcRoutineDefinition if (this typeis CalcStepDefinitionOperand) { def = this.CalcStep.CalcRoutineDefinition } else if (this typeis CalcStepDefinitionArgument) { def = this.CalcRoutineDefinition } return def.OrderedSteps.firstWhere(\step -> step.StoreLocation == this.VariableName) } // Read-only property used to provide a UI friendly label for CalcStepValueDelegate // (e.g. CalcStepDefinitionOperands and CalcStepDefinitionArguments) property get StepValueDisplayName(): String { if (this.OperandType == null) { return "" } switch (this.OperandType) { case TC_CONSTANT: if (this.IsDate) { return DisplayKey.get("Web.Rating.Flow.CalcRoutine.RateConstantLabel", this.DateConstantValue.ShortFormat) } else if (this.IsTypeKey) { return this.TypekeyConstantValue != null ? DisplayKey.get("Web.Rating.Flow.TypeKeyConstantLabel", this.TypekeyConstantValue.IntrinsicType.RelativeName, // the associated list's name this.TypekeyConstantValue.DisplayName) // the typekey's display name : DisplayKey.get("Web.Rating.Flow.TypeKeyConstantLabel", this.TypeDeclaration.remove(ExtensionInterfaces.IRatingUIUtilExtensions.TypekeyPrefix), // the associated list's name this.ConstantValue) } return DisplayKey.get("Web.Rating.Flow.CalcRoutine.RateConstantLabel", this.LocalizedConstantValue) case TC_LOCALVAR: if (this.VariableFieldName.HasContent) { // Currently we assume that the only case this is set is in the case where // we are referencing factors off a MultiFactorVariable; we need to determine // the table who's multiple factors are stored in the targeted variaable. // NOTE: This does not take into the account the case where two different // multi-factor rate tables are storing all factors to the same multi-factor variable. var colLabel = this.StepDefinition.PrimaryOperand.RateTableDefinition.getFactorLabel(this.VariableFieldName) return DisplayKey.get("Web.Rating.Flow.CalcRoutine.RateVariableLabel", this.VariableName, DisplayKey.get("Web.Rating.Flow.CalcRoutine.LocalVarFieldLabel", colLabel)) } return DisplayKey.get("Web.Rating.Flow.CalcRoutine.RateVariableLabel", this.VariableName, "") case TC_INSCOPE: if (this.InScopeParam == TC_COSTDATA) { if (this.InScopeValue != null) { return DisplayKey.get("Web.Rating.Flow.CalcRoutine.CostDataStoreLabel", this.InScopeParam.DisplayName, this.InScopeValue) } else { return "" } } else if (this.InScopeValue != null) { if (this.CovTermCode != null) { var termPattern = CovTermPatternLookup.getByPublicID(this.CovTermCode) if (this.IsCovTerm) { return DisplayKey.get("Web.Rating.Flow.CalcRoutine.InScopeCovTermOperandLabel", this.InScopeParam.DisplayName, termPattern.DisplayName) } else { var suffix = InScopeUsage.getCovTermOptionDisplaySuffix(termPattern, this.InScopeValue) return DisplayKey.get("Web.Rating.Flow.CalcRoutine.InScopeCovTermValueOperandLabel", this.InScopeParam.DisplayName, termPattern.DisplayName, suffix) } } return DisplayKey.get("Web.Rating.Flow.CalcRoutine.InScopeOperandAndValueLabel", this.InScopeParam.DisplayName, this.InScopeValue) } else { return DisplayKey.get("Web.Rating.Flow.CalcRoutine.InScopeOperandLabel", this.InScopeParam.DisplayName) } case TC_LOOPVAR: return DisplayKey.get("Web.Rating.Flow.CalcRoutine.LoopVarOperand.Reference.Label", this.VariableName, this.VariableFieldName) default: throw new RuntimeException("Attempted to get Step Value Display Name for unsupported operand type: " + this.OperandType) } } static function isCoverageType(valueType : IType) : boolean { return gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(entity.Coverage.Type, valueType) } static function isCovTermType(valueType : IType) : boolean { return gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(CovTerm.Type, valueType) // we need to check CovTermPattern also, for backward compatibility. or gw.api.util.TypeUtil.isNominallyOrStructurallyAssignable(CovTermPattern.Type, valueType) } property get IsCovTerm() : boolean { var valueType = TypeSystem.getByRelativeName(this.InScopeValueType) return isCovTermType(valueType) } }