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