Coverage Report

Created: 2026-07-30 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/patternparser.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/logstring.h>
19
#include <log4cxx/pattern/patternparser.h>
20
#include <log4cxx/pattern/literalpatternconverter.h>
21
#include <log4cxx/helpers/loglog.h>
22
23
using namespace LOG4CXX_NS;
24
using namespace LOG4CXX_NS::pattern;
25
using namespace LOG4CXX_NS::helpers;
26
27
const logchar PatternParser::ESCAPE_CHAR = 0x25; // '%'
28
29
30
/**
31
 * Private constructor.
32
 */
33
PatternParser::PatternParser()
34
0
{
35
0
}
36
37
bool PatternParser::isUnicodeIdentifierStart(logchar ch)
38
741k
{
39
  //
40
  //   greatly simplified version checks if
41
  //     character is USACII alpha or number
42
  //
43
741k
  return (ch >= 0x41 /* 'A' */ && ch <= 0x5A /* 'Z' */) ||
44
654k
    (ch >= 0x61 /* 'a' */ && ch <= 0x7A /* 'z' */) ||
45
182k
    (ch >= 0x30 /* '0' */ && ch <= 0x39 /* '9' */);
46
741k
}
47
48
bool PatternParser::isUnicodeIdentifierPart(logchar ch)
49
604k
{
50
  //
51
  //   greatly simplified version checks if
52
  //     character is USACII alpha or number
53
  //
54
604k
  return isUnicodeIdentifierStart(ch)
55
129k
    || (ch == 0x5F /* '_' */);
56
604k
}
57
58
size_t PatternParser::extractConverter(
59
  logchar lastChar, const LogString& pattern,
60
  LogString::size_type i, LogString& convBuf,
61
  LogString& currentLiteral)
62
136k
{
63
136k
  if (!convBuf.empty())
64
0
  {
65
0
    convBuf.erase(convBuf.begin(), convBuf.end());
66
0
  }
67
68
  // When this method is called, lastChar points to the first character of the
69
  // conversion word. For example:
70
  // For "%hello"     lastChar = 'h'
71
  // For "%-5hello"   lastChar = 'h'
72
  //System.out.println("lastchar is "+lastChar);
73
136k
  if (!isUnicodeIdentifierStart(lastChar))
74
7.11k
  {
75
7.11k
    return i;
76
7.11k
  }
77
78
129k
  convBuf.append(1, lastChar);
79
80
129k
  while (
81
605k
    (i < pattern.length())
82
604k
    && isUnicodeIdentifierPart(pattern[i]))
83
475k
  {
84
475k
    convBuf.append(1, pattern[i]);
85
475k
    currentLiteral.append(1, pattern[i]);
86
87
    //System.out.println("conv buffer is now ["+convBuf+"].");
88
475k
    i++;
89
475k
  }
90
91
129k
  return i;
92
136k
}
93
94
95
size_t PatternParser::extractOptions(const LogString& pattern, LogString::size_type i,
96
  std::vector<LogString>& options)
97
129k
{
98
185k
  while ((i < pattern.length()) && (pattern[i] == 0x7B /* '{' */))
99
56.6k
  {
100
56.6k
    size_t end = pattern.find(0x7D /* '}' */, i);
101
102
56.6k
    if (end == pattern.npos)
103
672
    {
104
672
      break;
105
672
    }
106
107
55.9k
    LogString r(pattern.substr(i + 1, end - i - 1));
108
55.9k
    options.push_back(r);
109
55.9k
    i = end + 1;
110
55.9k
  }
111
112
129k
  return i;
113
129k
}
114
115
#if LOG4CXX_ABI_VERSION <= 15
116
void PatternParser::parse(
117
  const LogString& pattern,
118
  std::vector<PatternConverterPtr>& patternConverters,
119
  std::vector<FormattingInfoPtr>& formattingInfos,
120
  const PatternMap& rules)
121
0
{
122
0
  patternConverters = parse(pattern, rules);
123
0
}
124
#endif
125
126
PatternConverterList PatternParser::parse
127
  ( const LogString&      pattern
128
  , const PatternMap&     rules
129
  )
