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