| /* |
| * 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; |
| } |