package gw.rating.flow uses com.google.common.base.Optional uses gw.api.system.PCLoggerCategory uses gw.lang.parser.TypeSystemAwareCache uses gw.lang.reflect.IType uses gw.lang.reflect.TypeSystem uses gw.util.concurrent.Cache uses gw.util.concurrent.LockingLazyVar uses java.lang.Throwable uses java.math.BigDecimal /** * Abstract superclass for classes which provide methods for use in RateFlow routines. * Subclasses of this method are instantiated reflectively and are therefore required to * have a no-args constructor. */ abstract class RatingFunctionSource { static final var _logger = PCLoggerCategory.FINANCIALS /** * IMPORTANT: concrete implementations should either override this method * with the protected attribute, or annotate it as @VisibleInRateflow(false) * Otherwise the method availableForLine(String) will be available in the rate routine GUI. * @param policyLineCode a code corresponding to a PolicyLine * @return true if this class' functions apply to the given policy line, false otherwise. */ abstract protected function availableForLine(policyLineCode : String) : boolean final property get SymbolName() : String { return "_" + this.IntrinsicType.Name.replace('.', '_') } /** * @param policyLineCode a code corresponding to a PolicyLine * @return a list of RatingFunctionSource instances which are valid for the given policyLineCode */ static function getFunctionSourcesForLine(policyLineCode : String) : List { return table.get(Optional.fromNullable(policyLineCode)) } // ------------------------------ cache implementation --------------------------/ // Keep a list of the subtypes of RatingFunctionSource. It is a LazyVar primarily so that we can // clear it on a TypeSystem refresh and have it do the right thing. private static var subtypes = LockingLazyVar.make(\-> { // IType.Subtypes can take a long time because it may have to load every class in the system. // So we will keep statistics on how long it takes. var startTime = java.lang.System.currentTimeMillis() var types = RatingFunctionSource.Type.Subtypes.map(\ t -> makeInstance(t)).where(\ t -> t != null) _logger.info("RatingFunctionSource subtypes found in ${getRunTime(startTime)} seconds") types.each(\elt -> _logger.info("Found RatingFunctionSource subtype ${elt.Class.Name}")) return types }) // When the instance list for the desired line code is not found, the miss handler // is called to generate it. This just gets the value from subtypes() and then filters // it using the "availableForLine" function. // Note: have to explicitly acquire the TypeSystem global lock to avoid a lock ordering issue related to the TypeSystemAwareCache. private static var missHandler = new Cache.MissHandler, List>() { override function load(lineCode : Optional) : List { using (TypeSystem.getGlobalLock()) { return subtypes.get().where(\ i -> i.availableForLine(lineCode.orNull())) } } } // This is the actual repository for the cache, which returns a list of RatingFunctionSource // instances applicable to a given PolicyLine Code. It is TypeSystemAware, which means it will be cleared // on a typesystem refresh. It also behaves like an AutoMap, calling a handler when there is a cache miss. private static var table : TypeSystemAwareCache, List> = new TypeSystemAwareCache, List>("RatingFunctionSourceCache", 8, missHandler) { override function clear() { // if the typesystem has been refreshed, clear the subtypes as well as the cache. subtypes.clear() super.clear() } } // @return an instance of the given IType, or null if construction fails private static function makeInstance(t : IType) : RatingFunctionSource { try { return t.TypeInfo.getConstructor({}).Constructor.newInstance({}) as RatingFunctionSource } catch (e : Throwable) { // if anything at all goes wrong, log the error and return null _logger.error("Could not instantiate ${t.Name}: ${e}") return null } } // generate timing information, using the given start time (a long value containing milliseconds since 1/1/1970) // @return a floating-point representation of the number of seconds between startTime and now. private static function getRunTime(startTime : long) : BigDecimal { return ((java.lang.System.currentTimeMillis() - startTime) / 1000.0bd).setScale(3) } }