Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80680 views
1
/*
2
Copyright (c) 2012, Yahoo! Inc. All rights reserved.
3
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4
*/
5
6
var path = require('path'),
7
SEP = path.sep || '/',
8
utils = require('../object-utils');
9
10
function commonArrayPrefix(first, second) {
11
var len = first.length < second.length ? first.length : second.length,
12
i,
13
ret = [];
14
for (i = 0; i < len; i += 1) {
15
if (first[i] === second[i]) {
16
ret.push(first[i]);
17
} else {
18
break;
19
}
20
}
21
return ret;
22
}
23
24
function findCommonArrayPrefix(args) {
25
if (args.length === 0) {
26
return [];
27
}
28
29
var separated = args.map(function (arg) { return arg.split(SEP); }),
30
ret = separated.pop();
31
32
if (separated.length === 0) {
33
return ret.slice(0, ret.length - 1);
34
} else {
35
return separated.reduce(commonArrayPrefix, ret);
36
}
37
}
38
39
function Node(fullName, kind, metrics) {
40
this.name = fullName;
41
this.fullName = fullName;
42
this.kind = kind;
43
this.metrics = metrics || null;
44
this.parent = null;
45
this.children = [];
46
}
47
48
Node.prototype = {
49
displayShortName: function () {
50
return this.relativeName;
51
},
52
fullPath: function () {
53
return this.fullName;
54
},
55
addChild: function (child) {
56
this.children.push(child);
57
child.parent = this;
58
},
59
toJSON: function () {
60
return {
61
name: this.name,
62
relativeName: this.relativeName,
63
fullName: this.fullName,
64
kind: this.kind,
65
metrics: this.metrics,
66
parent: this.parent === null ? null : this.parent.name,
67
children: this.children.map(function (node) { return node.toJSON(); })
68
};
69
}
70
};
71
72
function TreeSummary(summaryMap, commonPrefix) {
73
this.prefix = commonPrefix;
74
this.convertToTree(summaryMap, commonPrefix);
75
}
76
77
TreeSummary.prototype = {
78
getNode: function (shortName) {
79
return this.map[shortName];
80
},
81
convertToTree: function (summaryMap, arrayPrefix) {
82
var nodes = [],
83
rootPath = arrayPrefix.join(SEP) + SEP,
84
root = new Node(rootPath, 'dir'),
85
tmp,
86
tmpChildren,
87
seen = {},
88
filesUnderRoot = false;
89
90
seen[rootPath] = root;
91
Object.keys(summaryMap).forEach(function (key) {
92
var metrics = summaryMap[key],
93
node,
94
parentPath,
95
parent;
96
node = new Node(key, 'file', metrics);
97
seen[key] = node;
98
nodes.push(node);
99
parentPath = path.dirname(key) + SEP;
100
if (parentPath === SEP + SEP) {
101
parentPath = SEP + '__root__' + SEP;
102
}
103
parent = seen[parentPath];
104
if (!parent) {
105
parent = new Node(parentPath, 'dir');
106
root.addChild(parent);
107
seen[parentPath] = parent;
108
}
109
parent.addChild(node);
110
if (parent === root) { filesUnderRoot = true; }
111
});
112
113
if (filesUnderRoot && arrayPrefix.length > 0) {
114
arrayPrefix.pop(); //start at one level above
115
tmp = root;
116
tmpChildren = tmp.children;
117
tmp.children = [];
118
root = new Node(arrayPrefix.join(SEP) + SEP, 'dir');
119
root.addChild(tmp);
120
tmpChildren.forEach(function (child) {
121
if (child.kind === 'dir') {
122
root.addChild(child);
123
} else {
124
tmp.addChild(child);
125
}
126
});
127
}
128
this.fixupNodes(root, arrayPrefix.join(SEP) + SEP);
129
this.calculateMetrics(root);
130
this.root = root;
131
this.map = {};
132
this.indexAndSortTree(root, this.map);
133
},
134
135
fixupNodes: function (node, prefix, parent) {
136
var that = this;
137
if (node.name.indexOf(prefix) === 0) {
138
node.name = node.name.substring(prefix.length);
139
}
140
if (node.name.charAt(0) === SEP) {
141
node.name = node.name.substring(1);
142
}
143
if (parent) {
144
if (parent.name !== '__root__' + SEP) {
145
node.relativeName = node.name.substring(parent.name.length);
146
} else {
147
node.relativeName = node.name;
148
}
149
} else {
150
node.relativeName = node.name.substring(prefix.length);
151
}
152
node.children.forEach(function (child) {
153
that.fixupNodes(child, prefix, node);
154
});
155
},
156
calculateMetrics: function (entry) {
157
var that = this,
158
fileChildren;
159
if (entry.kind !== 'dir') {return; }
160
entry.children.forEach(function (child) {
161
that.calculateMetrics(child);
162
});
163
entry.metrics = utils.mergeSummaryObjects.apply(
164
null,
165
entry.children.map(function (child) { return child.metrics; })
166
);
167
// calclulate "java-style" package metrics where there is no hierarchy
168
// across packages
169
fileChildren = entry.children.filter(function (n) { return n.kind !== 'dir'; });
170
if (fileChildren.length > 0) {
171
entry.packageMetrics = utils.mergeSummaryObjects.apply(
172
null,
173
fileChildren.map(function (child) { return child.metrics; })
174
);
175
} else {
176
entry.packageMetrics = null;
177
}
178
},
179
indexAndSortTree: function (node, map) {
180
var that = this;
181
map[node.name] = node;
182
node.children.sort(function (a, b) {
183
a = a.relativeName;
184
b = b.relativeName;
185
return a < b ? -1 : a > b ? 1 : 0;
186
});
187
node.children.forEach(function (child) {
188
that.indexAndSortTree(child, map);
189
});
190
},
191
toJSON: function () {
192
return {
193
prefix: this.prefix,
194
root: this.root.toJSON()
195
};
196
}
197
};
198
199
function TreeSummarizer() {
200
this.summaryMap = {};
201
}
202
203
TreeSummarizer.prototype = {
204
addFileCoverageSummary: function (filePath, metrics) {
205
this.summaryMap[filePath] = metrics;
206
},
207
getTreeSummary: function () {
208
var commonArrayPrefix = findCommonArrayPrefix(Object.keys(this.summaryMap));
209
return new TreeSummary(this.summaryMap, commonArrayPrefix);
210
}
211
};
212
213
module.exports = TreeSummarizer;
214
215