| // |
| // Copyright (C) 2017 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include <property_info_serializer/property_info_serializer.h> |
| |
| #include <android-base/strings.h> |
| |
| #include "space_tokenizer.h" |
| |
| using android::base::Join; |
| using android::base::Split; |
| using android::base::StartsWith; |
| using android::base::Trim; |
| |
| namespace android { |
| namespace properties { |
| |
| namespace { |
| |
| bool IsTypeValid(const std::vector<std::string>& type_strings) { |
| if (type_strings.empty()) { |
| return false; |
| } |
| |
| // There must be at least one string following 'enum' |
| if (type_strings[0] == "enum") { |
| return type_strings.size() > 1; |
| } |
| |
| // There should not be any string following any other types. |
| if (type_strings.size() != 1) { |
| return false; |
| } |
| |
| // Check that the type matches one of remaining valid types. |
| static const char* const no_parameter_types[] = {"string", "bool", "int", |
| "uint", "double", "size"}; |
| for (const auto& type : no_parameter_types) { |
| if (type_strings[0] == type) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) { |
| auto tokenizer = SpaceTokenizer(line); |
| |
| auto property = tokenizer.GetNext(); |
| if (property.empty()) { |
| *error = "Did not find a property entry in '" + line + "'"; |
| return false; |
| } |
| |
| auto context = tokenizer.GetNext(); |
| if (context.empty()) { |
| *error = "Did not find a context entry in '" + line + "'"; |
| return false; |
| } |
| |
| // It is not an error to not find exact_match or a type, as older files will not contain them. |
| auto exact_match = tokenizer.GetNext(); |
| // We reformat type to be space deliminated regardless of the input whitespace for easier storage |
| // and subsequent parsing. |
| auto type_strings = std::vector<std::string>{}; |
| auto type = tokenizer.GetNext(); |
| while (!type.empty()) { |
| type_strings.emplace_back(type); |
| type = tokenizer.GetNext(); |
| } |
| |
| if (!type_strings.empty() && !IsTypeValid(type_strings)) { |
| *error = "Type '" + Join(type_strings, " ") + "' is not valid"; |
| return false; |
| } |
| |
| *out = {property, context, Join(type_strings, " "), exact_match == "exact"}; |
| return true; |
| } |
| |
| } // namespace |
| |
| void ParsePropertyInfoFile(const std::string& file_contents, |
| std::vector<PropertyInfoEntry>* property_infos, |
| std::vector<std::string>* errors) { |
| // Do not clear property_infos to allow this function to be called on multiple files, with |
| // their results concatenated. |
| errors->clear(); |
| |
| for (const auto& line : Split(file_contents, "\n")) { |
| auto trimmed_line = Trim(line); |
| if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) { |
| continue; |
| } |
| |
| auto property_info_entry = PropertyInfoEntry{}; |
| auto parse_error = std::string{}; |
| if (!ParsePropertyInfoLine(trimmed_line, &property_info_entry, &parse_error)) { |
| errors->emplace_back(parse_error); |
| continue; |
| } |
| |
| property_infos->emplace_back(property_info_entry); |
| } |
| } |
| |
| } // namespace properties |
| } // namespace android |