blob: c11dbf50ee31f5eb5bd69ebf7b3ee4dca7f27991 [file] [log] [blame]
/*
* 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 <log4cxx/logstring.h>
#include <log4cxx/helpers/properties.h>
#include <log4cxx/helpers/inputstreamreader.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/pool.h>
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<LogString> Properties::propertyNames() const
{
std::vector<LogString> 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;
}