1 /* <lambda>null2 * Copyright (C) 2019 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 com.android.protolog.tool 18 19 import java.util.regex.Pattern 20 21 class CommandOptions(args: Array<String>) { 22 companion object { 23 const val TRANSFORM_CALLS_CMD = "transform-protolog-calls" 24 const val GENERATE_CONFIG_CMD = "generate-viewer-config" 25 const val READ_LOG_CMD = "read-log" 26 private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD, READ_LOG_CMD) 27 28 private const val PROTOLOG_CLASS_PARAM = "--protolog-class" 29 private const val PROTOLOGIMPL_CLASS_PARAM = "--protolog-impl-class" 30 private const val PROTOLOGCACHE_CLASS_PARAM = "--protolog-cache-class" 31 private const val PROTOLOGGROUP_CLASS_PARAM = "--loggroups-class" 32 private const val PROTOLOGGROUP_JAR_PARAM = "--loggroups-jar" 33 private const val VIEWER_CONFIG_JSON_PARAM = "--viewer-conf" 34 private const val OUTPUT_SOURCE_JAR_PARAM = "--output-srcjar" 35 private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGIMPL_CLASS_PARAM, 36 PROTOLOGCACHE_CLASS_PARAM, PROTOLOGGROUP_CLASS_PARAM, PROTOLOGGROUP_JAR_PARAM, 37 VIEWER_CONFIG_JSON_PARAM, OUTPUT_SOURCE_JAR_PARAM) 38 39 val USAGE = """ 40 Usage: ${Constants.NAME} <command> [<args>] 41 Available commands: 42 43 $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGIMPL_CLASS_PARAM 44 <class name> $PROTOLOGCACHE_CLASS_PARAM 45 <class name> $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM 46 <config.jar> $OUTPUT_SOURCE_JAR_PARAM <output.srcjar> [<input.java>] 47 - processes java files replacing stub calls with logging code. 48 49 $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGGROUP_CLASS_PARAM 50 <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar> $VIEWER_CONFIG_JSON_PARAM 51 <viewer.json> [<input.java>] 52 - creates viewer config file from given java files. 53 54 $READ_LOG_CMD $VIEWER_CONFIG_JSON_PARAM <viewer.json> <wm_log.pb> 55 - translates a binary log to a readable format. 56 """.trimIndent() 57 58 private fun validateClassName(name: String): String { 59 if (!Pattern.matches("^([a-z]+[A-Za-z0-9]*\\.)+([A-Za-z0-9$]+)$", name)) { 60 throw InvalidCommandException("Invalid class name $name") 61 } 62 return name 63 } 64 65 private fun getParam(paramName: String, params: Map<String, String>): String { 66 if (!params.containsKey(paramName)) { 67 throw InvalidCommandException("Param $paramName required") 68 } 69 return params.getValue(paramName) 70 } 71 72 private fun validateNotSpecified(paramName: String, params: Map<String, String>): String { 73 if (params.containsKey(paramName)) { 74 throw InvalidCommandException("Unsupported param $paramName") 75 } 76 return "" 77 } 78 79 private fun validateJarName(name: String): String { 80 if (!name.endsWith(".jar")) { 81 throw InvalidCommandException("Jar file required, got $name instead") 82 } 83 return name 84 } 85 86 private fun validateSrcJarName(name: String): String { 87 if (!name.endsWith(".srcjar")) { 88 throw InvalidCommandException("Source jar file required, got $name instead") 89 } 90 return name 91 } 92 93 private fun validateJSONName(name: String): String { 94 if (!name.endsWith(".json")) { 95 throw InvalidCommandException("Json file required, got $name instead") 96 } 97 return name 98 } 99 100 private fun validateJavaInputList(list: List<String>): List<String> { 101 if (list.isEmpty()) { 102 throw InvalidCommandException("No java source input files") 103 } 104 list.forEach { name -> 105 if (!name.endsWith(".java")) { 106 throw InvalidCommandException("Not a java source file $name") 107 } 108 } 109 return list 110 } 111 112 private fun validateLogInputList(list: List<String>): String { 113 if (list.isEmpty()) { 114 throw InvalidCommandException("No log input file") 115 } 116 if (list.size > 1) { 117 throw InvalidCommandException("Only one log input file allowed") 118 } 119 return list[0] 120 } 121 } 122 123 val protoLogClassNameArg: String 124 val protoLogGroupsClassNameArg: String 125 val protoLogImplClassNameArg: String 126 val protoLogCacheClassNameArg: String 127 val protoLogGroupsJarArg: String 128 val viewerConfigJsonArg: String 129 val outputSourceJarArg: String 130 val logProtofileArg: String 131 val javaSourceArgs: List<String> 132 val command: String 133 134 init { 135 if (args.isEmpty()) { 136 throw InvalidCommandException("No command specified.") 137 } 138 command = args[0] 139 if (command !in commands) { 140 throw InvalidCommandException("Unknown command.") 141 } 142 143 val params: MutableMap<String, String> = mutableMapOf() 144 val inputFiles: MutableList<String> = mutableListOf() 145 146 var idx = 1 147 while (idx < args.size) { 148 if (args[idx].startsWith("--")) { 149 if (idx + 1 >= args.size) { 150 throw InvalidCommandException("No value for ${args[idx]}") 151 } 152 if (args[idx] !in parameters) { 153 throw InvalidCommandException("Unknown parameter ${args[idx]}") 154 } 155 if (args[idx + 1].startsWith("--")) { 156 throw InvalidCommandException("No value for ${args[idx]}") 157 } 158 if (params.containsKey(args[idx])) { 159 throw InvalidCommandException("Duplicated parameter ${args[idx]}") 160 } 161 params[args[idx]] = args[idx + 1] 162 idx += 2 163 } else { 164 inputFiles.add(args[idx]) 165 idx += 1 166 } 167 } 168 169 when (command) { 170 TRANSFORM_CALLS_CMD -> { 171 protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params)) 172 protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, 173 params)) 174 protoLogImplClassNameArg = validateClassName(getParam(PROTOLOGIMPL_CLASS_PARAM, 175 params)) 176 protoLogCacheClassNameArg = validateClassName(getParam(PROTOLOGCACHE_CLASS_PARAM, 177 params)) 178 protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params)) 179 viewerConfigJsonArg = validateNotSpecified(VIEWER_CONFIG_JSON_PARAM, params) 180 outputSourceJarArg = validateSrcJarName(getParam(OUTPUT_SOURCE_JAR_PARAM, params)) 181 javaSourceArgs = validateJavaInputList(inputFiles) 182 logProtofileArg = "" 183 } 184 GENERATE_CONFIG_CMD -> { 185 protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params)) 186 protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, 187 params)) 188 protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params) 189 protoLogCacheClassNameArg = validateNotSpecified(PROTOLOGCACHE_CLASS_PARAM, params) 190 protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params)) 191 viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params)) 192 outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params) 193 javaSourceArgs = validateJavaInputList(inputFiles) 194 logProtofileArg = "" 195 } 196 READ_LOG_CMD -> { 197 protoLogClassNameArg = validateNotSpecified(PROTOLOG_CLASS_PARAM, params) 198 protoLogGroupsClassNameArg = validateNotSpecified(PROTOLOGGROUP_CLASS_PARAM, params) 199 protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params) 200 protoLogCacheClassNameArg = validateNotSpecified(PROTOLOGCACHE_CLASS_PARAM, params) 201 protoLogGroupsJarArg = validateNotSpecified(PROTOLOGGROUP_JAR_PARAM, params) 202 viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params)) 203 outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params) 204 javaSourceArgs = listOf() 205 logProtofileArg = validateLogInputList(inputFiles) 206 } 207 else -> { 208 throw InvalidCommandException("Unknown command.") 209 } 210 } 211 } 212 } 213