1/*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// kind - a type used for categorization of different levels
18// name - name of the node
19// children - list of child entries. Each child entry is pair list [raw object, nested transform function].
20// bounds - used to calculate the full bounds of parents
21// stableId - unique id for an entry. Used to maintain selection across frames.
22function transform({obj, kind, name, children, timestamp, rect, bounds, highlight, rects_transform, chips, visible, flattened, stableId}) {
23	function call(fn, arg) {
24		return (typeof fn == 'function') ? fn(arg) : fn;
25	}
26	function handle_children(arg, transform) {
27		return [].concat(...arg.map((item) => {
28			var childrenFunc = item[0];
29			var transformFunc = item[1];
30			var childs = call(childrenFunc, obj);
31			if (childs) {
32				if (typeof childs.map != 'function'){
33					throw 'Childs should be an array, but is: ' + (typeof childs) + '.'
34				}
35				return transform ? childs.map(transformFunc) : childs;
36			} else {
37				return [];
38			}
39		}));
40	}
41	function concat(arg, args, argsmap) {
42		var validArg = arg !== undefined && arg !== null;
43
44		if (Array.isArray(args)) {
45			if (validArg) {
46				return [arg].concat(...args.map(argsmap));
47			} else {
48				return [].concat(...args.map(argsmap));
49			}
50		} else if (validArg) {
51			return [arg];
52		} else {
53			return undefined;
54		}
55	}
56
57	var transformed_children = handle_children(children, true /* transform */);
58	rects_transform = (rects_transform === undefined) ? (e) => e : rects_transform;
59
60	var kindResolved = call(kind, obj);
61	var nameResolved = call(name, obj);
62	var rectResolved = call(rect, obj);
63	var stableIdResolved = (stableId === undefined) ?
64			kindResolved + '|-|' + nameResolved :
65			call(stableId, obj);
66
67	var result = {
68		kind: kindResolved,
69		name: nameResolved,
70		children: transformed_children,
71		obj: obj,
72		timestamp: call(timestamp, obj),
73		skip: handle_children(children, false /* transform */),
74		bounds: call(bounds, obj) || transformed_children.map((e) => e.bounds).find((e) => true) || undefined,
75		rect: rectResolved,
76		rects: rects_transform(concat(rectResolved, transformed_children, (e) => e.rects)),
77		highlight: call(highlight, obj),
78		chips: call(chips, obj),
79		stableId: stableIdResolved,
80		visible: call(visible, obj),
81		childrenVisible: transformed_children.some((c) => {
82				return c.childrenVisible || c.visible
83			}),
84		flattened: call(flattened, obj),
85	};
86
87	if (rectResolved) {
88		rectResolved.ref = result;
89	}
90
91	return Object.freeze(result);
92}
93
94
95function transform_json(obj, name, options) {
96	let {skip, formatter} = options;
97
98	var children = [];
99	var formatted = undefined;
100
101	if (skip && skip.includes(obj)) {
102		// skip
103	} else if ((formatted = formatter(obj))) {
104		children.push(transform_json(null, formatted, options));
105	} else if (Array.isArray(obj)) {
106		obj.forEach((e, i) => {
107			children.push(transform_json(e, ""+i, options));
108		})
109	} else if (typeof obj == 'string') {
110		children.push(transform_json(null, obj, options));
111	} else if (typeof obj == 'number' || typeof obj == 'boolean') {
112		children.push(transform_json(null, ""+obj, options));
113	} else if (obj && typeof obj == 'object') {
114		Object.keys(obj).forEach((key) => {
115			children.push(transform_json(obj[key], key, options));
116		});
117	}
118
119	if (children.length == 1 && !children[0].combined) {
120		return Object.freeze({
121			kind: "",
122			name: name + ": " + children[0].name,
123			children: children[0].children,
124			combined: true
125		});
126	}
127
128	return Object.freeze({
129		kind: "",
130		name: name,
131		children: children,
132	});
133}
134
135function nanos_to_string(elapsedRealtimeNanos) {
136	var units = [
137		[1000000, '(ns)'],
138		[1000, 'ms'],
139		[60, 's'],
140		[60, 'm'],
141		[24, 'h'],
142		[Infinity, 'd'],
143	];
144
145	var parts = []
146	units.some(([div, str], i) => {
147		var part = (elapsedRealtimeNanos % div).toFixed()
148		if (!str.startsWith('(')) {
149			parts.push(part + str);
150		}
151		elapsedRealtimeNanos = Math.floor(elapsedRealtimeNanos / div);
152		return elapsedRealtimeNanos == 0;
153	});
154
155	return parts.reverse().join('');
156}
157
158 // Returns a UI element used highlight a visible entry.
159 function get_visible_chip() {
160	return {short: 'V', long: "visible", class: 'default'};
161 }
162
163export {transform, transform_json, nanos_to_string, get_visible_chip};
164