xref: /aosp_15_r20/frameworks/native/libs/input/PropertyMap.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright (C) 2008 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "PropertyMap"
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include <cstdlib>
20*38e8c45fSAndroid Build Coastguard Worker 
21*38e8c45fSAndroid Build Coastguard Worker #include <input/PropertyMap.h>
22*38e8c45fSAndroid Build Coastguard Worker #include <log/log.h>
23*38e8c45fSAndroid Build Coastguard Worker 
24*38e8c45fSAndroid Build Coastguard Worker // Enables debug output for the parser.
25*38e8c45fSAndroid Build Coastguard Worker #define DEBUG_PARSER 0
26*38e8c45fSAndroid Build Coastguard Worker 
27*38e8c45fSAndroid Build Coastguard Worker // Enables debug output for parser performance.
28*38e8c45fSAndroid Build Coastguard Worker #define DEBUG_PARSER_PERFORMANCE 0
29*38e8c45fSAndroid Build Coastguard Worker 
30*38e8c45fSAndroid Build Coastguard Worker namespace android {
31*38e8c45fSAndroid Build Coastguard Worker 
32*38e8c45fSAndroid Build Coastguard Worker static const char* WHITESPACE = " \t\r";
33*38e8c45fSAndroid Build Coastguard Worker static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
34*38e8c45fSAndroid Build Coastguard Worker 
35*38e8c45fSAndroid Build Coastguard Worker // --- PropertyMap ---
36*38e8c45fSAndroid Build Coastguard Worker 
PropertyMap()37*38e8c45fSAndroid Build Coastguard Worker PropertyMap::PropertyMap() {}
38*38e8c45fSAndroid Build Coastguard Worker 
~PropertyMap()39*38e8c45fSAndroid Build Coastguard Worker PropertyMap::~PropertyMap() {}
40*38e8c45fSAndroid Build Coastguard Worker 
clear()41*38e8c45fSAndroid Build Coastguard Worker void PropertyMap::clear() {
42*38e8c45fSAndroid Build Coastguard Worker     mProperties.clear();
43*38e8c45fSAndroid Build Coastguard Worker }
44*38e8c45fSAndroid Build Coastguard Worker 
addProperty(const std::string & key,const std::string & value)45*38e8c45fSAndroid Build Coastguard Worker void PropertyMap::addProperty(const std::string& key, const std::string& value) {
46*38e8c45fSAndroid Build Coastguard Worker     mProperties.emplace(key, value);
47*38e8c45fSAndroid Build Coastguard Worker }
48*38e8c45fSAndroid Build Coastguard Worker 
getKeysWithPrefix(const std::string & prefix) const49*38e8c45fSAndroid Build Coastguard Worker std::unordered_set<std::string> PropertyMap::getKeysWithPrefix(const std::string& prefix) const {
50*38e8c45fSAndroid Build Coastguard Worker     std::unordered_set<std::string> keys;
51*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [key, _] : mProperties) {
52*38e8c45fSAndroid Build Coastguard Worker         if (key.starts_with(prefix)) {
53*38e8c45fSAndroid Build Coastguard Worker             keys.insert(key);
54*38e8c45fSAndroid Build Coastguard Worker         }
55*38e8c45fSAndroid Build Coastguard Worker     }
56*38e8c45fSAndroid Build Coastguard Worker     return keys;
57*38e8c45fSAndroid Build Coastguard Worker }
58*38e8c45fSAndroid Build Coastguard Worker 
hasProperty(const std::string & key) const59*38e8c45fSAndroid Build Coastguard Worker bool PropertyMap::hasProperty(const std::string& key) const {
60*38e8c45fSAndroid Build Coastguard Worker     return mProperties.find(key) != mProperties.end();
61*38e8c45fSAndroid Build Coastguard Worker }
62*38e8c45fSAndroid Build Coastguard Worker 
getString(const std::string & key) const63*38e8c45fSAndroid Build Coastguard Worker std::optional<std::string> PropertyMap::getString(const std::string& key) const {
64*38e8c45fSAndroid Build Coastguard Worker     auto it = mProperties.find(key);
65*38e8c45fSAndroid Build Coastguard Worker     return it != mProperties.end() ? std::make_optional(it->second) : std::nullopt;
66*38e8c45fSAndroid Build Coastguard Worker }
67*38e8c45fSAndroid Build Coastguard Worker 
getBool(const std::string & key) const68*38e8c45fSAndroid Build Coastguard Worker std::optional<bool> PropertyMap::getBool(const std::string& key) const {
69*38e8c45fSAndroid Build Coastguard Worker     std::optional<int32_t> intValue = getInt(key);
70*38e8c45fSAndroid Build Coastguard Worker     return intValue.has_value() ? std::make_optional(*intValue != 0) : std::nullopt;
71*38e8c45fSAndroid Build Coastguard Worker }
72*38e8c45fSAndroid Build Coastguard Worker 
getInt(const std::string & key) const73*38e8c45fSAndroid Build Coastguard Worker std::optional<int32_t> PropertyMap::getInt(const std::string& key) const {
74*38e8c45fSAndroid Build Coastguard Worker     std::optional<std::string> stringValue = getString(key);
75*38e8c45fSAndroid Build Coastguard Worker     if (!stringValue.has_value() || stringValue->length() == 0) {
76*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
77*38e8c45fSAndroid Build Coastguard Worker     }
78*38e8c45fSAndroid Build Coastguard Worker 
79*38e8c45fSAndroid Build Coastguard Worker     char* end;
80*38e8c45fSAndroid Build Coastguard Worker     int32_t value = static_cast<int32_t>(strtol(stringValue->c_str(), &end, 10));
81*38e8c45fSAndroid Build Coastguard Worker     if (*end != '\0') {
82*38e8c45fSAndroid Build Coastguard Worker         ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.c_str(),
83*38e8c45fSAndroid Build Coastguard Worker               stringValue->c_str());
84*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
85*38e8c45fSAndroid Build Coastguard Worker     }
86*38e8c45fSAndroid Build Coastguard Worker     return value;
87*38e8c45fSAndroid Build Coastguard Worker }
88*38e8c45fSAndroid Build Coastguard Worker 
getFloat(const std::string & key) const89*38e8c45fSAndroid Build Coastguard Worker std::optional<float> PropertyMap::getFloat(const std::string& key) const {
90*38e8c45fSAndroid Build Coastguard Worker     std::optional<std::string> stringValue = getString(key);
91*38e8c45fSAndroid Build Coastguard Worker     if (!stringValue.has_value() || stringValue->length() == 0) {
92*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
93*38e8c45fSAndroid Build Coastguard Worker     }
94*38e8c45fSAndroid Build Coastguard Worker 
95*38e8c45fSAndroid Build Coastguard Worker     char* end;
96*38e8c45fSAndroid Build Coastguard Worker     float value = strtof(stringValue->c_str(), &end);
97*38e8c45fSAndroid Build Coastguard Worker     if (*end != '\0') {
98*38e8c45fSAndroid Build Coastguard Worker         ALOGW("Property key '%s' has invalid value '%s'.  Expected a float.", key.c_str(),
99*38e8c45fSAndroid Build Coastguard Worker               stringValue->c_str());
100*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
101*38e8c45fSAndroid Build Coastguard Worker     }
102*38e8c45fSAndroid Build Coastguard Worker     return value;
103*38e8c45fSAndroid Build Coastguard Worker }
104*38e8c45fSAndroid Build Coastguard Worker 
getDouble(const std::string & key) const105*38e8c45fSAndroid Build Coastguard Worker std::optional<double> PropertyMap::getDouble(const std::string& key) const {
106*38e8c45fSAndroid Build Coastguard Worker     std::optional<std::string> stringValue = getString(key);
107*38e8c45fSAndroid Build Coastguard Worker     if (!stringValue.has_value() || stringValue->length() == 0) {
108*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
109*38e8c45fSAndroid Build Coastguard Worker     }
110*38e8c45fSAndroid Build Coastguard Worker 
111*38e8c45fSAndroid Build Coastguard Worker     char* end;
112*38e8c45fSAndroid Build Coastguard Worker     double value = strtod(stringValue->c_str(), &end);
113*38e8c45fSAndroid Build Coastguard Worker     if (*end != '\0') {
114*38e8c45fSAndroid Build Coastguard Worker         ALOGW("Property key '%s' has invalid value '%s'.  Expected a double.", key.c_str(),
115*38e8c45fSAndroid Build Coastguard Worker               stringValue->c_str());
116*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
117*38e8c45fSAndroid Build Coastguard Worker     }
118*38e8c45fSAndroid Build Coastguard Worker     return value;
119*38e8c45fSAndroid Build Coastguard Worker }
120*38e8c45fSAndroid Build Coastguard Worker 
addAll(const PropertyMap * map)121*38e8c45fSAndroid Build Coastguard Worker void PropertyMap::addAll(const PropertyMap* map) {
122*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [key, value] : map->mProperties) {
123*38e8c45fSAndroid Build Coastguard Worker         mProperties.emplace(key, value);
124*38e8c45fSAndroid Build Coastguard Worker     }
125*38e8c45fSAndroid Build Coastguard Worker }
126*38e8c45fSAndroid Build Coastguard Worker 
load(const char * filename)127*38e8c45fSAndroid Build Coastguard Worker android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) {
128*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>();
129*38e8c45fSAndroid Build Coastguard Worker     if (outMap == nullptr) {
130*38e8c45fSAndroid Build Coastguard Worker         return android::base::Error(NO_MEMORY) << "Error allocating property map.";
131*38e8c45fSAndroid Build Coastguard Worker     }
132*38e8c45fSAndroid Build Coastguard Worker 
133*38e8c45fSAndroid Build Coastguard Worker     Tokenizer* rawTokenizer;
134*38e8c45fSAndroid Build Coastguard Worker     status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
135*38e8c45fSAndroid Build Coastguard Worker     if (status) {
136*38e8c45fSAndroid Build Coastguard Worker         return android::base::Error(-status) << "Could not open file: " << filename;
137*38e8c45fSAndroid Build Coastguard Worker     }
138*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER_PERFORMANCE
139*38e8c45fSAndroid Build Coastguard Worker     nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
140*38e8c45fSAndroid Build Coastguard Worker #endif
141*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
142*38e8c45fSAndroid Build Coastguard Worker     Parser parser(outMap.get(), tokenizer.get());
143*38e8c45fSAndroid Build Coastguard Worker     status = parser.parse();
144*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER_PERFORMANCE
145*38e8c45fSAndroid Build Coastguard Worker     nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
146*38e8c45fSAndroid Build Coastguard Worker     ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(),
147*38e8c45fSAndroid Build Coastguard Worker           tokenizer->getLineNumber(), elapsedTime / 1000000.0);
148*38e8c45fSAndroid Build Coastguard Worker #endif
149*38e8c45fSAndroid Build Coastguard Worker     if (status) {
150*38e8c45fSAndroid Build Coastguard Worker         return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
151*38e8c45fSAndroid Build Coastguard Worker     }
152*38e8c45fSAndroid Build Coastguard Worker 
153*38e8c45fSAndroid Build Coastguard Worker     return std::move(outMap);
154*38e8c45fSAndroid Build Coastguard Worker }
155*38e8c45fSAndroid Build Coastguard Worker 
156*38e8c45fSAndroid Build Coastguard Worker // --- PropertyMap::Parser ---
157*38e8c45fSAndroid Build Coastguard Worker 
Parser(PropertyMap * map,Tokenizer * tokenizer)158*38e8c45fSAndroid Build Coastguard Worker PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer)
159*38e8c45fSAndroid Build Coastguard Worker       : mMap(map), mTokenizer(tokenizer) {}
160*38e8c45fSAndroid Build Coastguard Worker 
~Parser()161*38e8c45fSAndroid Build Coastguard Worker PropertyMap::Parser::~Parser() {}
162*38e8c45fSAndroid Build Coastguard Worker 
parse()163*38e8c45fSAndroid Build Coastguard Worker status_t PropertyMap::Parser::parse() {
164*38e8c45fSAndroid Build Coastguard Worker     while (!mTokenizer->isEof()) {
165*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER
166*38e8c45fSAndroid Build Coastguard Worker         ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
167*38e8c45fSAndroid Build Coastguard Worker               mTokenizer->peekRemainderOfLine().c_str());
168*38e8c45fSAndroid Build Coastguard Worker #endif
169*38e8c45fSAndroid Build Coastguard Worker 
170*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->skipDelimiters(WHITESPACE);
171*38e8c45fSAndroid Build Coastguard Worker 
172*38e8c45fSAndroid Build Coastguard Worker         if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
173*38e8c45fSAndroid Build Coastguard Worker             String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
174*38e8c45fSAndroid Build Coastguard Worker             if (keyToken.empty()) {
175*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().c_str());
176*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
177*38e8c45fSAndroid Build Coastguard Worker             }
178*38e8c45fSAndroid Build Coastguard Worker 
179*38e8c45fSAndroid Build Coastguard Worker             mTokenizer->skipDelimiters(WHITESPACE);
180*38e8c45fSAndroid Build Coastguard Worker 
181*38e8c45fSAndroid Build Coastguard Worker             if (mTokenizer->nextChar() != '=') {
182*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Expected '=' between property key and value.",
183*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->getLocation().c_str());
184*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
185*38e8c45fSAndroid Build Coastguard Worker             }
186*38e8c45fSAndroid Build Coastguard Worker 
187*38e8c45fSAndroid Build Coastguard Worker             mTokenizer->skipDelimiters(WHITESPACE);
188*38e8c45fSAndroid Build Coastguard Worker 
189*38e8c45fSAndroid Build Coastguard Worker             String8 valueToken = mTokenizer->nextToken(WHITESPACE);
190*38e8c45fSAndroid Build Coastguard Worker             if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
191*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
192*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->getLocation().c_str());
193*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
194*38e8c45fSAndroid Build Coastguard Worker             }
195*38e8c45fSAndroid Build Coastguard Worker 
196*38e8c45fSAndroid Build Coastguard Worker             mTokenizer->skipDelimiters(WHITESPACE);
197*38e8c45fSAndroid Build Coastguard Worker             if (!mTokenizer->isEol()) {
198*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(),
199*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->peekRemainderOfLine().c_str());
200*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
201*38e8c45fSAndroid Build Coastguard Worker             }
202*38e8c45fSAndroid Build Coastguard Worker 
203*38e8c45fSAndroid Build Coastguard Worker             if (mMap->hasProperty(keyToken.c_str())) {
204*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Duplicate property value for key '%s'.",
205*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->getLocation().c_str(), keyToken.c_str());
206*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
207*38e8c45fSAndroid Build Coastguard Worker             }
208*38e8c45fSAndroid Build Coastguard Worker 
209*38e8c45fSAndroid Build Coastguard Worker             mMap->addProperty(keyToken.c_str(), valueToken.c_str());
210*38e8c45fSAndroid Build Coastguard Worker         }
211*38e8c45fSAndroid Build Coastguard Worker 
212*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->nextLine();
213*38e8c45fSAndroid Build Coastguard Worker     }
214*38e8c45fSAndroid Build Coastguard Worker     return OK;
215*38e8c45fSAndroid Build Coastguard Worker }
216*38e8c45fSAndroid Build Coastguard Worker 
217*38e8c45fSAndroid Build Coastguard Worker } // namespace android
218