130
3.45k
{
131
3.45k
  PatternConverterList patternConverters;
132
3.45k
  LogString currentLiteral;
133
134
3.45k
  size_t patternLength = pattern.length();
135
3.45k
  int state = LITERAL_STATE;
136
3.45k
  logchar c;
137
3.45k
  size_t i = 0;
138
3.45k
  int minDigitCount{ 0 }, maxDigitCount{ 0 };
139
3.45k
  auto formattingInfo = FormattingInfo::getDefault();
140
141
691k
  while (i < patternLength)
142
688k
  {
143
688k
    c = pattern[i++];
144
145
688k
    switch (state)
146
688k
    {
147
524k
      case LITERAL_STATE:
148
149
        // In literal state, the last char is always a literal.
150
524k
        if (i == patternLength)
151
415
        {
152
415
          currentLiteral.append(1, c);
153
154
415
          continue;
155
415
        }
156
157
524k
        if (c == ESCAPE_CHAR)
158
139k
        {
159
          // peek at the next char.
160
139k
          if (pattern[i] == ESCAPE_CHAR)
161
1.74k
          {
162
1.74k
            currentLiteral.append(1, c);
163
1.74k
            i++; // move pointer
164
1.74k
          }
165
138k
          else
166
138k
          {
167
138k
            if (!currentLiteral.empty())
168
43.3k
            {
169
43.3k
              patternConverters.push_back(
170
43.3k
                LiteralPatternConverter::newInstance(currentLiteral));
171
43.3k
              currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
172
43.3k
            }
173
174
138k
            currentLiteral.append(1, c); // append %
175
138k
            state = CONVERTER_STATE;
176
138k
            formattingInfo = FormattingInfo::getDefault();
177
138k
          }
178
139k
        }
179
384k
        else
180
384k
        {
181
384k
          currentLiteral.append(1, c);
182
384k
        }
183
184
524k
        break;
185
186
149k
      case CONVERTER_STATE:
187
149k
        currentLiteral.append(1, c);
188
189
149k
        switch (c)
190
149k
        {
191
10.9k
          case 0x2D: // '-'
192
10.9k
            formattingInfo = std::make_shared<FormattingInfo>(
193
10.9k
                  true, formattingInfo->getMinLength(),
194
10.9k
                  formattingInfo->getMaxLength());
195
196
10.9k
            break;
197
198
3.35k
          case 0x2E: // '.'
199
3.35k
            state = DOT_STATE;
200
201
3.35k
            break;
202
203
134k
          default:
204
205
134k
            if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
206
4.92k
            {
207
4.92k
              formattingInfo = std::make_shared<FormattingInfo>(
208
4.92k
                    formattingInfo->isLeftAligned(), c - 0x30 /* '0' */,
209
4.92k
                    formattingInfo->getMaxLength());
210
4.92k
              state = MIN_STATE;
211
4.92k
              minDigitCount = 1;
212
4.92k
            }
213
129k
            else
214
129k
            {
215
129k
              i = finalizeConverter(
216
129k
                  c, pattern, i, currentLiteral, formattingInfo,
217
129k
                  rules, patternConverters);
218
219
              // Next pattern is assumed to be a literal.
220
129k
              state = LITERAL_STATE;
221
129k
              formattingInfo = FormattingInfo::getDefault();
222
223
129k
              if (!currentLiteral.empty())
224
0
              {
225
0
                currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
226
0
              }
227
129k
            }
228
149k
        } // switch
229
230
149k
        break;
231
232
149k
      case MIN_STATE:
233
6.07k
        currentLiteral.append(1, c);
234
235
6.07k
        if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */) && minDigitCount < 3)
236
1.18k
        {
237
1.18k
          formattingInfo = std::make_shared<FormattingInfo>(
238
1.18k
                formattingInfo->isLeftAligned(),
239
1.18k
                (formattingInfo->getMinLength() * 10) + (c - 0x30 /* '0' */),
240
1.18k
                formattingInfo->getMaxLength());
241
1.18k
          ++minDigitCount;
242
1.18k
        }
243
4.88k
        else if (c == 0x2E /* '.' */)
244
734
        {
245
734
          state = DOT_STATE;
246
734
        }
247
4.14k
        else
248
4.14k
        {
249
4.14k
          i = finalizeConverter(
250
4.14k
              c, pattern, i, currentLiteral, formattingInfo,
251
4.14k
              rules, patternConverters);
252
4.14k
          state = LITERAL_STATE;
253
4.14k
          formattingInfo = FormattingInfo::getDefault();
254
255
4.14k
          if (!currentLiteral.empty())
256
0
          {
257
0
            currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
258
0
          }
259
4.14k
        }
260
261
6.07k
        break;
262
263
4.07k
      case DOT_STATE:
264
4.07k
        currentLiteral.append(1, c);
265
266
4.07k
        if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
267
3.04k
        {
268
3.04k
          formattingInfo = std::make_shared<FormattingInfo>(
269
3.04k
                formattingInfo->isLeftAligned(), formattingInfo->getMinLength(),
270
3.04k
                c - 0x30 /* '0' */);
271
3.04k
          state = MAX_STATE;
272
3.04k
          maxDigitCount = 1;
273
3.04k
        }
274
1.02k
        else
275
1.02k
        {
276
1.02k
          LogLog::error(LOG4CXX_STR("Error in pattern, was expecting digit."));
277
278
1.02k
          state = LITERAL_STATE;
279
1.02k
        }
280
281
4.07k
        break;
282
283
4.19k
      case MAX_STATE:
284
4.19k
        currentLiteral.append(1, c);
285
286
4.19k
        if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */) && maxDigitCount < 3)
