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