diff --git a/schema/out/ts/ifcx.d.ts b/schema/out/ts/ifcx.d.ts index 745fa2b..7aa9bda 100644 --- a/schema/out/ts/ifcx.d.ts +++ b/schema/out/ts/ifcx.d.ts @@ -24,6 +24,8 @@ export interface components { [key: string]: components["schemas"]["IfcxSchema"]; }; data: components["schemas"]["IfcxNode"][]; + // hack hack hack + src?: string; }; IfcxHeader: { id: string; diff --git a/src/ifcx-core/layers/layer-stack.ts b/src/ifcx-core/layers/layer-stack.ts index 7a2b78c..f1afb44 100644 --- a/src/ifcx-core/layers/layer-stack.ts +++ b/src/ifcx-core/layers/layer-stack.ts @@ -88,13 +88,15 @@ export class IfcxLayerStackBuilder private async SatisfyDependencies(activeLayer: IfcxFile, placed: Map, orderedLayers: IfcxFile[]) { let pending: IfcxFile[] = []; - for (const impt of activeLayer.imports) { + for (const impt of activeLayer.imports || []) { if (!placed.has(impt.uri)) { let layer = await this.provider.GetLayerByURI(impt.uri); if (layer instanceof Error) { return layer; + } else { + layer.src = impt.uri; } pending.push(layer); placed.set(impt.uri, true); diff --git a/src/ifcx-core/workflows.ts b/src/ifcx-core/workflows.ts index 23d56ad..185a90e 100644 --- a/src/ifcx-core/workflows.ts +++ b/src/ifcx-core/workflows.ts @@ -1,7 +1,7 @@ import { FlattenCompositionInput, CreateArtificialRoot, ExpandFirstRootInInput } from "./composition/compose"; import { CompositionInputNode } from "./composition/node"; import { IfcxFile, IfcxNode, ImportNode } from "./schema/schema-helper"; -import { Validate } from "./schema/schema-validation"; +import { SchemaValidationError, Validate } from "./schema/schema-validation"; import { MMSet } from "./util/mm"; function ToInputNodes(data: IfcxNode[]) @@ -151,7 +151,7 @@ export function Diff(file1: IfcxFile, file2: IfcxFile) return result; } -export function Federate(files: IfcxFile[]) +export function Federate(files: IfcxFile[], acceptDuplicateSchemas: boolean = false) { if (files.length === 0) { @@ -165,6 +165,26 @@ export function Federate(files: IfcxFile[]) data: [] }; + if (!acceptDuplicateSchemas) { + files.forEach((file) => { + // @todo not very efficient in case of deep import trees + const localSchemas = new Set(Object.keys(file.schemas)); + const importedSchemas : Set = new Set(); + const recurse = (uri : string) => { + files.filter(f => f.src == uri).forEach(f => { + Object.keys(f.schemas).forEach(s => importedSchemas.add(s)); + f.imports.map(i => i.uri).forEach(recurse); + }); + }; + file.imports.map(i => i.uri).forEach(recurse); + for (let s of localSchemas) { + if (importedSchemas.has(s)) { + throw new SchemaValidationError(`Imported schema '${s}' also defined in base layer`); + } + } + }) + } + files.forEach((file) => { Object.keys(file.schemas).forEach((schemaID) => result.schemas[schemaID] = file.schemas[schemaID]); })