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
23using android::base::Join;
24using android::base::Split;
25using android::base::StartsWith;
26using android::base::Trim;
27
28namespace android {
29namespace properties {
30
31namespace {
32
33bool 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
59bool 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
96void 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