|
| 1 | +import { Argument, Function, Implementation, Mapping, Predicate } from './models'; |
| 2 | +import { GraphHandler, LocalValue } from "./GraphHandler"; |
| 3 | +import { Handler } from './handlers/Handler'; |
| 4 | +import * as $rdf from "rdflib"; |
| 5 | + |
| 6 | +import prefixes from './prefixes'; |
| 7 | + |
| 8 | +export class FunctionHandler { |
| 9 | + private _graphHandler; |
| 10 | + private _handlerIndex = {}; |
| 11 | + |
| 12 | + constructor() { |
| 13 | + this._graphHandler = new GraphHandler(); |
| 14 | + } |
| 15 | + |
| 16 | + async addFunctionResource(iri: string, localValue: LocalValue | null = null) { |
| 17 | + await this._graphHandler.addGraph(iri, localValue) |
| 18 | + }; |
| 19 | + |
| 20 | + async addHandler(handler: Handler) { |
| 21 | + this._handlerIndex[handler.id] = handler; |
| 22 | + } |
| 23 | + |
| 24 | + async getFunction(iri: string): Promise<Function | null> { |
| 25 | + const term = this._graphHandler.getSubjectOfType(iri, `${prefixes.fno}Function`); |
| 26 | + if (term) { |
| 27 | + return new Function(term); |
| 28 | + } |
| 29 | + return null; |
| 30 | + } |
| 31 | + |
| 32 | + /** |
| 33 | + * @deprecated |
| 34 | + * @param iri |
| 35 | + * @returns |
| 36 | + */ |
| 37 | + async getPredicate(iri: string) { |
| 38 | + const term = this._graphHandler.getSubjectOfType(iri); |
| 39 | + if (term) { |
| 40 | + return new Predicate(term); |
| 41 | + } |
| 42 | + return null; |
| 43 | + } |
| 44 | + |
| 45 | + async executeFunction(fn: Function, args: Argument[], mapping: Mapping | null = null) { |
| 46 | + let mappings: Mapping[] = []; |
| 47 | + if (mapping === null) { |
| 48 | + mappings = this.getMappingsFromFunction(fn); |
| 49 | + } else { |
| 50 | + mappings = [mapping]; |
| 51 | + } |
| 52 | + const possibleImplementations: { |
| 53 | + mapping: Mapping, |
| 54 | + implementation: Implementation, |
| 55 | + handler: Handler |
| 56 | + }[] = []; |
| 57 | + if (mappings.length === 0) { |
| 58 | + console.warn(`Could not find any relevant mapping for function ${fn.id}`) |
| 59 | + } |
| 60 | + mappings.forEach(mapping => { |
| 61 | + const implementations = this.getImplementationFromMapping(mapping); |
| 62 | + if (implementations.length === 0) { |
| 63 | + console.warn(`Could not find any relevant implementation for mapping ${mapping.id}`) |
| 64 | + return; |
| 65 | + } |
| 66 | + implementations.forEach(implementation => { |
| 67 | + const handlers = this.getHandlers(implementation); |
| 68 | + if (handlers.length === 0) { |
| 69 | + console.warn(`Could not find any relevant handlers for implementation ${implementation.id}`) |
| 70 | + return; |
| 71 | + } |
| 72 | + handlers.forEach(handler => { |
| 73 | + possibleImplementations.push({ |
| 74 | + mapping, |
| 75 | + implementation, |
| 76 | + handler |
| 77 | + }) |
| 78 | + }) |
| 79 | + }) |
| 80 | + }) |
| 81 | + if (possibleImplementations.length === 0) { |
| 82 | + throw new Error(`Could not find any relevant implementation to execute ${fn.id}`) |
| 83 | + } |
| 84 | + const optimalImplementation = possibleImplementations[0]; |
| 85 | + return optimalImplementation.handler.executeFunction(optimalImplementation.implementation, this.getArgs(optimalImplementation.mapping, args)) |
| 86 | + } |
| 87 | + |
| 88 | + private getMappingsFromFunction(fn: Function) { |
| 89 | + const mappings = this._graphHandler.match(null, $rdf.sym(`${prefixes.fno}function`), fn.term); |
| 90 | + if (mappings.length === 0) { |
| 91 | + return []; |
| 92 | + } |
| 93 | + return mappings.map(m => new Mapping(m.subject)); |
| 94 | + } |
| 95 | + |
| 96 | + private getImplementationFromMapping(mapping: Mapping) { |
| 97 | + const implementations = this._graphHandler.match(mapping.term, $rdf.sym(`${prefixes.fno}implementation`)); |
| 98 | + if (implementations.length === 0) { |
| 99 | + return []; |
| 100 | + } |
| 101 | + return implementations.map(m => new Implementation(m.object)); |
| 102 | + } |
| 103 | + |
| 104 | + private getHandlers(implementation: Implementation) { |
| 105 | + const loadedHandlerClasses = Object.keys(this._handlerIndex); |
| 106 | + const handlers: Handler[] = []; |
| 107 | + loadedHandlerClasses.forEach(c => { |
| 108 | + const match = this._graphHandler.match(implementation.term, $rdf.sym(`${prefixes.rdf}type`), $rdf.sym(c)) |
| 109 | + if(match.length > 0) { |
| 110 | + handlers.push(this._handlerIndex[c]); |
| 111 | + } |
| 112 | + }) |
| 113 | + |
| 114 | + return handlers; |
| 115 | + } |
| 116 | + |
| 117 | + private getArgs(mapping: Mapping, args: Argument[]) { |
| 118 | + const result = {}; |
| 119 | + const parameterMappings = this._graphHandler.match(mapping.term, $rdf.sym(`${prefixes.fno}parameterMapping`)).map(p => p.object) |
| 120 | + parameterMappings.forEach(pMapping => { |
| 121 | + let parameter = this._graphHandler.match(pMapping, $rdf.sym(`${prefixes.fnom}functionParameter`)).map(p=>p.object); |
| 122 | + if (parameter.length === 0) { |
| 123 | + console.warn(`Could not find parameter assigned to ${pMapping.value}`) |
| 124 | + return |
| 125 | + } |
| 126 | + if (parameter.length > 1) { |
| 127 | + console.warn(`More parameters for ${pMapping.value} than expected (1). Picking one at random.`) |
| 128 | + } |
| 129 | + parameter = parameter[0]; |
| 130 | + const arg = args.filter(a => parameter.value === a.term.value); |
| 131 | + if (!arg) { |
| 132 | + console.warn(`Argument for parameter ${parameter.value} not found`) |
| 133 | + return |
| 134 | + } |
| 135 | + let type = this._graphHandler.match(parameter, $rdf.sym(`${prefixes.fno}type`)); |
| 136 | + if (type.length === 0) { |
| 137 | + console.warn(`No type information for parameter ${parameter.value} found`) |
| 138 | + } |
| 139 | + if(type.length > 1) { |
| 140 | + console.warn(`More types for ${parameter.value} than expected (1). Picking one at random.`) |
| 141 | + } |
| 142 | + type = type[0] || null; |
| 143 | + if (this._graphHandler.match(pMapping, $rdf.sym(`${prefixes.rdf}type`), $rdf.sym(`${prefixes.fnom}PositionParameterMapping`)).length > 0) { |
| 144 | + const positions = this._graphHandler.match(pMapping, $rdf.sym(`${prefixes.fnom}implementationParameterPosition`)).map(p => p.object.value); |
| 145 | + positions.forEach(p => { |
| 146 | + arg.forEach(a => { |
| 147 | + addToResult(p, a.value, type); |
| 148 | + }) |
| 149 | + }) |
| 150 | + } else { |
| 151 | + throw new Error('Unsupported if not positionparametermapping') |
| 152 | + } |
| 153 | + }) |
| 154 | + |
| 155 | + return result; |
| 156 | + |
| 157 | + function addToResult(key, value, type) { |
| 158 | + if (type?.value === `${prefixes.rdf}List`) { |
| 159 | + if (!result[key]) { |
| 160 | + result[key] = []; |
| 161 | + } |
| 162 | + result[key].push(value); |
| 163 | + } else { |
| 164 | + if (!result[key]) { |
| 165 | + result[key] = value; |
| 166 | + } else { |
| 167 | + console.warn(`Multiple values found for argument ${key}. Keeping a random one.`) |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + } |
| 172 | +} |
0 commit comments