• 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