using afIoc::Injectusing afBeanUtils::TypeCoercerusing afBeanUtils::NotFoundErr** (Service) - Provides the config values. @Jsconstmixin ConfigSource {** Creates a 'ConfigSource' instance with the given 'ConfigProviders'.staticnew make(ConfigProvider[] configProviders){ ConfigSourceImpl(configProviders)}** Return the config value with the given id, optionally coercing it to the given type.** ** Throws 'ArgErr' if the ID could not be found. @Operatorabstract Obj? get(Str id, Type? coerceTo := null, Bool checked := true)** Returns a case-insensitive map of all the config valuesabstract Str:Obj? config()** Returns a case-insensitive map suitable for logging. ** Same as 'config()' but without environment variables and duplicated env config.abstract Str:Obj? configMuted()** Properties defined with this prefix override those without. ** Example, if 'env' equals 'dev' and the given the config contains:** ** acme.myProperty = wot ** dev.acme.myProperty = ever** ** Then the config 'acme.myProperty' would have the value 'ever'.** ** 'env' is set via the special config property 'afIocConfig.env'.abstract Str? env()}@Jsinternalconstclass ConfigSourceImpl : ConfigSource {privateconst TypeCoercer typeCoercer := TypeCoercer()overrideconst Str:Obj? configoverrideconst Str:Obj? configMutedoverrideconst Str? envnew make(ConfigProvider[] configProviders){ config := Str:Obj?[:]{ caseInsensitive = true} configProviders.each { config.setAll(it.config)}this.env = config["afIocConfig.env"]?.toStr?.trimToNull// tidy up the config by removing env vars and overrides configMuted := config.dup envPrefix := (env ?: "") + "." envPrefixes := typeCoercer.coerce(config["afIocConfig.envs"], Str?#)?.toStr?.split(',')?.map {"${it}."} ?: Str#.emptyList config.each |val, key| {if(env != null && key.startsWith(envPrefix)) configMuted[key[envPrefix.size..-1]] = valif(envPrefixes.any { key.startsWith(it)}) configMuted.remove(key)}this.config = configMuted.toImmutable// remove environment variables from muted map Env.cur.vars.keys.each {// we could be removing useful stuff here - maybe have a re-think!? configMuted.remove(it)} configMuted.remove("afIocConfig.env") configMuted.remove("afIocConfig.envs")this.configMuted = configMuted}override Obj? get(Str id, Type? coerceTo := null, Bool checked := true){if(!config.containsKey(id))return checked ? throw ConfigNotFoundErr(ErrMsgs.configNotFound(id), config.keys) : null value := config[id]if(value == null)returnnullif(coerceTo == null || value.typeof.fits(coerceTo))return valuereturn typeCoercer.coerce(value, coerceTo)}}** Thrown when a config ID has not been mapped.@Js @NoDoc // we can't subclass ArgErr in JS - see http://fantom.org/forum/topic/2468constclass ConfigNotFoundErr : Err, NotFoundErr {overrideconst Str?[] availableValuesoverrideconst Str valueMsg := "Available Config IDs:"new make(Str msg, Obj?[] availableValues, Err? cause := null) : super(msg, cause){this.availableValues = availableValues.map {it?.toStr }.sort}override Str toStr(){ NotFoundErr.super.toStr }}