in lib/object-context.ts [429:509]
private analyzeNamedObjectDependencies(): void {
// First pass to analyze direct dependencies, do type check and protocol check.
for (let def of this._objectDefs) {
def.dependencies = new ObjectContextDependency();
ScopedObjectContextDef.analyzeDirectDependencies(def.dependencies, def.value);
// Do type check.
let typeDeps = def.dependencies.typeDependencies;
typeDeps.forEach(typeDep => {
if (this.getTypeDef(typeDep) == null) {
throw new Error("Unrecoginized type '" + typeDep + "' found in named object '" + def.name + "'.");
}
});
// Do URI provider check.
let protocolDeps = def.dependencies.protocolDependencies;
protocolDeps.forEach(protocolDep => {
if (this.getProviderDef(protocolDep) == null) {
throw new Error("Unrecongized URI protocol '" + protocolDep + "' found in named object '" + def.name + "'.");
}
});
}
// Second pass to get closure from redirect/indirect dependencies.
// Key: object name. Value: Closure of dependent object names.
let resolved: Map<string, Set<string>> = new Map<string, Set<string>>();
let toResolve: { unresolvedDeps: Set<string>, def: NamedObjectDef }[] = [];
// Create dependencies to resolve.
for (let def of this._objectDefs) {
let objectDeps = def.dependencies.objectDependencies;
if (objectDeps.size != 0) {
let unresolvedDeps = new Set<string>();
objectDeps.forEach(depName => {
unresolvedDeps.add(depName);
});
toResolve.push({
unresolvedDeps: unresolvedDeps,
def: def
});
}
else {
resolved.set(def.name, objectDeps);
}
}
// Multi-round resolution.
while (toResolve.length != 0) {
let remaining: { unresolvedDeps: Set<string>, def: NamedObjectDef }[] = [];
let resolvedThisRound = 0;
// One round to resolved each unresolved.
for (let record of toResolve) {
let unresolvedDeps = Object.keys(record.unresolvedDeps);
// For each direct dependency, add their resolved dependency closure to current one.
for (let dep of unresolvedDeps) {
let depClosure = resolved.get(dep);
if (depClosure != null) {
record.unresolvedDeps.delete(dep);
depClosure.forEach(depName => {
record.def.dependencies.setObjectDependency(depName);
});
}
}
// All unresolved dependencies are already resolved.
if (unresolvedDeps.length == 0) {
resolved.set(record.def.name, record.def.dependencies.objectDependencies);
++resolvedThisRound;
}
else {
remaining.push(record);
}
}
if (resolvedThisRound == 0) {
throw new Error("Undefined named object or cyclic dependencies found: '"
+ toResolve.map(obj => { return obj.def.name; }).join(","))
+ "'.";
}
toResolve = remaining;
}
}