1 //
2 // Copyright (C) 2017 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 #include <property_info_serializer/property_info_serializer.h>
18
19 #include <android-base/strings.h>
20
21 #include "space_tokenizer.h"
22
23 using android::base::Join;
24 using android::base::Split;
25 using android::base::StartsWith;
26 using android::base::Trim;
27
28 namespace android {
29 namespace properties {
30
31 namespace {
32
IsTypeValid(const std::vector<std::string> & type_strings)33 bool IsTypeValid(const std::vector<std::string>& type_strings) {
34 if (type_strings.empty()) {
35 return false;
36 }
37
38 // There must be at least one string following 'enum'
39 if (type_strings[0] == "enum") {
40 return type_strings.size() > 1;
41 }
42
43 // There should not be any string following any other types.
44 if (type_strings.size() != 1) {
45 return false;
46 }
47
48 // Check that the type matches one of remaining valid types.
49 static const char* const no_parameter_types[] = {"string", "bool", "int",
50 "uint", "double", "size"};
51 for (const auto& type : no_parameter_types) {
52 if (type_strings[0] == type) {
53 return true;
54 }
55 }
56 return false;
57 }
58
ParsePropertyInfoLine(const std::string & line,PropertyInfoEntry * out,std::string * error)59 bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
60 auto tokenizer = SpaceTokenizer(line);
61
62 auto property = tokenizer.GetNext();
63 if (property.empty()) {
64 *error = "Did not find a property entry in '" + line + "'";
65 return false;
66 }
67
68 auto context = tokenizer.GetNext();
69 if (context.empty()) {
70 *error = "Did not find a context entry in '" + line + "'";
71 return false;
72 }
73
74 // It is not an error to not find exact_match or a type, as older files will not contain them.
75 auto exact_match = tokenizer.GetNext();
76 // We reformat type to be space deliminated regardless of the input whitespace for easier storage
77 // and subsequent parsing.
78 auto type_strings = std::vector<std::string>{};
79 auto type = tokenizer.GetNext();
80 while (!type.empty()) {
81 type_strings.emplace_back(type);
82 type = tokenizer.GetNext();
83 }
84
85 if (!type_strings.empty() && !IsTypeValid(type_strings)) {
86 *error = "Type '" + Join(type_strings, " ") + "' is not valid";
87 return false;
88 }
89
90 *out = {property, context, Join(type_strings, " "), exact_match == "exact"};
91 return true;
92 }
93
94 } // namespace
95
ParsePropertyInfoFile(const std::string & file_contents,std::vector<PropertyInfoEntry> * property_infos,std::vector<std::string> * errors)96 void ParsePropertyInfoFile(const std::string& file_contents,
97 std::vector<PropertyInfoEntry>* property_infos,
98 std::vector<std::string>* errors) {
99 // Do not clear property_infos to allow this function to be called on multiple files, with
100 // their results concatenated.
101 errors->clear();
102
103 for (const auto& line : Split(file_contents, "\n")) {
104 auto trimmed_line = Trim(line);
105 if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
106 continue;
107 }
108
109 auto property_info_entry = PropertyInfoEntry{};
110 auto parse_error = std::string{};
111 if (!ParsePropertyInfoLine(trimmed_line, &property_info_entry, &parse_error)) {
112 errors->emplace_back(parse_error);
113 continue;
114 }
115
116 property_infos->emplace_back(property_info_entry);
117 }
118 }
119
120 } // namespace properties
121 } // namespace android
122