/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.protolog.tool import java.util.regex.Pattern class CommandOptions(args: Array) { companion object { const val TRANSFORM_CALLS_CMD = "transform-protolog-calls" const val GENERATE_CONFIG_CMD = "generate-viewer-config" private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD) // TODO: This is always the same. I don't think it's required private const val PROTOLOG_CLASS_PARAM = "--protolog-class" private const val PROTOLOGGROUP_CLASS_PARAM = "--loggroups-class" private const val PROTOLOGGROUP_JAR_PARAM = "--loggroups-jar" private const val VIEWER_CONFIG_PARAM = "--viewer-config" private const val OUTPUT_SOURCE_JAR_PARAM = "--output-srcjar" private const val VIEWER_CONFIG_FILE_PATH_PARAM = "--viewer-config-file-path" private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGGROUP_CLASS_PARAM, PROTOLOGGROUP_JAR_PARAM, VIEWER_CONFIG_PARAM, OUTPUT_SOURCE_JAR_PARAM, VIEWER_CONFIG_FILE_PATH_PARAM) val USAGE = """ Usage: ${Constants.NAME} [] Available commands: $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM $PROTOLOGGROUP_CLASS_PARAM $PROTOLOGGROUP_JAR_PARAM $OUTPUT_SOURCE_JAR_PARAM [] - processes java files replacing stub calls with logging code. $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM $PROTOLOGGROUP_CLASS_PARAM $PROTOLOGGROUP_JAR_PARAM $VIEWER_CONFIG_PARAM [] - creates viewer config file from given java files. """.trimIndent() private fun validateClassName(name: String): String { if (!Pattern.matches("^([a-z]+[A-Za-z0-9]*\\.)+([A-Za-z0-9$]+)$", name)) { throw InvalidCommandException("Invalid class name $name") } return name } private fun getParam(paramName: String, params: Map): String { if (!params.containsKey(paramName)) { throw InvalidCommandException("Param $paramName required") } return params.getValue(paramName) } private fun getOptionalParam(paramName: String, params: Map): String? { if (!params.containsKey(paramName)) { return null } return params.getValue(paramName) } private fun validateNotSpecified(paramName: String, params: Map): String { if (params.containsKey(paramName)) { throw InvalidCommandException("Unsupported param $paramName") } return "" } private fun validateJarName(name: String): String { if (!name.endsWith(".jar")) { throw InvalidCommandException("Jar file required, got $name instead") } return name } private fun validateSrcJarName(name: String): String { if (!name.endsWith(".srcjar")) { throw InvalidCommandException("Source jar file required, got $name instead") } return name } private fun validateViewerConfigFilePath(name: String): String { if (!name.endsWith(".pb")) { throw InvalidCommandException("Proto file (ending with .pb) required, " + "got $name instead") } return name } private fun validateOutputFilePath(name: String): String { if (!name.endsWith(".winscope")) { throw InvalidCommandException("Winscope file (ending with .winscope) required, " + "got $name instead") } return name } private fun validateConfigFileName(name: String): String { if (!name.endsWith(".json") && !name.endsWith(".pb")) { throw InvalidCommandException("Json file (ending with .json) or proto file " + "(ending with .pb) required, got $name instead") } return name } private fun validateConfigType(name: String): String { val validType = listOf("json", "proto") if (!validType.contains(name)) { throw InvalidCommandException("Unexpected config file type. " + "Expected on of [${validType.joinToString()}], but got $name") } return name } private fun validateJavaInputList(list: List): List { if (list.isEmpty()) { throw InvalidCommandException("No java source input files") } list.forEach { name -> if (!name.endsWith(".java") && !name.endsWith(".kt")) { throw InvalidCommandException("Not a java or kotlin source file $name") } } return list } private fun validateLogInputList(list: List): String { if (list.isEmpty()) { throw InvalidCommandException("No log input file") } if (list.size > 1) { throw InvalidCommandException("Only one log input file allowed") } return list[0] } } val protoLogClassNameArg: String val protoLogGroupsClassNameArg: String val protoLogGroupsJarArg: String val viewerConfigFileNameArg: String val outputSourceJarArg: String val logProtofileArg: String val viewerConfigFilePathArg: String val javaSourceArgs: List val command: String init { if (args.isEmpty()) { throw InvalidCommandException("No command specified.") } command = args[0] if (command !in commands) { throw InvalidCommandException("Unknown command.") } val params: MutableMap = mutableMapOf() val inputFiles: MutableList = mutableListOf() var idx = 1 while (idx < args.size) { if (args[idx].startsWith("--")) { if (idx + 1 >= args.size) { throw InvalidCommandException("No value for ${args[idx]}") } if (args[idx] !in parameters) { throw InvalidCommandException("Unknown parameter ${args[idx]}") } if (args[idx + 1].startsWith("--")) { throw InvalidCommandException("No value for ${args[idx]}") } if (params.containsKey(args[idx])) { throw InvalidCommandException("Duplicated parameter ${args[idx]}") } params[args[idx]] = args[idx + 1] idx += 2 } else { inputFiles.add(args[idx]) idx += 1 } } when (command) { TRANSFORM_CALLS_CMD -> { protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params)) protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, params)) protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params)) viewerConfigFileNameArg = validateNotSpecified(VIEWER_CONFIG_PARAM, params) outputSourceJarArg = validateSrcJarName(getParam(OUTPUT_SOURCE_JAR_PARAM, params)) viewerConfigFilePathArg = validateViewerConfigFilePath( getParam(VIEWER_CONFIG_FILE_PATH_PARAM, params)) javaSourceArgs = validateJavaInputList(inputFiles) logProtofileArg = "" } GENERATE_CONFIG_CMD -> { protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params)) protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, params)) protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params)) viewerConfigFileNameArg = validateConfigFileName(getParam(VIEWER_CONFIG_PARAM, params)) outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params) viewerConfigFilePathArg = validateNotSpecified(VIEWER_CONFIG_FILE_PATH_PARAM, params) javaSourceArgs = validateJavaInputList(inputFiles) logProtofileArg = "" } else -> { throw InvalidCommandException("Unknown command.") } } } }