import { DataProvider, FilterParams } from '../provider'; import { Dictionary, ClassModel, LinkType, ElementModel, LinkModel, LinkCount, Property, PropertyModel, LocalizedString, } from '../model'; const DATA_PROVIDER_PROPERTY = 'http://ontodia.org/property/DataProvider'; export interface CompositeResponse { dataSourceName: string; useInStats?: boolean; response: Type; } export function mergeClassTree(composite: CompositeResponse[]): ClassModel[] { const lists = composite.filter(r => r.response).map(({useInStats, response}) => ({useInStats, classes: classTreeToArray(response)}) ); const dictionary: Dictionary = {}; const topLevelModels: Dictionary = {}; const childrenMap: Dictionary = {}; for (const {useInStats, classes} of lists) { for (const model of classes) { const childrenIds: string[] = childrenMap[model.id] || []; model.children.map(ch => ch.id).forEach(id => { if (childrenIds.indexOf(id) === -1) { childrenIds.push(id); } }); model.children = []; if (!useInStats) { delete model.count; } if (!dictionary[model.id]) { topLevelModels[model.id] = model; dictionary[model.id] = model; childrenMap[model.id] = childrenIds; } else { topLevelModels[model.id] = mergeClassModel(dictionary[model.id], model); dictionary[model.id] = topLevelModels[model.id]; } } } const models = Object.keys(dictionary).map(key => dictionary[key]); for (const m of models) { m.children = (childrenMap[m.id] || []).map(id => { delete topLevelModels[id]; return dictionary[id]; }); } return Object.keys(topLevelModels).map(key => topLevelModels[key]); } export function mergePropertyInfo( response: CompositeResponse>[], ): Dictionary { const result: Dictionary = {}; const props = response.filter(r => r.response).map(r => r.response); for (const model of props) { const keys = Object.keys(model); for (const key of keys) { const prop = model[key]; if (!result[key]) { result[key] = prop; } else { result[key].label = mergeLabels(result[key].label, prop.label); } } } return result; } export function mergeClassInfo(response: CompositeResponse[]): ClassModel[] { const dictionaries = response.filter(r => r.response).map(r => r.response); const dictionary: Dictionary = {}; for (const models of dictionaries) { for (const model of models) { if (!dictionary[model.id]) { dictionary[model.id] = model; } else { dictionary[model.id] = mergeClassModel(dictionary[model.id], model); } } } return Object.keys(dictionary).map(key => dictionary[key]); } export function mergeLinkTypesInfo(response: CompositeResponse[]): LinkType[] { const lists = response.filter(r => r.response).map(r => r.response); const mergeLinkType = (a: LinkType, b: LinkType): LinkType => { return { id: a.id, label: mergeLabels(a.label, b.label), count: a.count + b.count, }; }; const dictionary: Dictionary = {}; for (const linkTypes of lists) { for (const linkType of linkTypes) { if (!dictionary[linkType.id]) { dictionary[linkType.id] = linkType; } else { dictionary[linkType.id] = mergeLinkType(dictionary[linkType.id], linkType); } } } return Object.keys(dictionary).map(key => dictionary[key]); } export function mergeLinkTypes(response: CompositeResponse[]): LinkType[] { return mergeLinkTypesInfo(response); } export function mergeElementInfo(response: CompositeResponse>[]): Dictionary { const mergeElementModels = (a: ElementModel, b: ElementModel): ElementModel => { const types = a.types; for (const t of b.types) { if (types.indexOf(t) === -1) { types.push(t); } } const sources: string[] = []; for (const s of a.sources) { if (sources.indexOf(s) === -1) { sources.push(s); } } for (const s of b.sources) { if (sources.indexOf(s) === -1) { sources.push(s); } } return { id: a.id, label: mergeLabels(a.label, b.label), types: types, image: a.image || b.image, properties: mergeProperties(a.properties, b.properties), sources: sources, }; }; const dictionaries = response.filter(r => r.response).map(r => r.response); const dictionary: Dictionary = {}; for (const resp of response) { if (!resp.response) { continue; } const list = Object.keys(resp.response).map(k => resp.response[k]); for (const em of list) { em.sources = [resp.dataSourceName]; em.properties[DATA_PROVIDER_PROPERTY] = { type: 'string', values: [{value: resp.dataSourceName, language: ''}], }; if (!dictionary[em.id]) { dictionary[em.id] = em; } else { dictionary[em.id] = mergeElementModels(dictionary[em.id], em); } } } return dictionary; } export function mergeProperties(a: Dictionary, b: Dictionary): Dictionary { const aLists = Object.keys(a); const bLists = Object.keys(b); const result: Dictionary = {}; function createIdForProperty(baseId: string): string { let counter = 1; while (result[baseId + '_' + counter]) { counter++; } return baseId + '_' + counter; } for (const pKey of aLists) { const prop = a[pKey]; if (!result[pKey]) { result[pKey] = prop; } else { result[createIdForProperty(pKey)] = prop; } } for (const pKey of bLists) { const prop = b[pKey]; if (!result[pKey]) { result[pKey] = prop; } else { result[createIdForProperty(pKey)] = prop; } } return result; } export function mergeLinksInfo(response: CompositeResponse[]): LinkModel[] { const lists = response.filter(r => r.response).map(r => r.response); const resultInfo: LinkModel[] = []; function compareLinksInfo(a: LinkModel, b: LinkModel): boolean { return a.sourceId === b.sourceId && a.targetId === b.targetId && a.linkTypeId === b.linkTypeId; } for (const linkInfo of lists) { for (const linkModel of linkInfo) { if (!resultInfo.some(l => compareLinksInfo(l, linkModel))) { resultInfo.push(linkModel); } } } return resultInfo; } export function mergeLinkTypesOf(response: CompositeResponse[]): LinkCount[] { const lists = response.filter(r => r.response).map(r => r.response); const dictionary: Dictionary = {}; const merge = (a: LinkCount, b: LinkCount): LinkCount => { return { id: a.id, inCount: a.inCount + b.inCount, outCount: a.outCount + b.outCount, }; }; for (const linkCount of lists) { for (const lCount of linkCount) { if (!dictionary[lCount.id]) { dictionary[lCount.id] = lCount; } else { dictionary[lCount.id] = merge(lCount, dictionary[lCount.id]); } } } return Object.keys(dictionary).map(key => dictionary[key]); } export function mergeLinkElements(response: CompositeResponse>[]): Dictionary { return mergeElementInfo(response); } export function mergeFilter(response: CompositeResponse>[]): Dictionary { return mergeElementInfo(response); } export function classTreeToArray(models: ClassModel[]): ClassModel[] { let resultArray: ClassModel[] = models; function getDescendants(model: ClassModel): ClassModel[] { let descendants = model.children || []; for (const descendant of descendants) { const nextGeneration = getDescendants(descendant); descendants = descendants.concat(nextGeneration); } return descendants; } for (const model of models) { const descendants = getDescendants(model); resultArray = resultArray.concat(descendants); } return resultArray; } export function mergeLabels( a: { values: LocalizedString[] }, b: { values: LocalizedString[] }, ): { values: LocalizedString[] } { function compareLabels(l1: LocalizedString, l2: LocalizedString): boolean { return l1.language === l2.language && l1.value === l2.value; } const mergedValuesList = a.values; for (const locStr of b.values) { if (!mergedValuesList.some(l => compareLabels(l, locStr))) { mergedValuesList.push(locStr); } } return { values: mergedValuesList, }; } export function mergeCounts(a?: number, b?: number): number | undefined { if (a === undefined && b === undefined) { return undefined; } return (a || 0) + (b || 0); } export function mergeClassModel(a: ClassModel, b: ClassModel): ClassModel { const childrenDictionary: Dictionary = {}; for (const child of a.children.concat(b.children)) { if (!childrenDictionary[child.id]) { childrenDictionary[child.id] = child; } } return { id: a.id, label: mergeLabels(a.label, b.label), count: mergeCounts(a.count, b.count), children: Object.keys(childrenDictionary).map(key => childrenDictionary[key]), }; }