1 /* 2 * Copyright (C) 2023 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 package android.tools.traces.parsers.perfetto 18 19 class Args { 20 private var child: MutableMap<String, Args>? = null 21 private var children: MutableMap<String, MutableList<Args>>? = null 22 23 private var value: Any? = null 24 getChildnull25 fun getChild(key: String): Args? = child?.get(key) 26 27 fun getChildren(key: String): List<Args>? = children?.get(key) 28 29 fun getBoolean(): Boolean { 30 require(value !== null) { "Cannot access value of a non-leaf args" } 31 return value as Boolean 32 } 33 getIntnull34 fun getInt(): Int { 35 require(value !== null) { "Cannot access value of a non-leaf args" } 36 return (value as Long).toInt() 37 } 38 getLongnull39 fun getLong(): Long { 40 require(value !== null) { "Cannot access value of a non-leaf args" } 41 return value as Long 42 } 43 getFloatnull44 fun getFloat(): Float { 45 require(value !== null) { "Cannot access value of a non-leaf args" } 46 return (value as Double).toFloat() 47 } 48 isStringnull49 fun isString(): Boolean { 50 return value is String 51 } 52 getStringnull53 fun getString(): String { 54 require(value !== null) { "Cannot access value of a non-leaf args" } 55 return value as String 56 } 57 addnull58 fun add(key: String, value: String, valueType: String) { 59 val keyTokens = key.split(".") 60 val parsedValue = parseValue(value, valueType) 61 add(keyTokens, 0, parsedValue) 62 } 63 addnull64 private fun add(keyTokens: List<String>, currentToken: Int, value: Any) { 65 if (currentToken == keyTokens.size) { 66 require(child == null && children == null) { 67 "Cannot assign a value to a non-leaf args" 68 } 69 this.value = value 70 return 71 } 72 73 require(this.value == null) { "Cannot add child/children to a leaf args" } 74 75 val keyAndIndex = tryParseKeyAndIndex(keyTokens[currentToken]) 76 if (keyAndIndex != null) { 77 addToChildrenMap(keyAndIndex, keyTokens, currentToken, value) 78 return 79 } 80 81 addToChildMap(keyTokens, currentToken, value) 82 } 83 addToChildrenMapnull84 private fun addToChildrenMap( 85 keyAndIndex: Pair<String, Int>, 86 keyTokens: List<String>, 87 currentToken: Int, 88 value: Any 89 ) { 90 val (key, index) = keyAndIndex 91 if (children == null) { 92 children = HashMap() 93 } 94 if (children!![key] == null) { 95 children!![key] = ArrayList() 96 } 97 while (index >= children!![key]!!.size) { 98 children!![key]!!.add(Args()) 99 } 100 children!![key]!![index].add(keyTokens, currentToken + 1, value) 101 } 102 addToChildMapnull103 private fun addToChildMap(keyTokens: List<String>, currentToken: Int, value: Any) { 104 val key = keyTokens[currentToken] 105 if (child == null) { 106 child = HashMap() 107 } 108 if (child!![key] != null) { 109 child!![key]!!.add(keyTokens, currentToken + 1, value) 110 } else { 111 child!![key] = Args().apply { add(keyTokens, currentToken + 1, value) } 112 } 113 } 114 parseValuenull115 private fun parseValue(value: String, valueType: String): Any { 116 val result: Any? = 117 when (valueType) { 118 "bool" -> value.toBooleanStrict() 119 "int" -> value.toLong() 120 "uint" -> value.toLong() 121 "real" -> value.toDouble() 122 "string" -> value 123 else -> null 124 } 125 require(result != null) { "Unrecognized args value type '$valueType'" } 126 return result 127 } 128 tryParseKeyAndIndexnull129 private fun tryParseKeyAndIndex(token: String): Pair<String, Int>? { 130 require(token.length > 0) { "Key token cannot have length 0" } 131 132 if (token[token.length - 1] != ']') { 133 return null 134 } 135 136 val indexOpeningBracket = token.indexOf('[') 137 require(indexOpeningBracket != -1) { 138 "Key token contains closing bracket ']' but not an opening one" 139 } 140 141 val key = token.substring(0, indexOpeningBracket) 142 val index = token.substring(indexOpeningBracket + 1, token.length - 1).toInt() 143 return Pair(key, index) 144 } 145 146 companion object { buildnull147 fun build(rows: List<Row>): Args { 148 val args = Args() 149 rows.forEach { 150 val key = it.get("key") 151 val value = it.get("value") 152 val valueType = it.get("value_type") 153 if (valueType != "null" && value != null) { 154 args.add(key as String, value as String, valueType as String) 155 } 156 } 157 return args 158 } 159 } 160 } 161