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 40 PropertyMap::PropertyMap() { 41 } 42 43 PropertyMap::~PropertyMap() { 44 } 45 46 void PropertyMap::clear() { 47 mProperties.clear(); 48 } 49 50 void PropertyMap::addProperty(const String8& key, const String8& value) { 51 mProperties.add(key, value); 52 } 53 54 bool PropertyMap::hasProperty(const String8& key) const { 55 return mProperties.indexOfKey(key) >= 0; 56 } 57 58 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 68 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 78 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 LOGW("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 95 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 LOGW("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 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 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 LOGE("Error %d opening property file %s.", status, filename.string()); 125 } else { 126 PropertyMap* map = new PropertyMap(); 127 if (!map) { 128 LOGE("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 LOGD("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 156 PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : 157 mMap(map), mTokenizer(tokenizer) { 158 } 159 160 PropertyMap::Parser::~Parser() { 161 } 162 163 status_t PropertyMap::Parser::parse() { 164 while (!mTokenizer->isEof()) { 165 #if DEBUG_PARSER 166 LOGD("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 LOGE("%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 LOGE("%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 LOGE("%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 LOGE("%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 LOGE("%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