sourceafJson::JsonTypeInspectors.fan

using afConcurrent::AtomicMap

** (Service) - 
** Holds a list of 'JsonTypeInspector' instances and the cached 'JsonTypeMeta' objects.
@Js
const mixin JsonTypeInspectors {

    ** Creates an instance of 'JsonTypeInspectors' with the given inspectors.
    static new make(JsonTypeInspector[] inspectors := defaultInspectors) {
        JsonTypeInspectorsImpl(inspectors)
    }

    ** The default list of inspectors.
    static JsonTypeInspector[] defaultInspectors() {
        [
            LiteralInspector(),
            NamedInspector(Map#,    MapConverter()),
            NamedInspector(List#,   ListConverter()),
            NamedInspector(Type#,   TypeConverter()),
            NamedInspector(Slot#,   SlotConverter()),
            NamedInspector(Method#, SlotConverter()),
            NamedInspector(Field#,  SlotConverter()),
            NamedInspector(Obj#,    LiteralConverter()),    // use Obj as a catch all
            SerializableInspector(),
            ObjInspector()
        ]
    }
    
    ** Returns and caches 'JsonTypeMeta' associated with the given type.
    @Operator
    abstract JsonTypeMeta getOrInspect(Type type)

    ** Sets 'JsonTypeMeta' to be associated with the given type.
    @Operator
    abstract Void set(Type type, JsonTypeMeta meta)
}

@Js
internal const class JsonTypeInspectorsImpl : JsonTypeInspectors {
    private const AtomicMap             metaObjs    := AtomicMap { it.keyType=Type#; it.valType=JsonTypeMeta# }
    private const JsonTypeInspector[]   inspectors
    
    new make(JsonTypeInspector[] inspectors) {
        this.inspectors = inspectors
    }

    override JsonTypeMeta getOrInspect(Type type) {
        metaObjs.getOrAdd(type) { 
            inspectors.eachWhile { it.inspect(type, this) } ?: Err("Could not find Inspector to process ${type.qname}")
        }
    }
    
    override Void set(Type type, JsonTypeMeta meta) {
        metaObjs[type] = meta
    }
}