Home | History | Annotate | Download | only in libutils
      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         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 
     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         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 
    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         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 
    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         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