blob: 6e45fde96592957f0927d446a3249d00694510dd [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/pattern/patternparser.h>
#include <log4cxx/pattern/literalpatternconverter.h>
#include <log4cxx/helpers/loglog.h>
using namespace log4cxx;
using namespace log4cxx::pattern;
using namespace log4cxx::helpers;
const logchar PatternParser::ESCAPE_CHAR = 0x25; // '%'
/**
* Private constructor.
*/
PatternParser::PatternParser()
{
}
bool PatternParser::isUnicodeIdentifierStart(logchar ch)
{
//
// greatly simplified version checks if
// character is USACII alpha or number
//
return (ch >= 0x41 /* 'A' */ && ch <= 0x5A /* 'Z' */) ||
(ch >= 0x61 /* 'a' */ && ch <= 0x7A /* 'z' */) ||
(ch >= 0x30 /* '0' */ && ch <= 0x39 /* '9' */);
}
bool PatternParser::isUnicodeIdentifierPart(logchar ch)
{
//
// greatly simplified version checks if
// character is USACII alpha or number
//
return isUnicodeIdentifierStart(ch)
|| (ch == 0x5F /* '_' */);
}
size_t PatternParser::extractConverter(
logchar lastChar, const LogString& pattern,
LogString::size_type i, LogString& convBuf,
LogString& currentLiteral)
{
if (!convBuf.empty())
{
convBuf.erase(convBuf.begin(), convBuf.end());
}
// When this method is called, lastChar points to the first character of the
// conversion word. For example:
// For "%hello" lastChar = 'h'
// For "%-5hello" lastChar = 'h'
//System.out.println("lastchar is "+lastChar);
if (!isUnicodeIdentifierStart(lastChar))
{
return i;
}
convBuf.append(1, lastChar);
while (
(i < pattern.length())
&& isUnicodeIdentifierPart(pattern[i]))
{
convBuf.append(1, pattern[i]);
currentLiteral.append(1, pattern[i]);
//System.out.println("conv buffer is now ["+convBuf+"].");
i++;
}
return i;
}
size_t PatternParser::extractOptions(const LogString& pattern, LogString::size_type i,
std::vector<LogString>& options)
{
while ((i < pattern.length()) && (pattern[i] == 0x7B /* '{' */))
{
size_t end = pattern.find(0x7D /* '}' */, i);
if (end == pattern.npos)
{
break;
}
LogString r(pattern.substr(i + 1, end - i - 1));
options.push_back(r);
i = end + 1;
}
return i;
}
void PatternParser::parse(
const LogString& pattern,
std::vector<PatternConverterPtr>& patternConverters,
std::vector<FormattingInfoPtr>& formattingInfos,
const PatternMap& rules)
{
LogString currentLiteral;
size_t patternLength = pattern.length();
int state = LITERAL_STATE;
logchar c;
size_t i = 0;
FormattingInfoPtr formattingInfo(FormattingInfo::getDefault());
while (i < patternLength)
{
c = pattern[i++];
switch (state)
{
case LITERAL_STATE:
// In literal state, the last char is always a literal.
if (i == patternLength)
{
currentLiteral.append(1, c);
continue;
}
if (c == ESCAPE_CHAR)
{
// peek at the next char.
if (pattern[i] == ESCAPE_CHAR)
{
currentLiteral.append(1, c);
i++; // move pointer
}
else
{
if (!currentLiteral.empty())
{
patternConverters.push_back(
LiteralPatternConverter::newInstance(currentLiteral));
formattingInfos.push_back(FormattingInfo::getDefault());
currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
}
currentLiteral.append(1, c); // append %
state = CONVERTER_STATE;
formattingInfo = FormattingInfo::getDefault();
}
}
else
{
currentLiteral.append(1, c);
}
break;
case CONVERTER_STATE:
currentLiteral.append(1, c);
switch (c)
{
case 0x2D: // '-'
formattingInfo =
new FormattingInfo(
true, formattingInfo->getMinLength(),
formattingInfo->getMaxLength());
break;
case 0x2E: // '.'
state = DOT_STATE;
break;
default:
if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
{
formattingInfo =
new FormattingInfo(
formattingInfo->isLeftAligned(), c - 0x30 /* '0' */,
formattingInfo->getMaxLength());
state = MIN_STATE;
}
else
{
i = finalizeConverter(
c, pattern, i, currentLiteral, formattingInfo,
rules, patternConverters, formattingInfos);
// Next pattern is assumed to be a literal.
state = LITERAL_STATE;
formattingInfo = FormattingInfo::getDefault();
if (!currentLiteral.empty())
{
currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
}
}
} // switch
break;
case MIN_STATE:
currentLiteral.append(1, c);
if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
{
formattingInfo =
new FormattingInfo(
formattingInfo->isLeftAligned(),
(formattingInfo->getMinLength() * 10) + (c - 0x30 /* '0' */),
formattingInfo->getMaxLength());
}
else if (c == 0x2E /* '.' */)
{
state = DOT_STATE;
}
else
{
i = finalizeConverter(
c, pattern, i, currentLiteral, formattingInfo,
rules, patternConverters, formattingInfos);
state = LITERAL_STATE;
formattingInfo = FormattingInfo::getDefault();
if (!currentLiteral.empty())
{
currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
}
}
break;
case DOT_STATE:
currentLiteral.append(1, c);
if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
{
formattingInfo =
new FormattingInfo(
formattingInfo->isLeftAligned(), formattingInfo->getMinLength(),
c - 0x30 /* '0' */);
state = MAX_STATE;
}
else
{
LogLog::error(LOG4CXX_STR("Error in pattern, was expecting digit."));
state = LITERAL_STATE;
}
break;
case MAX_STATE:
currentLiteral.append(1, c);
if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
{
formattingInfo =
new FormattingInfo(
formattingInfo->isLeftAligned(), formattingInfo->getMinLength(),
(formattingInfo->getMaxLength() * 10) + (c - 0x30 /* '0' */));
}
else
{
i = finalizeConverter(
c, pattern, i, currentLiteral, formattingInfo,
rules, patternConverters, formattingInfos);
state = LITERAL_STATE;
formattingInfo = FormattingInfo::getDefault();
if (!currentLiteral.empty())
{
currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
}
}
break;
} // switch
}
// while
if (currentLiteral.length() != 0)
{
patternConverters.push_back(
LiteralPatternConverter::newInstance(currentLiteral));
formattingInfos.push_back(FormattingInfo::getDefault());
}
}
PatternConverterPtr PatternParser::createConverter(
const LogString& converterId,
LogString& currentLiteral,
const PatternMap& rules,
std::vector<LogString>& options)
{
LogString converterName(converterId);
for (size_t i = converterId.length(); i > 0; i--)
{
converterName = converterName.substr(0, i);
PatternMap::const_iterator iter = rules.find(converterName);
if (iter != rules.end())
{
currentLiteral.erase(currentLiteral.begin(),
currentLiteral.end() - (converterId.length() - i));
return (iter->second)(options);
}
}
LogLog::error(LogString(LOG4CXX_STR("Unrecognized format specifier ")) + converterId);
ObjectPtr converterObj;
return converterObj;
}
size_t PatternParser::finalizeConverter(
logchar c, const LogString& pattern, size_t i,
LogString& currentLiteral, const FormattingInfoPtr& formattingInfo,
const PatternMap& rules,
std::vector<PatternConverterPtr>& patternConverters,
std::vector<FormattingInfoPtr>& formattingInfos)
{
LogString convBuf;
i = extractConverter(c, pattern, i, convBuf, currentLiteral);
if (convBuf.empty())
{
LogLog::error(LOG4CXX_STR("Empty conversion specifier"));
patternConverters.push_back(
LiteralPatternConverter::newInstance(currentLiteral));
formattingInfos.push_back(FormattingInfo::getDefault());
}
else
{
LogString converterId(convBuf);
std::vector<LogString> options;
i = extractOptions(pattern, i, options);
PatternConverterPtr pc(
createConverter(
converterId, currentLiteral, rules, options));
if (pc == NULL)
{
LogString msg(LOG4CXX_STR("Unrecognized conversion specifier ["));
msg.append(converterId);
msg.append(LOG4CXX_STR("] in conversion pattern."));
LogLog::error(msg);
patternConverters.push_back(
LiteralPatternConverter::newInstance(currentLiteral));
formattingInfos.push_back(FormattingInfo::getDefault());
}
else
{
patternConverters.push_back(pc);
formattingInfos.push_back(formattingInfo);
if (currentLiteral.length() > 0)
{
patternConverters.push_back(
LiteralPatternConverter::newInstance(currentLiteral));
formattingInfos.push_back(FormattingInfo::getDefault());
}
}
}
if (!currentLiteral.empty())
{
currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
}
return i;
}