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