287
1.18k
        {
288
1.18k
          formattingInfo = std::make_shared<FormattingInfo>(
289
1.18k
                formattingInfo->isLeftAligned(), formattingInfo->getMinLength(),
290
1.18k
                (formattingInfo->getMaxLength() * 10) + (c - 0x30 /* '0' */));
291
1.18k
          ++maxDigitCount;
292
1.18k
        }
293
3.00k
        else
294
3.00k
        {
295
3.00k
          i = finalizeConverter(
296
3.00k
              c, pattern, i, currentLiteral, formattingInfo,
297
3.00k
              rules, patternConverters);
298
3.00k
          state = LITERAL_STATE;
299
3.00k
          formattingInfo = FormattingInfo::getDefault();
300
301
3.00k
          if (!currentLiteral.empty())
302
0
          {
303
0
            currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
304
0
          }
305
3.00k
        }
306
307
4.19k
        break;
308
688k
    } // switch
309
688k
  }
310
311
  // while
312
3.45k
  if (currentLiteral.length() != 0)
313
563
  {
314
563
    patternConverters.push_back(
315
563
      LiteralPatternConverter::newInstance(currentLiteral));
316
563
  }
317
3.45k
  return patternConverters;
318
3.45k
}
319
320
321
PatternConverterPtr PatternParser::createConverter(
322
  const LogString& converterId,
323
  LogString& currentLiteral,
324
  const PatternMap& rules,
325
  std::vector<LogString>& options)
326
129k
{
327
328
129k
  LogString converterName(converterId);
329
330
594k
  for (size_t i = converterId.length(); i > 0; i--)
331
588k
  {
332
588k
    converterName = converterName.substr(0, i);
333
588k
    PatternMap::const_iterator iter = rules.find(converterName);
334
335
588k
    if (iter != rules.end())
336
123k
    {
337
123k
      currentLiteral.erase(currentLiteral.begin(),
338
123k
        currentLiteral.end() - (converterId.length() - i));
339
123k
      return (iter->second)(options);
340
123k
    }
341
588k
  }
342
343
6.21k
  LogLog::error(LogString(LOG4CXX_STR("Unrecognized format specifier ")) + converterId);
344
345
6.21k
  return PatternConverterPtr();
346
129k
}
347
348
size_t PatternParser::finalizeConverter(
349
  logchar c, const LogString& pattern, size_t i,
350
  LogString& currentLiteral, const FormattingInfoPtr& formattingInfo,
351
  const PatternMap&  rules,
352
  PatternConverterList& patternConverters)
353
136k
{
354
136k
  LogString convBuf;
355
136k
  i = extractConverter(c, pattern, i, convBuf, currentLiteral);
356
357
136k
  if (convBuf.empty())
358
7.11k
  {
359
7.11k
    LogLog::error(LOG4CXX_STR("Empty conversion specifier"));
360
7.11k
    patternConverters.push_back(
361
7.11k
      LiteralPatternConverter::newInstance(currentLiteral));
362
7.11k
  }
363
129k
  else
364
129k
  {
365
129k
    LogString converterId(convBuf);
366
367
129k
    std::vector<LogString> options;
368
129k
    i = extractOptions(pattern, i, options);
369
370
129k
    PatternConverterPtr pc(
371
129k
      createConverter(
372
129k
        converterId, currentLiteral, rules, options));
373
374
129k
    if (pc == NULL)
375
6.21k
    {
376
6.21k
      LogString msg(LOG4CXX_STR("Unrecognized conversion specifier ["));
377
6.21k
      msg.append(converterId);
378
6.21k
      msg.append(LOG4CXX_STR("] in conversion pattern."));
379
6.21k
      LogLog::error(msg);
380
6.21k
      patternConverters.push_back(
381
6.21k
        LiteralPatternConverter::newInstance(currentLiteral));
382
6.21k
    }
383
123k
    else
384
123k
    {
385
123k
      patternConverters.push_back(pc);
386
123k
      pc->setFormattingInfo(formattingInfo);
387
388
123k
      if (currentLiteral.length() > 0)
389
37.2k
      {
390
37.2k
        patternConverters.push_back(
391
37.2k
          LiteralPatternConverter::newInstance(currentLiteral));
392
37.2k
      }
393
123k
    }
394
129k
  }
395
396
136k
  if (!currentLiteral.empty())
397
50.6k
  {
398
50.6k
    currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
399
50.6k
  }
400
401
136k
  return i;
402
136k
}