MapComplete/src/Models/ThemeConfig/LayerConfigDependencyGraph.ts

91 lines
3.1 KiB
TypeScript

import { LayerConfigJson } from "./Json/LayerConfigJson"
export interface LevelInfo {
ids: string[],
loop?: boolean
}
export class LayerConfigDependencyGraph {
/**
* Calculates the dependencies for the given layer
* @param layerconfig
*/
public static getLayerImports(layerconfig: LayerConfigJson): string[] {
const defaultImports: ReadonlyArray<string> = ["questions", "filters","icons"]
if (defaultImports.indexOf(layerconfig.id) >= 0) {
return []
}
const importedTrs: string[] = []
for (const tr of layerconfig.tagRenderings ?? []) {
if (typeof tr === "string") {
importedTrs.push(tr)
} else if (tr["builtin"] !== undefined) {
const builtin = <string | string[]>tr["builtin"]
if (typeof builtin === "string") {
importedTrs.push(builtin)
} else {
importedTrs.push(...builtin)
}
}
}
const imports = new Set<string>(defaultImports)
for (const importValue of importedTrs) {
if (importValue.indexOf(".") < 0) {
continue
}
const [layer, _] = importValue.split(".")
imports.add(layer)
}
return Array.from(imports)
}
public static buildDirectDependencies(layers: LayerConfigJson[]) {
const dependsOn = new Map<string, string[]>()
for (const layer of layers) {
const layerDependsOn = LayerConfigDependencyGraph.getLayerImports(layer)
dependsOn.set(layer.id, layerDependsOn)
}
return dependsOn
}
public static buildLevels(dependsOn: Map<string, string[]>): LevelInfo[]{
const levels: LevelInfo[] = []
const seenIds = new Set<string>()
while (Array.from(dependsOn.keys()).length > 0) {
const currentLevel: LevelInfo = {
ids: <string[]>[],
}
levels.push(currentLevel)
for (const layerId of dependsOn.keys()) {
const dependencies = dependsOn.get(layerId)
if (dependencies.length === 0) {
currentLevel.ids.push(layerId)
seenIds.add(layerId)
}
}
const newDependsOn = new Map<string, string[]>()
for (const layerId of dependsOn.keys()) {
if (seenIds.has(layerId)) {
continue
}
const dependencies = dependsOn.get(layerId)
newDependsOn.set(layerId, dependencies.filter(d => !seenIds.has(d)))
}
const oldSize = dependsOn.size
if(oldSize === newDependsOn.size){
// We detected a loop.
currentLevel.loop = true
const allLayers =Array.from(newDependsOn.keys())
currentLevel.ids.push(...allLayers )
allLayers.forEach(l => seenIds.add(l))
}
dependsOn = newDependsOn
}
return levels
}
}