| /* | |
| * 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_NS; | |
| using namespace LOG4CXX_NS::pattern; | |
| using namespace LOG4CXX_NS::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; | |
| } | |
| PatternConverterList PatternParser::parse | |
| ( const LogString& pattern | |
| , const PatternMap& rules | |
| ) | |
| { | |
| PatternConverterList patternConverters; | |
| LogString currentLiteral; | |
| size_t patternLength = pattern.length(); | |
| int state = LITERAL_STATE; | |
| logchar c; | |
| size_t i = 0; | |
| int minDigitCount{ 0 }, maxDigitCount{ 0 }; | |
| auto 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)); | |
| 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 = std::make_shared<FormattingInfo>( | |
| true, formattingInfo->getMinLength(), | |
| formattingInfo->getMaxLength()); | |
| break; | |
| case 0x2E: // '.' | |
| state = DOT_STATE; | |
| break; | |
| default: | |
| if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */)) | |
| { | |
| formattingInfo = std::make_shared<FormattingInfo>( | |
| formattingInfo->isLeftAligned(), c - 0x30 /* '0' */, | |
| formattingInfo->getMaxLength()); | |
| state = MIN_STATE; | |
| minDigitCount = 1; | |
| } | |
| else | |
| { | |
| i = finalizeConverter( | |
| c, pattern, i, currentLiteral, formattingInfo, | |
| rules, patternConverters); | |
| // 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' */) && minDigitCount < 3) | |
| { | |
| formattingInfo = std::make_shared<FormattingInfo>( | |
| formattingInfo->isLeftAligned(), | |
| (formattingInfo->getMinLength() * 10) + (c - 0x30 /* '0' */), | |
| formattingInfo->getMaxLength()); | |
| ++minDigitCount; | |
| } | |
| else if (c == 0x2E /* '.' */) | |
| { | |
| state = DOT_STATE; | |
| } | |
| else | |
| { | |
| i = finalizeConverter( | |
| c, pattern, i, currentLiteral, formattingInfo, | |
| rules, patternConverters); | |
| 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 = std::make_shared<FormattingInfo>( | |
| formattingInfo->isLeftAligned(), formattingInfo->getMinLength(), | |
| c - 0x30 /* '0' */); | |
| state = MAX_STATE; | |
| maxDigitCount = 1; | |
| } | |
| 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' */) && maxDigitCount < 3) | |
| { | |
| formattingInfo = std::make_shared<FormattingInfo>( | |
| formattingInfo->isLeftAligned(), formattingInfo->getMinLength(), | |
| (formattingInfo->getMaxLength() * 10) + (c - 0x30 /* '0' */)); | |
| ++maxDigitCount; | |
| } | |
| else | |
| { | |
| i = finalizeConverter( | |
| c, pattern, i, currentLiteral, formattingInfo, | |
| rules, patternConverters); | |
| 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)); | |
| } | |
| return patternConverters; | |
| } | |
| 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); | |
| return PatternConverterPtr(); | |
| } | |
| size_t PatternParser::finalizeConverter( | |
| logchar c, const LogString& pattern, size_t i, | |
| LogString& currentLiteral, const FormattingInfoPtr& formattingInfo, | |
| const PatternMap& rules, | |
| PatternConverterList& patternConverters) | |
| { | |
| 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)); | |
| } | |
| 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)); | |
| } | |
| else | |
| { | |
| patternConverters.push_back(pc); | |
| pc->setFormattingInfo(formattingInfo); | |
| if (currentLiteral.length() > 0) | |
| { | |
| patternConverters.push_back( | |
| LiteralPatternConverter::newInstance(currentLiteral)); | |
| } | |
| } | |
| } | |
| if (!currentLiteral.empty()) | |
| { | |
| currentLiteral.erase(currentLiteral.begin(), currentLiteral.end()); | |
| } | |
| return i; | |
| } |