package gw.rating.rtm.query uses gw.api.locale.DisplayKey uses gw.api.rating.DefaultFactorSourceLogger uses gw.api.rating.RateQueryResult uses gw.api.rating.RatingException uses gw.api.system.PCDependenciesGateway uses gw.api.system.PCLoggerCategory uses gw.rating.RateFlowLogger uses gw.rating.rtm.domain.OrderedPersistenceAdapter uses gw.rating.rtm.matchop.StatelessMatchOperator uses java.lang.Comparable uses java.lang.Integer uses java.util.Collections uses java.util.Map uses java.util.concurrent.ConcurrentHashMap uses gw.rating.rtm.matchop.InterpolatingMatchOperator uses gw.util.Pair uses java.util.List uses gw.api.extension.ExtensionInterfaces @ReadOnly abstract class AbstractStatelessQuery implements RatingQuery { final protected var _matchOps: List as readonly MatchOps protected var _numActiveOps: int as readonly NumActiveOps var interpState : IInterpolationState static var _cache = initCache() construct(table: RateTable) { _matchOps = getMatchOperators(table.Definition) _numActiveOps = _matchOps.size() } static function initCache(): ConcurrentHashMap, List> { var cache = new ConcurrentHashMap, List>() PCDependenciesGateway.getRateTableDefinitionCache().addListener(\-> cache.clear()) return cache } private static function getCacheKey(def : RateTableDefinition) : Pair { return Pair.make(def.TableCode, def.PolicyLine) } static function getMatchOperators(def: RateTableDefinition): List { var ops = _cache.get(getCacheKey(def)) if (ops == null) { ops = Collections.unmodifiableList(def.SortedMatchOps.map(\o -> o.newStatelessInstance())) _cache.put(getCacheKey(def), ops) } return ops } @Deprecated(:value="Use version that takes Comparable array instead of list", :version="8.0.1") override reified function query(table: RateTable, inputParams: List, factorCode: String): RateQueryResult { return query(table, inputParams?.partitionUniquely(\e -> e.Name), factorCode) } @Deprecated(:value="Use version that takes Comparable array instead of list", :version="8.0.1") override reified function query(table: RateTable, inputParams: Map, factorCode: String): RateQueryResult { var getParam = \n: String -> { var param = inputParams?.get(n) if (param == null) { throw new RatingException(DisplayKey.get("Web.Rating.Errors.TableArgumentListMismatch", inputParams.Values, table.Definition.TableCode)) } return param } var compArray = AbstractStatelessQuery.getMatchOperators(table.Definition) .map(\o -> o.makeConvertedParam(getParam(o.MatchOpName).Value)) .toTypedArray() return query(table, compArray, factorCode) } override reified function query(table: RateTable, inputParams: Comparable[], factorCode: String): RateQueryResult { if (factorCode?.size == 0) { factorCode = table.Definition.Factors[0].ColumnName } var result = queryMultipleFactors(table, inputParams, {factorCode}).get(factorCode) if (result != null) { return new RateQueryResult(result as R, new DefaultFactorSourceLogger()) } // Log at warning level if no factor found PCLoggerCategory.RTM .warn("No results found for Rate Table Query [book=" + table.RateBook.DisplayName + "; table=" + table.DisplayName + "; params=" + inputParams.toList() + "]") return new RateQueryResult() } override reified function query(table: RateTable, inputParams: Comparable[]): RateQueryResult { return query(table, inputParams, null) } private function convertParams(table: RateTable, inputParams: Comparable[]): Comparable[] { var convertedInputParams = new Comparable[inputParams.length] AbstractStatelessQuery.getMatchOperators(table.Definition) .eachWithIndex(\op, i -> {convertedInputParams[i] = op.makeConvertedParam(inputParams[i]) }) return convertedInputParams } override reified function convertParamsAndQuery(table: RateTable, inputParams: Comparable[], factorCode: String): RateQueryResult { return query(table, convertParams(table, inputParams), factorCode) } override function convertAndQueryMultipleFactors(table: RateTable, inputParams: Comparable[]) : Map { var factorCodes = table.Definition.Factors.map(\f -> f.ColumnName).toList() return queryMultipleFactors(table, convertParams(table, inputParams), factorCodes) } protected property get InterpolationIsOrdered() : boolean { return false } protected function initializeInterpolation(existingState : IInterpolationState, tableDef : RateTableDefinition, inputParams : Comparable[]) : IInterpolationState { if (existingState != null) { // null out existingState if interpolated matchop is relaxed away return existingState.InterpolatedMatchOpIndex < NumActiveOps ? existingState : null } var interpolatedMatchOp = _matchOps.firstWhere(\ o -> o typeis InterpolatingMatchOperator) var interpolatedMatchOpIndex = _matchOps.indexOf(interpolatedMatchOp) if (interpolatedMatchOpIndex < 0 or interpolatedMatchOpIndex >= NumActiveOps) { return null } else { return ExtensionInterfaces.IInterpolationStateFactory.newInstance(InterpolationIsOrdered, tableDef, _matchOps[interpolatedMatchOpIndex] as InterpolatingMatchOperator, interpolatedMatchOpIndex, inputParams) } } protected function extractFactors(tableDef: RateTableDefinition, matchedRows: List, factorCodes: List, inputParams: Comparable[]): Map { var numRows = matchedRows?.size() if (numRows == 0) { return null// null is required return value if nothing matched } if (factorCodes == null) { // If factorCodes is null, assume only single factor // exists for the target table and use it. factorCodes = {tableDef.Factors.sortBy(\f -> f.SortOrder).first().ColumnName} } var resultRow: OrderedPersistenceAdapter interpState = initializeInterpolation(interpState, tableDef, inputParams) if (interpState != null) { // extractInterpolationRows returns a new interpState with updated rows interpState = interpState.MatchOp.extractInterpolationRows(interpState, MatchOps, NumActiveOps, matchedRows) // now try to get a result. Will return null if it can't interpolate return interpState.MatchOp.interpolateResult(interpState, factorCodes) } else if (numRows == 1) { resultRow = matchedRows.get(0) } else { resultRow = ExtensionInterfaces.IInterpolationStateExtensions.findHighestScoringRow(ExtensionInterfaces.IInterpolationStateExtensions.getComparator(MatchOps, NumActiveOps, inputParams), matchedRows) } return factorCodes.mapToValue(\factorCode -> resultRow.getFieldValue(tableDef.getFactorPhysicalColumnName(factorCode))) } protected property get IsFullyRelaxed(): boolean { return NumActiveOps < 0 } protected function relaxQuery() { // TODO: calculate the skip points rather than simply subtracting 1 _numActiveOps-- } protected function relaxToIndex(i: Integer) { if (i == null or i < 0 or i > _matchOps.size()) { throw new java.lang.IllegalArgumentException("Argument must be between 0 and " + _matchOps.size()) } _numActiveOps = i } protected function preRelax(inputParams: Comparable[]) { // pre-relax if there are nulls in the input for (p in inputParams index i) { if (p == null) { if (RateFlowLogger.Logger.DebugEnabled) { RateFlowLogger.Logger.debug("Found null input param at index " + i) } relaxToIndex(i) break } } } protected function resetNumActiveOpsAndCheckInputParams(table: RateTable, inputParams: Comparable[]) { _numActiveOps = _matchOps.size()// reset this, in case Query was executed more than once if (inputParams.length != NumActiveOps) { throw new RatingException(DisplayKey.get("Web.Rating.Errors.TableArgumentListMismatch", inputParams.join(","), table.Definition.TableCode)) } } abstract override function queryMultipleFactors(table: RateTable, inputParams: Comparable[], factorCodes: List): Map }