private analyzeNamedObjectDependencies()

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;
        }
    }