• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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