1 /*
2 * Copyright (C) 2008 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 #define LOG_TAG "PropertyMap"
18
19 #include <input/PropertyMap.h>
20
21 // Enables debug output for the parser.
22 #define DEBUG_PARSER 0
23
24 // Enables debug output for parser performance.
25 #define DEBUG_PARSER_PERFORMANCE 0
26
27 namespace android {
28
29 static const char* WHITESPACE = " \t\r";
30 static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
31
32 // --- PropertyMap ---
33
PropertyMap()34 PropertyMap::PropertyMap() {}
35
~PropertyMap()36 PropertyMap::~PropertyMap() {}
37
clear()38 void PropertyMap::clear() {
39 mProperties.clear();
40 }
41
addProperty(const String8 & key,const String8 & value)42 void PropertyMap::addProperty(const String8& key, const String8& value) {
43 mProperties.add(key, value);
44 }
45
hasProperty(const String8 & key) const46 bool PropertyMap::hasProperty(const String8& key) const {
47 return mProperties.indexOfKey(key) >= 0;
48 }
49
tryGetProperty(const String8 & key,String8 & outValue) const50 bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
51 ssize_t index = mProperties.indexOfKey(key);
52 if (index < 0) {
53 return false;
54 }
55
56 outValue = mProperties.valueAt(index);
57 return true;
58 }
59
tryGetProperty(const String8 & key,bool & outValue) const60 bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
61 int32_t intValue;
62 if (!tryGetProperty(key, intValue)) {
63 return false;
64 }
65
66 outValue = intValue;
67 return true;
68 }
69
tryGetProperty(const String8 & key,int32_t & outValue) const70 bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
71 String8 stringValue;
72 if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
73 return false;
74 }
75
76 char* end;
77 int value = strtol(stringValue.string(), &end, 10);
78 if (*end != '\0') {
79 ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(),
80 stringValue.string());
81 return false;
82 }
83 outValue = value;
84 return true;
85 }
86
tryGetProperty(const String8 & key,float & outValue) const87 bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
88 String8 stringValue;
89 if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
90 return false;
91 }
92
93 char* end;
94 float value = strtof(stringValue.string(), &end);
95 if (*end != '\0') {
96 ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(),
97 stringValue.string());
98 return false;
99 }
100 outValue = value;
101 return true;
102 }
103
addAll(const PropertyMap * map)104 void PropertyMap::addAll(const PropertyMap* map) {
105 for (size_t i = 0; i < map->mProperties.size(); i++) {
106 mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
107 }
108 }
109
load(const char * filename)110 android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) {
111 std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>();
112 if (outMap == nullptr) {
113 return android::base::Error(NO_MEMORY) << "Error allocating property map.";
114 }
115
116 Tokenizer* rawTokenizer;
117 status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
118 std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
119 if (status) {
120 ALOGE("Error %d opening property file %s.", status, filename);
121 } else {
122 #if DEBUG_PARSER_PERFORMANCE
123 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
124 #endif
125 Parser parser(outMap.get(), tokenizer.get());
126 status = parser.parse();
127 #if DEBUG_PARSER_PERFORMANCE
128 nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
129 ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
130 tokenizer->getFilename().string(), tokenizer->getLineNumber(),
131 elapsedTime / 1000000.0);
132 #endif
133 if (status) {
134 return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
135 }
136 }
137 return std::move(outMap);
138 }
139
140 // --- PropertyMap::Parser ---
141
Parser(PropertyMap * map,Tokenizer * tokenizer)142 PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer)
143 : mMap(map), mTokenizer(tokenizer) {}
144
~Parser()145 PropertyMap::Parser::~Parser() {}
146
parse()147 status_t PropertyMap::Parser::parse() {
148 while (!mTokenizer->isEof()) {
149 #if DEBUG_PARSER
150 ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
151 mTokenizer->peekRemainderOfLine().string());
152 #endif
153
154 mTokenizer->skipDelimiters(WHITESPACE);
155
156 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
157 String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
158 if (keyToken.isEmpty()) {
159 ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
160 return BAD_VALUE;
161 }
162
163 mTokenizer->skipDelimiters(WHITESPACE);
164
165 if (mTokenizer->nextChar() != '=') {
166 ALOGE("%s: Expected '=' between property key and value.",
167 mTokenizer->getLocation().string());
168 return BAD_VALUE;
169 }
170
171 mTokenizer->skipDelimiters(WHITESPACE);
172
173 String8 valueToken = mTokenizer->nextToken(WHITESPACE);
174 if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
175 ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
176 mTokenizer->getLocation().string());
177 return BAD_VALUE;
178 }
179
180 mTokenizer->skipDelimiters(WHITESPACE);
181 if (!mTokenizer->isEol()) {
182 ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(),
183 mTokenizer->peekRemainderOfLine().string());
184 return BAD_VALUE;
185 }
186
187 if (mMap->hasProperty(keyToken)) {
188 ALOGE("%s: Duplicate property value for key '%s'.",
189 mTokenizer->getLocation().string(), keyToken.string());
190 return BAD_VALUE;
191 }
192
193 mMap->addProperty(keyToken, valueToken);
194 }
195
196 mTokenizer->nextLine();
197 }
198 return OK;
199 }
200
201 } // namespace android
202