/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), SEP = path.sep || '/', utils = require('../object-utils'); function commonArrayPrefix(first, second) { var len = first.length < second.length ? first.length : second.length, i, ret = []; for (i = 0; i < len; i += 1) { if (first[i] === second[i]) { ret.push(first[i]); } else { break; } } return ret; } function findCommonArrayPrefix(args) { if (args.length === 0) { return []; } var separated = args.map(function (arg) { return arg.split(SEP); }), ret = separated.pop(); if (separated.length === 0) { return ret.slice(0, ret.length - 1); } else { return separated.reduce(commonArrayPrefix, ret); } } function Node(fullName, kind, metrics) { this.name = fullName; this.fullName = fullName; this.kind = kind; this.metrics = metrics || null; this.parent = null; this.children = []; } Node.prototype = { displayShortName: function () { return this.relativeName; }, fullPath: function () { return this.fullName; }, addChild: function (child) { this.children.push(child); child.parent = this; }, toJSON: function () { return { name: this.name, relativeName: this.relativeName, fullName: this.fullName, kind: this.kind, metrics: this.metrics, parent: this.parent === null ? null : this.parent.name, children: this.children.map(function (node) { return node.toJSON(); }) }; } }; function TreeSummary(summaryMap, commonPrefix) { this.prefix = commonPrefix; this.convertToTree(summaryMap, commonPrefix); } TreeSummary.prototype = { getNode: function (shortName) { return this.map[shortName]; }, convertToTree: function (summaryMap, arrayPrefix) { var nodes = [], rootPath = arrayPrefix.join(SEP) + SEP, root = new Node(rootPath, 'dir'), tmp, tmpChildren, seen = {}, filesUnderRoot = false; seen[rootPath] = root; Object.keys(summaryMap).forEach(function (key) { var metrics = summaryMap[key], node, parentPath, parent; node = new Node(key, 'file', metrics); seen[key] = node; nodes.push(node); parentPath = path.dirname(key) + SEP; if (parentPath === SEP + SEP || parentPath === '.' + SEP) { parentPath = SEP + '__root__' + SEP; } parent = seen[parentPath]; if (!parent) { parent = new Node(parentPath, 'dir'); root.addChild(parent); seen[parentPath] = parent; } parent.addChild(node); if (parent === root) { filesUnderRoot = true; } }); if (filesUnderRoot && arrayPrefix.length > 0) { arrayPrefix.pop(); //start at one level above tmp = root; tmpChildren = tmp.children; tmp.children = []; root = new Node(arrayPrefix.join(SEP) + SEP, 'dir'); root.addChild(tmp); tmpChildren.forEach(function (child) { if (child.kind === 'dir') { root.addChild(child); } else { tmp.addChild(child); } }); } this.fixupNodes(root, arrayPrefix.join(SEP) + SEP); this.calculateMetrics(root); this.root = root; this.map = {}; this.indexAndSortTree(root, this.map); }, fixupNodes: function (node, prefix, parent) { var that = this; if (node.name.indexOf(prefix) === 0) { node.name = node.name.substring(prefix.length); } if (node.name.charAt(0) === SEP) { node.name = node.name.substring(1); } if (parent) { if (parent.name !== '__root__' + SEP) { node.relativeName = node.name.substring(parent.name.length); } else { node.relativeName = node.name; } } else { node.relativeName = node.name.substring(prefix.length); } node.children.forEach(function (child) { that.fixupNodes(child, prefix, node); }); }, calculateMetrics: function (entry) { var that = this, fileChildren; if (entry.kind !== 'dir') {return; } entry.children.forEach(function (child) { that.calculateMetrics(child); }); entry.metrics = utils.mergeSummaryObjects.apply( null, entry.children.map(function (child) { return child.metrics; }) ); // calclulate "java-style" package metrics where there is no hierarchy // across packages fileChildren = entry.children.filter(function (n) { return n.kind !== 'dir'; }); if (fileChildren.length > 0) { entry.packageMetrics = utils.mergeSummaryObjects.apply( null, fileChildren.map(function (child) { return child.metrics; }) ); } else { entry.packageMetrics = null; } }, indexAndSortTree: function (node, map) { var that = this; map[node.name] = node; node.children.sort(function (a, b) { a = a.relativeName; b = b.relativeName; return a < b ? -1 : a > b ? 1 : 0; }); node.children.forEach(function (child) { that.indexAndSortTree(child, map); }); }, toJSON: function () { return { prefix: this.prefix, root: this.root.toJSON() }; } }; function TreeSummarizer() { this.summaryMap = {}; } TreeSummarizer.prototype = { addFileCoverageSummary: function (filePath, metrics) { this.summaryMap[filePath] = metrics; }, getTreeSummary: function () { var commonArrayPrefix = findCommonArrayPrefix(Object.keys(this.summaryMap)); return new TreeSummary(this.summaryMap, commonArrayPrefix); } }; module.exports = TreeSummarizer;