• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.hoststubgen
17 
18 import java.io.PrintWriter
19 import java.util.zip.CRC32
20 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
21 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
22 import org.apache.commons.compress.archivers.zip.ZipFile
23 
24 /**
25  * Whether to skip compression when adding processed entries back to a zip file.
26  */
27 private const val SKIP_COMPRESSION = false
28 
29 /**
30  * Name of this executable. Set it in the main method.
31  */
32 var executableName = "[command name not set]"
33 
34 /**
35  * A regex that maches whitespate.
36  */
37 val whitespaceRegex = """\s+""".toRegex()
38 
39 /**
40  * Remove the comment ('#' and following) and surrounding whitespace from a line.
41  */
normalizeTextLinenull42 fun normalizeTextLine(s: String): String {
43     // Remove # and after. (comment)
44     val pos = s.indexOf('#')
45     val uncommented = if (pos < 0) s else s.substring(0, pos)
46 
47     // Remove surrounding whitespace.
48     return uncommented.trim()
49 }
50 
51 /**
52  * Concatenate list [a] and [b] and return it. As an optimization, it returns an input
53  * [List] as-is if the other [List] is empty, so do not modify input [List]'s.
54  */
addListsnull55 fun <T> addLists(a: List<T>, b: List<T>): List<T> {
56     if (a.isEmpty()) {
57         return b
58     }
59     if (b.isEmpty()) {
60         return a
61     }
62     return a + b
63 }
64 
65 /**
66  * Add element [b] to list [a] if [b] is not null. Otherwise, just return [a].
67  * (because the method may return [a] as-is, do not modify it after passing it.)
68  */
addNonNullElementnull69 fun <T> addNonNullElement(a: List<T>, b: T?): List<T> {
70     if (b == null) {
71         return a
72     }
73     if (a.isEmpty()) {
74         return listOf(b)
75     }
76     return a + b
77 }
78 
79 
80 /**
81  * Exception for a parse error in a file
82  */
83 class ParseException : Exception, UserErrorException {
84     val hasSourceInfo: Boolean
85 
86     constructor(message: String) : super(message) {
87         hasSourceInfo = false
88     }
89 
90     constructor(message: String, file: String, line: Int) :
91             super("$message in file $file line $line") {
92         hasSourceInfo = true
93     }
94 
withSourceInfonull95     fun withSourceInfo(filename: String, lineNo: Int): ParseException {
96         if (hasSourceInfo) {
97             return this // Already has source information.
98         } else {
99             return ParseException(this.message ?: "", filename, lineNo)
100         }
101     }
102 }
103 
104 /**
105  * Escape a string for a CSV field.
106  */
csvEscapenull107 fun csvEscape(value: String): String {
108     return "\"" + value.replace("\"", "\"\"") + "\""
109 }
110 
runMainWithBoilerplatenull111 inline fun runMainWithBoilerplate(realMain: () -> Unit) {
112     var success = false
113 
114     try {
115         realMain()
116 
117         success = true
118     } catch (e: Throwable) {
119         log.e("$executableName: Error: ${e.message}")
120         if (e !is UserErrorException) {
121             e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
122         }
123     } finally {
124         log.i("$executableName finished")
125         log.flush()
126     }
127 
128     System.exit(if (success) 0 else 1 )
129 }
130 
131 /**
132  * Copy a single ZIP entry to the output.
133  */
copyZipEntrynull134 fun copyZipEntry(
135     inZip: ZipFile,
136     entry: ZipArchiveEntry,
137     out: ZipArchiveOutputStream,
138 ) {
139     inZip.getRawInputStream(entry).use { out.addRawArchiveEntry(entry, it) }
140 }
141 
142 /**
143  * Add a single ZIP entry with data.
144  */
ZipArchiveOutputStreamnull145 fun ZipArchiveOutputStream.addBytesEntry(name: String, data: ByteArray) {
146     val newEntry = ZipArchiveEntry(name)
147     if (SKIP_COMPRESSION) {
148         newEntry.method = 0
149         newEntry.size = data.size.toLong()
150         newEntry.crc = CRC32().apply { update(data) }.value
151     }
152     putArchiveEntry(newEntry)
153     write(data)
154     closeArchiveEntry()
155 }
156