blob: 3ec6fd45c6e33d2379446ca76c08336bf53d2ac9 [file] [log] [blame]
Inseob Kim5f8f32c2018-08-24 11:10:44 +09001/*
2 * Copyright (C) 2018 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#define LOG_TAG "sysprop_java_gen"
18
19#include "JavaGen.h"
20
21#include <android-base/file.h>
22#include <android-base/logging.h>
23#include <android-base/stringprintf.h>
24#include <android-base/strings.h>
25#include <cerrno>
26#include <regex>
27#include <string>
28
29#include "CodeWriter.h"
30#include "Common.h"
31#include "sysprop.pb.h"
32
33namespace {
34
35constexpr const char* kIndent = " ";
36
37constexpr const char* kJavaFileImports =
38 R"(import android.annotation.SystemApi;
39
Inseob Kim0773b942018-10-04 19:29:27 +090040import android.os.SystemProperties;
Inseob Kim5f8f32c2018-08-24 11:10:44 +090041import java.util.ArrayList;
42import java.util.function.Function;
43import java.util.List;
44import java.util.Optional;
45import java.util.StringJoiner;
46
47)";
48
49constexpr const char* kJavaParsersAndFormatters =
50 R"(private static Boolean tryParseBoolean(String str) {
51 switch (str.toLowerCase()) {
52 case "1":
Inseob Kim5f8f32c2018-08-24 11:10:44 +090053 case "true":
54 return Boolean.TRUE;
55 case "0":
Inseob Kim5f8f32c2018-08-24 11:10:44 +090056 case "false":
57 return Boolean.FALSE;
58 default:
59 return null;
60 }
61}
62
63private static Integer tryParseInteger(String str) {
64 try {
65 return Integer.valueOf(str);
66 } catch (NumberFormatException e) {
67 return null;
68 }
69}
70
71private static Long tryParseLong(String str) {
72 try {
73 return Long.valueOf(str);
74 } catch (NumberFormatException e) {
75 return null;
76 }
77}
78
79private static Double tryParseDouble(String str) {
80 try {
81 return Double.valueOf(str);
82 } catch (NumberFormatException e) {
83 return null;
84 }
85}
86
87private static String tryParseString(String str) {
Inseob Kim14e51872018-10-25 14:27:33 +090088 return str.length() == 0 ? null : str;
Inseob Kim5f8f32c2018-08-24 11:10:44 +090089}
90
91private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) {
92 try {
93 return Enum.valueOf(enumType, str);
94 } catch (IllegalArgumentException e) {
95 return null;
96 }
97}
98
99private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) {
100 List<T> ret = new ArrayList<>();
101
102 for (String element : str.split(",")) {
103 T parsed = elementParser.apply(element);
104 if (parsed == null) {
105 return null;
106 }
107 ret.add(parsed);
108 }
109
110 return ret;
111}
112
113private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) {
114 List<T> ret = new ArrayList<>();
115
116 for (String element : str.split(",")) {
117 T parsed = tryParseEnum(enumType, element);
118 if (parsed == null) {
119 return null;
120 }
121 ret.add(parsed);
122 }
123
124 return ret;
125}
126
127private static <T> String formatList(List<T> list) {
128 StringJoiner joiner = new StringJoiner(",");
129
130 for (T element : list) {
131 joiner.add(element.toString());
132 }
133
134 return joiner.toString();
135}
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900136)";
137
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900138const std::regex kRegexDot{"\\."};
139const std::regex kRegexUnderscore{"_"};
140
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900141std::string GetJavaTypeName(const sysprop::Property& prop);
142std::string GetJavaEnumTypeName(const sysprop::Property& prop);
143std::string GetJavaPackageName(const sysprop::Properties& props);
144std::string GetJavaClassName(const sysprop::Properties& props);
145bool IsListProp(const sysprop::Property& prop);
Inseob Kim14e51872018-10-25 14:27:33 +0900146void WriteJavaAnnotation(CodeWriter& writer, sysprop::Scope scope);
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900147bool GenerateJavaClass(const sysprop::Properties& props,
148 std::string* java_result, std::string* err);
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900149
150std::string GetJavaEnumTypeName(const sysprop::Property& prop) {
Inseob Kim14e51872018-10-25 14:27:33 +0900151 return ApiNameToIdentifier(prop.api_name()) + "_values";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900152}
153
154std::string GetJavaTypeName(const sysprop::Property& prop) {
155 switch (prop.type()) {
156 case sysprop::Boolean:
157 return "Boolean";
158 case sysprop::Integer:
159 return "Integer";
160 case sysprop::Long:
161 return "Long";
162 case sysprop::Double:
163 return "Double";
164 case sysprop::String:
165 return "String";
166 case sysprop::Enum:
167 return GetJavaEnumTypeName(prop);
168 case sysprop::BooleanList:
169 return "List<Boolean>";
170 case sysprop::IntegerList:
171 return "List<Integer>";
172 case sysprop::LongList:
173 return "List<Long>";
174 case sysprop::DoubleList:
175 return "List<Double>";
176 case sysprop::StringList:
177 return "List<String>";
178 case sysprop::EnumList:
179 return "List<" + GetJavaEnumTypeName(prop) + ">";
180 default:
181 __builtin_unreachable();
182 }
183}
184
185std::string GetParsingExpression(const sysprop::Property& prop) {
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900186 switch (prop.type()) {
187 case sysprop::Boolean:
Inseob Kim0773b942018-10-04 19:29:27 +0900188 return "tryParseBoolean(value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900189 case sysprop::Integer:
Inseob Kim0773b942018-10-04 19:29:27 +0900190 return "tryParseInteger(value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900191 case sysprop::Long:
Inseob Kim0773b942018-10-04 19:29:27 +0900192 return "tryParseLong(value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900193 case sysprop::Double:
Inseob Kim0773b942018-10-04 19:29:27 +0900194 return "tryParseDouble(value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900195 case sysprop::String:
Inseob Kim0773b942018-10-04 19:29:27 +0900196 return "tryParseString(value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900197 case sysprop::Enum:
Inseob Kim0773b942018-10-04 19:29:27 +0900198 return "tryParseEnum(" + GetJavaEnumTypeName(prop) + ".class, value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900199 case sysprop::EnumList:
Inseob Kim0773b942018-10-04 19:29:27 +0900200 return "tryParseEnumList(" + GetJavaEnumTypeName(prop) +
201 ".class, "
202 "value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900203 default:
204 break;
205 }
206
207 // The remaining cases are lists for types other than Enum which share the
208 // same parsing function "tryParseList"
209 std::string element_parser;
210
211 switch (prop.type()) {
212 case sysprop::BooleanList:
213 element_parser = "v -> tryParseBoolean(v)";
214 break;
215 case sysprop::IntegerList:
216 element_parser = "v -> tryParseInteger(v)";
217 break;
218 case sysprop::LongList:
219 element_parser = "v -> tryParseLong(v)";
220 break;
221 case sysprop::DoubleList:
222 element_parser = "v -> tryParseDouble(v)";
223 break;
224 case sysprop::StringList:
225 element_parser = "v -> tryParseString(v)";
226 break;
227 default:
228 __builtin_unreachable();
229 }
230
Inseob Kim0773b942018-10-04 19:29:27 +0900231 return "tryParseList(" + element_parser + ", value)";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900232}
233
234std::string GetJavaPackageName(const sysprop::Properties& props) {
235 const std::string& module = props.module();
236 return module.substr(0, module.rfind('.'));
237}
238
239std::string GetJavaClassName(const sysprop::Properties& props) {
240 const std::string& module = props.module();
241 return module.substr(module.rfind('.') + 1);
242}
243
244bool IsListProp(const sysprop::Property& prop) {
245 switch (prop.type()) {
246 case sysprop::BooleanList:
247 case sysprop::IntegerList:
248 case sysprop::LongList:
249 case sysprop::DoubleList:
250 case sysprop::StringList:
251 case sysprop::EnumList:
252 return true;
253 default:
254 return false;
255 }
256}
257
Inseob Kim14e51872018-10-25 14:27:33 +0900258void WriteJavaAnnotation(CodeWriter& writer, sysprop::Scope scope) {
259 switch (scope) {
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900260 case sysprop::System:
261 writer.Write("@SystemApi\n");
262 break;
263 case sysprop::Internal:
264 writer.Write("/** @hide */\n");
265 break;
266 default:
267 break;
268 }
269}
270
271bool GenerateJavaClass(const sysprop::Properties& props,
272 std::string* java_result,
273 [[maybe_unused]] std::string* err) {
Inseob Kim1a58c042018-11-01 16:37:34 +0900274 sysprop::Scope classScope = sysprop::Internal;
275
276 for (int i = 0; i < props.prop_size(); ++i) {
277 // Get least restrictive scope among props. For example, if all props
278 // are internal, class can be as well internal. However, class should
279 // be public or system if at least one prop is so.
280 classScope = std::min(classScope, props.prop(i).scope());
281 }
282
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900283 std::string package_name = GetJavaPackageName(props);
284 std::string class_name = GetJavaClassName(props);
285
286 CodeWriter writer(kIndent);
287 writer.Write("%s", kGeneratedFileFooterComments);
288 writer.Write("package %s;\n\n", package_name.c_str());
289 writer.Write("%s", kJavaFileImports);
Inseob Kim1a58c042018-11-01 16:37:34 +0900290 WriteJavaAnnotation(writer, classScope);
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900291 writer.Write("public final class %s {\n", class_name.c_str());
292 writer.Indent();
293 writer.Write("private %s () {}\n\n", class_name.c_str());
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900294 writer.Write("%s", kJavaParsersAndFormatters);
295
296 for (int i = 0; i < props.prop_size(); ++i) {
297 writer.Write("\n");
298
299 const sysprop::Property& prop = props.prop(i);
300
Inseob Kim14e51872018-10-25 14:27:33 +0900301 std::string prop_id = ApiNameToIdentifier(prop.api_name()).c_str();
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900302 std::string prop_type = GetJavaTypeName(prop);
303
304 if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
Inseob Kim1a58c042018-11-01 16:37:34 +0900305 if (prop.scope() != classScope) {
306 WriteJavaAnnotation(writer, prop.scope());
307 }
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900308 writer.Write("public static enum %s {\n",
309 GetJavaEnumTypeName(prop).c_str());
310 writer.Indent();
311 for (const std::string& name :
312 android::base::Split(prop.enum_values(), "|")) {
313 writer.Write("%s,\n", name.c_str());
314 }
315 writer.Dedent();
316 writer.Write("}\n\n");
317 }
318
Inseob Kim1a58c042018-11-01 16:37:34 +0900319 if (prop.scope() != classScope) {
320 WriteJavaAnnotation(writer, prop.scope());
321 }
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900322
323 writer.Write("public static Optional<%s> %s() {\n", prop_type.c_str(),
324 prop_id.c_str());
325 writer.Indent();
Inseob Kim14e51872018-10-25 14:27:33 +0900326 writer.Write("String value = SystemProperties.get(\"%s\");\n",
327 prop.prop_name().c_str());
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900328 writer.Write("return Optional.ofNullable(%s);\n",
329 GetParsingExpression(prop).c_str());
330 writer.Dedent();
Inseob Kim0773b942018-10-04 19:29:27 +0900331 writer.Write("}\n");
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900332
Inseob Kim14e51872018-10-25 14:27:33 +0900333 if (prop.access() != sysprop::Readonly) {
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900334 writer.Write("\n");
Inseob Kim1a58c042018-11-01 16:37:34 +0900335 if (classScope != sysprop::Internal) {
336 WriteJavaAnnotation(writer, sysprop::Internal);
337 }
Inseob Kim0773b942018-10-04 19:29:27 +0900338 writer.Write("public static void %s(%s value) {\n", prop_id.c_str(),
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900339 prop_type.c_str());
340 writer.Indent();
Inseob Kimbd987ed2018-11-01 11:07:15 +0900341 writer.Write("SystemProperties.set(\"%s\", value == null ? \"\" : %s);\n",
Inseob Kim14e51872018-10-25 14:27:33 +0900342 prop.prop_name().c_str(),
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900343 IsListProp(prop) ? "formatList(value)" : "value.toString()");
344 writer.Dedent();
Inseob Kim14e51872018-10-25 14:27:33 +0900345 writer.Write("}\n");
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900346 }
347 }
348
349 writer.Dedent();
350 writer.Write("}\n");
351
352 *java_result = writer.Code();
353 return true;
354}
355
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900356} // namespace
357
358bool GenerateJavaLibrary(const std::string& input_file_path,
Inseob Kim0773b942018-10-04 19:29:27 +0900359 const std::string& java_output_dir, std::string* err) {
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900360 sysprop::Properties props;
361
362 if (!ParseProps(input_file_path, &props, err)) {
363 return false;
364 }
365
Inseob Kim0773b942018-10-04 19:29:27 +0900366 std::string java_result;
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900367
368 if (!GenerateJavaClass(props, &java_result, err)) {
369 return false;
370 }
371
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900372 std::string package_name = GetJavaPackageName(props);
373 std::string java_package_dir =
374 java_output_dir + "/" + std::regex_replace(package_name, kRegexDot, "/");
375
376 if (!IsDirectory(java_package_dir) && !CreateDirectories(java_package_dir)) {
377 *err = "Creating directory to " + java_package_dir +
378 " failed: " + strerror(errno);
379 return false;
380 }
381
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900382 std::string class_name = GetJavaClassName(props);
383 std::string java_output_file = java_package_dir + "/" + class_name + ".java";
384 if (!android::base::WriteStringToFile(java_result, java_output_file)) {
385 *err = "Writing generated java class to " + java_output_file +
386 " failed: " + strerror(errno);
387 return false;
388 }
389
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900390 return true;
391}