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,bool require_prefix_or_exact,PropertyInfoEntry * out,std::string * error)59 bool ParsePropertyInfoLine(const std::string& line, bool require_prefix_or_exact,
60 PropertyInfoEntry* out, std::string* error) {
61 auto tokenizer = SpaceTokenizer(line);
62
63 auto property = tokenizer.GetNext();
64 if (property.empty()) {
65 *error = "Did not find a property entry in '" + line + "'";
66 return false;
67 }
68
69 auto context = tokenizer.GetNext();
70 if (context.empty()) {
71 *error = "Did not find a context entry in '" + line + "'";
72 return false;
73 }
74
75 // It is not an error to not find exact_match or a type, as older files will not contain them.
76 auto match_operation = tokenizer.GetNext();
77 // We reformat type to be space deliminated regardless of the input whitespace for easier storage
78 // and subsequent parsing.
79 auto type_strings = std::vector<std::string>{};
80 auto type = tokenizer.GetNext();
81 while (!type.empty()) {
82 type_strings.emplace_back(type);
83 type = tokenizer.GetNext();
84 }
85
86 bool exact_match = false;
87 if (match_operation == "exact") {
88 exact_match = true;
89 } else if (match_operation != "prefix" && match_operation != "" && require_prefix_or_exact) {
90 *error = "Match operation '" + match_operation +
91 "' is not valid: must be either 'prefix' or 'exact'";
92 return false;
93 }
94
95 if (!type_strings.empty() && !IsTypeValid(type_strings)) {
96 *error = "Type '" + Join(type_strings, " ") + "' is not valid";
97 return false;
98 }
99
100 *out = {property, context, Join(type_strings, " "), exact_match};
101 return true;
102 }
103
104 } // namespace
105
ParsePropertyInfoFile(const std::string & file_contents,bool require_prefix_or_exact,std::vector<PropertyInfoEntry> * property_infos,std::vector<std::string> * errors)106 void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,
107 std::vector<PropertyInfoEntry>* property_infos,
108 std::vector<std::string>* errors) {
109 // Do not clear property_infos to allow this function to be called on multiple files, with
110 // their results concatenated.
111 errors->clear();
112
113 for (const auto& line : Split(file_contents, "\n")) {
114 auto trimmed_line = Trim(line);
115 if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
116 continue;
117 }
118
119 auto property_info_entry = PropertyInfoEntry{};
120 auto parse_error = std::string{};
121 if (!ParsePropertyInfoLine(trimmed_line, require_prefix_or_exact, &property_info_entry,
122 &parse_error)) {
123 errors->emplace_back(parse_error);
124 continue;
125 }
126
127 property_infos->emplace_back(property_info_entry);
128 }
129 }
130
131 } // namespace properties
132 } // namespace android
133