/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include using namespace log4cxx; using namespace log4cxx::helpers; class PropertyParser { public: void parse(LogString& in, Properties& properties) { LogString key, element; LexemType lexemType = BEGIN; logchar c; bool finished = false; if (!get(in, c)) { return; } while (!finished) { switch(lexemType) { case BEGIN: switch(c) { case 0x20: // ' ' case 0x09: // '\t' case 0x0A: // '\n' case 0x0D: // '\r' if (!get(in, c)) finished = true; break; case 0x23: // '#' case 0x21: // '!' lexemType = COMMENT; if (!get(in, c)) finished = true; break; default: lexemType = KEY; break; } break; case KEY: switch(c) { case 0x5C: // '\\' lexemType = KEY_ESCAPE; if (!get(in, c)) finished = true; break; case 0x09: // '\t' case 0x20: // ' ' case 0x3A: // ':' case 0x3D: // '=' lexemType = DELIMITER; if (!get(in, c)) finished = true; break; case 0x0A: case 0x0D: // key associated with an empty string element properties.setProperty(key, LogString()); key.erase(key.begin(), key.end()); lexemType = BEGIN; if (!get(in, c)) finished = true; break; default: key.append(1, c); if (!get(in, c)) finished = true; break; } break; case KEY_ESCAPE: switch(c) { case 0x74: // 't' key.append(1, 0x09); lexemType = KEY; break; case 0x6E: // 'n' key.append(1, 0x0A); lexemType = KEY; break; case 0x72: // 'r' key.append(1, 0x0D); lexemType = KEY; break; case 0x0A: // '\n' lexemType = KEY_CONTINUE; break; case 0x0D: // '\r' lexemType = KEY_CONTINUE2; break; default: key.append(1, c); lexemType = KEY; } if (!get(in, c)) { finished = true; } break; case KEY_CONTINUE: switch(c) { case 0x20: // ' ' case 0x09: // '\t' if (!get(in, c)) finished = true; break; default: lexemType = KEY; break; } break; case KEY_CONTINUE2: switch(c) { case 0x0A: // '\n' if (!get(in, c)) finished = true; lexemType = KEY_CONTINUE; break; default: lexemType = KEY_CONTINUE; break; } break; case DELIMITER: switch(c) { case 0x09: // '\t' case 0x20: // ' ' case 0x3A: // ':' case 0x3D: // '=' if (!get(in, c)) finished = true; break; default: lexemType = ELEMENT; break; } break; case ELEMENT: switch(c) { case 0x5C: // '\\' lexemType = ELEMENT_ESCAPE; if (!get(in, c)) finished = true; break; case 0x0A: // '\n' case 0x0D: // '\r' // key associated with an empty string element properties.setProperty(key, element); key.erase(key.begin(), key.end()); element.erase(element.begin(), element.end()); lexemType = BEGIN; if (!get(in, c)) finished = true; break; default: element.append(1, c); if (!get(in, c)) finished = true; break; } break; case ELEMENT_ESCAPE: switch(c) { case 0x74: // 't' element.append(1, 0x09); lexemType = ELEMENT; break; case 0x6E: // 'n' element.append(1, 0x0A); lexemType = ELEMENT; break; case 0x72: // 'r' element.append(1, 0x0D); lexemType = ELEMENT; break; case 0x0A: // '\n' lexemType = ELEMENT_CONTINUE; break; case 0x0D: // '\r' lexemType = ELEMENT_CONTINUE2; break; default: element.append(1, c); lexemType = ELEMENT; break; } if (!get(in, c)) { finished = true; } break; case ELEMENT_CONTINUE: switch(c) { case 0x20: // ' ' case 0x09: // '\t' if (!get(in, c)) finished = true; break; default: lexemType = ELEMENT; break; } break; case ELEMENT_CONTINUE2: switch(c) { case 0x0A: // '\n' if (!get(in, c)) finished = true; lexemType = ELEMENT_CONTINUE; break; default: lexemType = ELEMENT_CONTINUE; break; } break; case COMMENT: if (c == 0x0A || c == 0x0D) { lexemType = BEGIN; } if (!get(in, c)) finished = true; break; } } if (!key.empty()) { properties.setProperty(key, element); } } protected: static bool get(LogString& in, logchar& c) { if (in.empty()) { c = 0; return false; } c = in[0]; in.erase(in.begin()); return true; } typedef enum { BEGIN, KEY, KEY_ESCAPE, KEY_CONTINUE, KEY_CONTINUE2, DELIMITER, ELEMENT, ELEMENT_ESCAPE, ELEMENT_CONTINUE, ELEMENT_CONTINUE2, COMMENT } LexemType; }; Properties::Properties() : properties(new PropertyMap()) { } Properties::~Properties() { delete properties; } LogString Properties::setProperty(const LogString& key, const LogString& value) { return put(key, value); } LogString Properties::put(const LogString& key, const LogString& value) { LogString oldValue((*properties)[key]); (*properties)[key] = value; return oldValue; } LogString Properties::getProperty(const LogString& key) const { return get(key); } LogString Properties::get(const LogString& key) const { PropertyMap::const_iterator it = properties->find(key); return (it != properties->end()) ? it->second : LogString(); } void Properties::load(InputStreamPtr inStream) { Pool pool; InputStreamReaderPtr lineReader( new InputStreamReader(inStream, CharsetDecoder::getISOLatinDecoder())); LogString contents = lineReader->read(pool); properties->clear(); PropertyParser parser; parser.parse(contents, *this); } std::vector Properties::propertyNames() const { std::vector names; names.reserve(properties->size()); PropertyMap::const_iterator it; for (it = properties->begin(); it != properties->end(); it++) { const LogString& key = it->first; names.push_back(key); } return names; }