sourceafBedSheet::ValueEncoderSource.fan

using afIoc::StrategyRegistry

** Holds a collection of `ValueEncoder`s.
** 
** pre>
**   @Contribute { serviceType=ValueEncoderSource# }
**   static Void contributeValueEncoders(MappedConfig config) {
**     config.addMapped(MyEntity#, config.autobuild(MyEntityEncoder#))
**   }
** <pre
** 
** @uses a 'MappedConfig' of 'Type' to `ValueEncoder`s
const class ValueEncoderSource {
    
    private const StrategyRegistry valueEncoderStrategy
    
    new make(Type:ValueEncoder valueEncoders) {
        this.valueEncoderStrategy = StrategyRegistry(valueEncoders)
    }
    
    ** Converts the given 'value' to Str via a contributed `ValueEncoder`. If no 'ValueEncoder' is 
    ** found, 'toStr()' is used. 
    Str toClient(Type valType, Obj value) {
        // check the basics first!
        if (value is Str)
            return value
        
        valEnc := get(valType)
        if (valEnc != null)
            try {
                return valEnc.toClient(value)
            } catch (Err cause) {
                throw ValueEncodingErr(BsMsgs.valueEncodingBuggered(value, Str#), cause)
            }
        
        return value.toStr
    }

    ** Converts the given 'clientValue' into the given 'valType' via a contributed `ValueEncoder`. 
    ** If no 'ValueEncoder' is found, this looks for a suitable static factory 'fromStr()' method
    ** on the type.
    Obj toValue(Type valType, Str clientValue) {
        // check the basics first!
        if (valType.fits(Str#))
            return clientValue

        valEnc := get(valType)
        if (valEnc != null)
            try {
                return get(valType).toValue(clientValue)
            } catch (Err cause) {
                throw ValueEncodingErr(BsMsgs.valueEncodingBuggered(clientValue, valType), cause)
            }
        
        // see http://fantom.org/sidewalk/topic/2154
        fromStr := ReflectUtils.findCtor(valType, "fromStr", [Str#])
        if (fromStr == null)
            fromStr = ReflectUtils.findMethod(valType, "fromStr", [Str#], true)
        if (fromStr == null)
            throw ValueEncodingErr(BsMsgs.valueEncodingNotFound(valType))
        
        try {
            return fromStr.call(clientValue) 
        } catch (Err cause) {
            throw ValueEncodingErr(BsMsgs.valueEncodingBuggered(clientValue, valType), cause)
        }
    }

    private ValueEncoder? get(Type valueType) {
        valueEncoderStrategy.findExactMatch(valueType, false)
    }
}