xref: /aosp_15_r20/external/pdfium/xfa/fxfa/parser/cxfa_localevalue.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "xfa/fxfa/parser/cxfa_localevalue.h"
8 
9 #include <wchar.h>
10 
11 #include <memory>
12 #include <utility>
13 #include <vector>
14 
15 #include "core/fxcrt/cfx_datetime.h"
16 #include "core/fxcrt/fx_extension.h"
17 #include "third_party/base/check.h"
18 #include "third_party/base/containers/span.h"
19 #include "xfa/fgas/crt/cfgas_stringformatter.h"
20 #include "xfa/fxfa/parser/cxfa_document.h"
21 #include "xfa/fxfa/parser/cxfa_localemgr.h"
22 #include "xfa/fxfa/parser/xfa_utils.h"
23 
24 namespace {
25 
ValueCategory(CFGAS_StringFormatter::Category eCategory,CXFA_LocaleValue::ValueType eValueType)26 CFGAS_StringFormatter::Category ValueCategory(
27     CFGAS_StringFormatter::Category eCategory,
28     CXFA_LocaleValue::ValueType eValueType) {
29   if (eCategory != CFGAS_StringFormatter::Category::kUnknown)
30     return eCategory;
31 
32   switch (eValueType) {
33     case CXFA_LocaleValue::ValueType::kBoolean:
34     case CXFA_LocaleValue::ValueType::kInteger:
35     case CXFA_LocaleValue::ValueType::kDecimal:
36     case CXFA_LocaleValue::ValueType::kFloat:
37       return CFGAS_StringFormatter::Category::kNum;
38     case CXFA_LocaleValue::ValueType::kText:
39       return CFGAS_StringFormatter::Category::kText;
40     case CXFA_LocaleValue::ValueType::kDate:
41       return CFGAS_StringFormatter::Category::kDate;
42     case CXFA_LocaleValue::ValueType::kTime:
43       return CFGAS_StringFormatter::Category::kTime;
44     case CXFA_LocaleValue::ValueType::kDateTime:
45       return CFGAS_StringFormatter::Category::kDateTime;
46     default:
47       return CFGAS_StringFormatter::Category::kUnknown;
48   }
49 }
50 
ValueSplitDateTime(const WideString & wsDateTime,WideString & wsDate,WideString & wsTime)51 bool ValueSplitDateTime(const WideString& wsDateTime,
52                         WideString& wsDate,
53                         WideString& wsTime) {
54   wsDate.clear();
55   wsTime.clear();
56   if (wsDateTime.IsEmpty())
57     return false;
58 
59   auto nSplitIndex = wsDateTime.Find('T');
60   if (!nSplitIndex.has_value())
61     nSplitIndex = wsDateTime.Find(' ');
62   if (!nSplitIndex.has_value())
63     return false;
64 
65   wsDate = wsDateTime.First(nSplitIndex.value());
66   wsTime = wsDateTime.Last(wsDateTime.GetLength() - nSplitIndex.value() - 1);
67   return true;
68 }
69 
70 class ScopedLocale {
71   CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
72 
73  public:
ScopedLocale(CXFA_LocaleMgr * pLocaleMgr,GCedLocaleIface * pNewLocale)74   ScopedLocale(CXFA_LocaleMgr* pLocaleMgr, GCedLocaleIface* pNewLocale)
75       : m_pLocaleMgr(pLocaleMgr),
76         m_pNewLocale(pNewLocale),
77         m_pOrigLocale(pNewLocale ? m_pLocaleMgr->GetDefLocale() : nullptr) {
78     if (m_pNewLocale)
79       m_pLocaleMgr->SetDefLocale(pNewLocale);
80   }
81 
~ScopedLocale()82   ~ScopedLocale() {
83     if (m_pNewLocale)
84       m_pLocaleMgr->SetDefLocale(m_pOrigLocale);
85   }
86 
87   ScopedLocale(const ScopedLocale& that) = delete;
88   ScopedLocale& operator=(const ScopedLocale& that) = delete;
89 
90  private:
91   UnownedPtr<CXFA_LocaleMgr> const m_pLocaleMgr;    // Ok, stack-only.
92   UnownedPtr<GCedLocaleIface> const m_pNewLocale;   // Ok, stack-only.
93   UnownedPtr<GCedLocaleIface> const m_pOrigLocale;  // Ok, stack-only.
94 };
95 
96 }  // namespace
97 
98 CXFA_LocaleValue::CXFA_LocaleValue() = default;
99 
CXFA_LocaleValue(ValueType eType,CXFA_LocaleMgr * pLocaleMgr)100 CXFA_LocaleValue::CXFA_LocaleValue(ValueType eType, CXFA_LocaleMgr* pLocaleMgr)
101     : m_pLocaleMgr(pLocaleMgr),
102       m_eType(eType),
103       m_bValid(m_eType != ValueType::kNull) {}
104 
CXFA_LocaleValue(ValueType eType,const WideString & wsValue,CXFA_LocaleMgr * pLocaleMgr)105 CXFA_LocaleValue::CXFA_LocaleValue(ValueType eType,
106                                    const WideString& wsValue,
107                                    CXFA_LocaleMgr* pLocaleMgr)
108     : m_pLocaleMgr(pLocaleMgr),
109       m_wsValue(wsValue),
110       m_eType(eType),
111       m_bValid(ValidateCanonicalValue(wsValue, eType)) {}
112 
CXFA_LocaleValue(ValueType eType,const WideString & wsValue,const WideString & wsFormat,GCedLocaleIface * pLocale,CXFA_LocaleMgr * pLocaleMgr)113 CXFA_LocaleValue::CXFA_LocaleValue(ValueType eType,
114                                    const WideString& wsValue,
115                                    const WideString& wsFormat,
116                                    GCedLocaleIface* pLocale,
117                                    CXFA_LocaleMgr* pLocaleMgr)
118     : m_pLocaleMgr(pLocaleMgr),
119       m_eType(eType),
120       m_bValid(ParsePatternValue(wsValue, wsFormat, pLocale)) {}
121 
122 CXFA_LocaleValue::CXFA_LocaleValue(const CXFA_LocaleValue& that) = default;
123 
124 CXFA_LocaleValue& CXFA_LocaleValue::operator=(const CXFA_LocaleValue& that) =
125     default;
126 
127 CXFA_LocaleValue::~CXFA_LocaleValue() = default;
128 
ValidateValue(const WideString & wsValue,const WideString & wsPattern,GCedLocaleIface * pLocale,WideString * pMatchFormat)129 bool CXFA_LocaleValue::ValidateValue(const WideString& wsValue,
130                                      const WideString& wsPattern,
131                                      GCedLocaleIface* pLocale,
132                                      WideString* pMatchFormat) {
133   if (!m_pLocaleMgr)
134     return false;
135 
136   ScopedLocale scoped_locale(m_pLocaleMgr, pLocale);
137   std::vector<WideString> wsPatterns =
138       CFGAS_StringFormatter::SplitOnBars(wsPattern);
139 
140   WideString wsOutput;
141   bool bRet = false;
142   size_t i = 0;
143   for (; !bRet && i < wsPatterns.size(); i++) {
144     const WideString& wsFormat = wsPatterns[i];
145     auto pFormat = std::make_unique<CFGAS_StringFormatter>(wsFormat);
146     switch (ValueCategory(pFormat->GetCategory(), m_eType)) {
147       case CFGAS_StringFormatter::Category::kNull:
148         bRet = pFormat->ParseNull(wsValue);
149         if (!bRet)
150           bRet = wsValue.IsEmpty();
151         break;
152       case CFGAS_StringFormatter::Category::kZero:
153         bRet = pFormat->ParseZero(wsValue);
154         if (!bRet)
155           bRet = wsValue.EqualsASCII("0");
156         break;
157       case CFGAS_StringFormatter::Category::kNum: {
158         WideString fNum;
159         bRet = pFormat->ParseNum(m_pLocaleMgr, wsValue, &fNum);
160         if (!bRet)
161           bRet = pFormat->FormatNum(m_pLocaleMgr, wsValue, &wsOutput);
162         break;
163       }
164       case CFGAS_StringFormatter::Category::kText:
165         bRet = pFormat->ParseText(wsValue, &wsOutput);
166         wsOutput.clear();
167         if (!bRet)
168           bRet = pFormat->FormatText(wsValue, &wsOutput);
169         break;
170       case CFGAS_StringFormatter::Category::kDate: {
171         CFX_DateTime dt;
172         bRet = ValidateCanonicalDate(wsValue, &dt);
173         if (!bRet) {
174           bRet = pFormat->ParseDateTime(
175               m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kDate,
176               &dt);
177           if (!bRet) {
178             bRet = pFormat->FormatDateTime(
179                 m_pLocaleMgr, wsValue,
180                 CFGAS_StringFormatter::DateTimeType::kDate, &wsOutput);
181           }
182         }
183         break;
184       }
185       case CFGAS_StringFormatter::Category::kTime: {
186         CFX_DateTime dt;
187         bRet = pFormat->ParseDateTime(
188             m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kTime,
189             &dt);
190         if (!bRet) {
191           bRet = pFormat->FormatDateTime(
192               m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kTime,
193               &wsOutput);
194         }
195         break;
196       }
197       case CFGAS_StringFormatter::Category::kDateTime: {
198         CFX_DateTime dt;
199         bRet = pFormat->ParseDateTime(
200             m_pLocaleMgr, wsValue,
201             CFGAS_StringFormatter::DateTimeType::kDateTime, &dt);
202         if (!bRet) {
203           bRet = pFormat->FormatDateTime(
204               m_pLocaleMgr, wsValue,
205               CFGAS_StringFormatter::DateTimeType::kDateTime, &wsOutput);
206         }
207         break;
208       }
209       default:
210         bRet = false;
211         break;
212     }
213   }
214   if (bRet && pMatchFormat)
215     *pMatchFormat = wsPatterns[i - 1];
216   return bRet;
217 }
218 
GetDoubleNum() const219 double CXFA_LocaleValue::GetDoubleNum() const {
220   if (!m_bValid || (m_eType != CXFA_LocaleValue::ValueType::kBoolean &&
221                     m_eType != CXFA_LocaleValue::ValueType::kInteger &&
222                     m_eType != CXFA_LocaleValue::ValueType::kDecimal &&
223                     m_eType != CXFA_LocaleValue::ValueType::kFloat)) {
224     return 0;
225   }
226 
227   return wcstod(m_wsValue.c_str(), nullptr);
228 }
229 
GetDate() const230 CFX_DateTime CXFA_LocaleValue::GetDate() const {
231   if (!m_bValid || m_eType != CXFA_LocaleValue::ValueType::kDate)
232     return CFX_DateTime();
233 
234   CFX_DateTime dt;
235   FX_DateFromCanonical(m_wsValue.span(), &dt);
236   return dt;
237 }
238 
GetTime() const239 CFX_DateTime CXFA_LocaleValue::GetTime() const {
240   if (!m_bValid || m_eType != CXFA_LocaleValue::ValueType::kTime)
241     return CFX_DateTime();
242 
243   CFX_DateTime dt;
244   FX_TimeFromCanonical(m_pLocaleMgr->GetDefLocale(), m_wsValue.span(), &dt);
245   return dt;
246 }
247 
SetDate(const CFX_DateTime & d)248 bool CXFA_LocaleValue::SetDate(const CFX_DateTime& d) {
249   m_eType = CXFA_LocaleValue::ValueType::kDate;
250   m_wsValue = WideString::Format(L"%04d-%02d-%02d", d.GetYear(), d.GetMonth(),
251                                  d.GetDay());
252   return true;
253 }
254 
SetTime(const CFX_DateTime & t)255 bool CXFA_LocaleValue::SetTime(const CFX_DateTime& t) {
256   m_eType = CXFA_LocaleValue::ValueType::kTime;
257   m_wsValue = WideString::Format(L"%02d:%02d:%02d", t.GetHour(), t.GetMinute(),
258                                  t.GetSecond());
259   if (t.GetMillisecond() > 0)
260     m_wsValue += WideString::Format(L"%:03d", t.GetMillisecond());
261   return true;
262 }
263 
SetDateTime(const CFX_DateTime & dt)264 bool CXFA_LocaleValue::SetDateTime(const CFX_DateTime& dt) {
265   m_eType = CXFA_LocaleValue::ValueType::kDateTime;
266   m_wsValue = WideString::Format(L"%04d-%02d-%02dT%02d:%02d:%02d", dt.GetYear(),
267                                  dt.GetMonth(), dt.GetDay(), dt.GetHour(),
268                                  dt.GetMinute(), dt.GetSecond());
269   if (dt.GetMillisecond() > 0)
270     m_wsValue += WideString::Format(L"%:03d", dt.GetMillisecond());
271   return true;
272 }
273 
FormatPatterns(WideString & wsResult,const WideString & wsFormat,GCedLocaleIface * pLocale,XFA_ValuePicture eValueType) const274 bool CXFA_LocaleValue::FormatPatterns(WideString& wsResult,
275                                       const WideString& wsFormat,
276                                       GCedLocaleIface* pLocale,
277                                       XFA_ValuePicture eValueType) const {
278   wsResult.clear();
279   for (const auto& pattern : CFGAS_StringFormatter::SplitOnBars(wsFormat)) {
280     if (FormatSinglePattern(wsResult, pattern, pLocale, eValueType))
281       return true;
282   }
283   return false;
284 }
285 
FormatSinglePattern(WideString & wsResult,const WideString & wsFormat,GCedLocaleIface * pLocale,XFA_ValuePicture eValueType) const286 bool CXFA_LocaleValue::FormatSinglePattern(WideString& wsResult,
287                                            const WideString& wsFormat,
288                                            GCedLocaleIface* pLocale,
289                                            XFA_ValuePicture eValueType) const {
290   if (!m_pLocaleMgr)
291     return false;
292 
293   ScopedLocale scoped_locale(m_pLocaleMgr, pLocale);
294   wsResult.clear();
295 
296   bool bRet = false;
297   auto pFormat = std::make_unique<CFGAS_StringFormatter>(wsFormat);
298   CFGAS_StringFormatter::Category eCategory =
299       ValueCategory(pFormat->GetCategory(), m_eType);
300   switch (eCategory) {
301     case CFGAS_StringFormatter::Category::kNull:
302       if (m_wsValue.IsEmpty())
303         bRet = pFormat->FormatNull(&wsResult);
304       break;
305     case CFGAS_StringFormatter::Category::kZero:
306       if (m_wsValue.EqualsASCII("0"))
307         bRet = pFormat->FormatZero(&wsResult);
308       break;
309     case CFGAS_StringFormatter::Category::kNum:
310       bRet = pFormat->FormatNum(m_pLocaleMgr, m_wsValue, &wsResult);
311       break;
312     case CFGAS_StringFormatter::Category::kText:
313       bRet = pFormat->FormatText(m_wsValue, &wsResult);
314       break;
315     case CFGAS_StringFormatter::Category::kDate:
316       bRet = pFormat->FormatDateTime(m_pLocaleMgr, m_wsValue,
317                                      CFGAS_StringFormatter::DateTimeType::kDate,
318                                      &wsResult);
319       break;
320     case CFGAS_StringFormatter::Category::kTime:
321       bRet = pFormat->FormatDateTime(m_pLocaleMgr, m_wsValue,
322                                      CFGAS_StringFormatter::DateTimeType::kTime,
323                                      &wsResult);
324       break;
325     case CFGAS_StringFormatter::Category::kDateTime:
326       bRet = pFormat->FormatDateTime(
327           m_pLocaleMgr, m_wsValue,
328           CFGAS_StringFormatter::DateTimeType::kDateTime, &wsResult);
329       break;
330     default:
331       wsResult = m_wsValue;
332       bRet = true;
333   }
334   if (!bRet && (eCategory != CFGAS_StringFormatter::Category::kNum ||
335                 eValueType != XFA_ValuePicture::kDisplay)) {
336     wsResult = m_wsValue;
337   }
338 
339   return bRet;
340 }
341 
ValidateCanonicalValue(const WideString & wsValue,ValueType eType)342 bool CXFA_LocaleValue::ValidateCanonicalValue(const WideString& wsValue,
343                                               ValueType eType) {
344   if (wsValue.IsEmpty())
345     return true;
346 
347   CFX_DateTime dt;
348   switch (eType) {
349     case ValueType::kDate: {
350       if (ValidateCanonicalDate(wsValue, &dt))
351         return true;
352 
353       WideString wsDate;
354       WideString wsTime;
355       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
356           ValidateCanonicalDate(wsDate, &dt)) {
357         return true;
358       }
359       return false;
360     }
361     case ValueType::kTime: {
362       if (ValidateCanonicalTime(wsValue))
363         return true;
364 
365       WideString wsDate;
366       WideString wsTime;
367       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
368           ValidateCanonicalTime(wsTime)) {
369         return true;
370       }
371       return false;
372     }
373     case ValueType::kDateTime: {
374       WideString wsDate, wsTime;
375       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
376           ValidateCanonicalDate(wsDate, &dt) && ValidateCanonicalTime(wsTime)) {
377         return true;
378       }
379     } break;
380     default: {
381       break;
382     }
383   }
384   return true;
385 }
386 
ValidateCanonicalDate(const WideString & wsDate,CFX_DateTime * unDate)387 bool CXFA_LocaleValue::ValidateCanonicalDate(const WideString& wsDate,
388                                              CFX_DateTime* unDate) {
389   static const uint8_t LastDay[12] = {31, 28, 31, 30, 31, 30,
390                                       31, 31, 30, 31, 30, 31};
391   static const uint16_t wCountY = 4;
392   static const uint16_t wCountM = 2;
393   static const uint16_t wCountD = 2;
394   pdfium::span<const wchar_t> spDate = wsDate.span();
395   if (spDate.size() < wCountY ||
396       spDate.size() > wCountY + wCountM + wCountD + 2) {
397     return false;
398   }
399   const bool bSymbol = wsDate.Contains(0x2D);
400   uint16_t wYear = 0;
401   uint16_t wMonth = 0;
402   uint16_t wDay = 0;
403   size_t nIndex = 0;
404   size_t nStart = 0;
405   while (nIndex < wCountY && spDate[nIndex] != '\0') {
406     if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
407       return false;
408 
409     wYear = (spDate[nIndex] - '0') + wYear * 10;
410     nIndex++;
411   }
412   if (bSymbol) {
413     if (nIndex >= spDate.size() || spDate[nIndex] != 0x2D)
414       return false;
415     nIndex++;
416   }
417 
418   nStart = nIndex;
419   while (nIndex < spDate.size() && spDate[nIndex] != '\0' &&
420          nIndex - nStart < wCountM) {
421     if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
422       return false;
423 
424     wMonth = (spDate[nIndex] - '0') + wMonth * 10;
425     nIndex++;
426   }
427   if (bSymbol) {
428     if (nIndex >= spDate.size() || spDate[nIndex] != 0x2D)
429       return false;
430     nIndex++;
431   }
432 
433   nStart = nIndex;
434   while (nIndex < spDate.size() && spDate[nIndex] != '\0' &&
435          nIndex - nStart < wCountD) {
436     if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
437       return false;
438 
439     wDay = (spDate[nIndex] - '0') + wDay * 10;
440     nIndex++;
441   }
442   if (nIndex != spDate.size())
443     return false;
444   if (wYear < 1900 || wYear > 2029)
445     return false;
446   if (wMonth < 1 || wMonth > 12)
447     return wMonth == 0 && spDate.size() == wCountY;
448   if (wDay < 1)
449     return wDay == 0 && spDate.size() == wCountY + wCountM;
450   if (wMonth == 2) {
451     if (wYear % 400 == 0 || (wYear % 100 != 0 && wYear % 4 == 0)) {
452       if (wDay > 29)
453         return false;
454     } else if (wDay > 28) {
455       return false;
456     }
457   } else if (wDay > LastDay[wMonth - 1]) {
458     return false;
459   }
460 
461   unDate->SetDate(wYear, static_cast<uint8_t>(wMonth),
462                   static_cast<uint8_t>(wDay));
463   return true;
464 }
465 
ValidateCanonicalTime(const WideString & wsTime)466 bool CXFA_LocaleValue::ValidateCanonicalTime(const WideString& wsTime) {
467   pdfium::span<const wchar_t> spTime = wsTime.span();
468   if (spTime.size() < 2)
469     return false;
470 
471   const uint16_t wCountH = 2;
472   const uint16_t wCountM = 2;
473   const uint16_t wCountS = 2;
474   const uint16_t wCountF = 3;
475   const bool bSymbol = wsTime.Contains(':');
476   uint16_t wHour = 0;
477   uint16_t wMinute = 0;
478   uint16_t wSecond = 0;
479   uint16_t wFraction = 0;
480   size_t nIndex = 0;
481   size_t nStart = 0;
482   while (nIndex - nStart < wCountH && spTime[nIndex]) {
483     if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
484       return false;
485     wHour = spTime[nIndex] - '0' + wHour * 10;
486     nIndex++;
487   }
488   if (bSymbol) {
489     if (nIndex < spTime.size() && spTime[nIndex] != ':')
490       return false;
491     nIndex++;
492   }
493 
494   nStart = nIndex;
495   while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
496          nIndex - nStart < wCountM) {
497     if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
498       return false;
499     wMinute = spTime[nIndex] - '0' + wMinute * 10;
500     nIndex++;
501   }
502   if (bSymbol) {
503     if (nIndex >= spTime.size() || spTime[nIndex] != ':')
504       return false;
505     nIndex++;
506   }
507   nStart = nIndex;
508   while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
509          nIndex - nStart < wCountS) {
510     if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
511       return false;
512     wSecond = spTime[nIndex] - '0' + wSecond * 10;
513     nIndex++;
514   }
515   auto pos = wsTime.Find('.');
516   if (pos.has_value() && pos.value() != 0) {
517     if (nIndex >= spTime.size() || spTime[nIndex] != '.')
518       return false;
519     nIndex++;
520     nStart = nIndex;
521     while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
522            nIndex - nStart < wCountF) {
523       if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
524         return false;
525       wFraction = spTime[nIndex] - '0' + wFraction * 10;
526       nIndex++;
527     }
528   }
529   if (nIndex < spTime.size()) {
530     if (spTime[nIndex] == 'Z') {
531       nIndex++;
532     } else if (spTime[nIndex] == '-' || spTime[nIndex] == '+') {
533       int16_t nOffsetH = 0;
534       int16_t nOffsetM = 0;
535       nIndex++;
536       nStart = nIndex;
537       while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
538              nIndex - nStart < wCountH) {
539         if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
540           return false;
541         nOffsetH = spTime[nIndex] - '0' + nOffsetH * 10;
542         nIndex++;
543       }
544       if (bSymbol) {
545         if (nIndex >= spTime.size() || spTime[nIndex] != ':')
546           return false;
547         nIndex++;
548       }
549       nStart = nIndex;
550       while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
551              nIndex - nStart < wCountM) {
552         if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
553           return false;
554         nOffsetM = spTime[nIndex] - '0' + nOffsetM * 10;
555         nIndex++;
556       }
557       if (nOffsetH > 12 || nOffsetM >= 60)
558         return false;
559     }
560   }
561   return nIndex == spTime.size() && wHour < 24 && wMinute < 60 &&
562          wSecond < 60 && wFraction <= 999;
563 }
564 
ParsePatternValue(const WideString & wsValue,const WideString & wsPattern,GCedLocaleIface * pLocale)565 bool CXFA_LocaleValue::ParsePatternValue(const WideString& wsValue,
566                                          const WideString& wsPattern,
567                                          GCedLocaleIface* pLocale) {
568   if (!m_pLocaleMgr)
569     return false;
570 
571   std::vector<WideString> wsPatterns =
572       CFGAS_StringFormatter::SplitOnBars(wsPattern);
573 
574   ScopedLocale scoped_locale(m_pLocaleMgr, pLocale);
575   bool bRet = false;
576   for (size_t i = 0; !bRet && i < wsPatterns.size(); i++) {
577     const WideString& wsFormat = wsPatterns[i];
578     auto pFormat = std::make_unique<CFGAS_StringFormatter>(wsFormat);
579     switch (ValueCategory(pFormat->GetCategory(), m_eType)) {
580       case CFGAS_StringFormatter::Category::kNull:
581         bRet = pFormat->ParseNull(wsValue);
582         if (bRet)
583           m_wsValue.clear();
584         break;
585       case CFGAS_StringFormatter::Category::kZero:
586         bRet = pFormat->ParseZero(wsValue);
587         if (bRet)
588           m_wsValue = L"0";
589         break;
590       case CFGAS_StringFormatter::Category::kNum: {
591         WideString fNum;
592         bRet = pFormat->ParseNum(m_pLocaleMgr, wsValue, &fNum);
593         if (bRet)
594           m_wsValue = std::move(fNum);
595         break;
596       }
597       case CFGAS_StringFormatter::Category::kText:
598         bRet = pFormat->ParseText(wsValue, &m_wsValue);
599         break;
600       case CFGAS_StringFormatter::Category::kDate: {
601         CFX_DateTime dt;
602         bRet = ValidateCanonicalDate(wsValue, &dt);
603         if (!bRet) {
604           bRet = pFormat->ParseDateTime(
605               m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kDate,
606               &dt);
607         }
608         if (bRet)
609           SetDate(dt);
610         break;
611       }
612       case CFGAS_StringFormatter::Category::kTime: {
613         CFX_DateTime dt;
614         bRet = pFormat->ParseDateTime(
615             m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kTime,
616             &dt);
617         if (bRet)
618           SetTime(dt);
619         break;
620       }
621       case CFGAS_StringFormatter::Category::kDateTime: {
622         CFX_DateTime dt;
623         bRet = pFormat->ParseDateTime(
624             m_pLocaleMgr, wsValue,
625             CFGAS_StringFormatter::DateTimeType::kDateTime, &dt);
626         if (bRet)
627           SetDateTime(dt);
628         break;
629       }
630       default:
631         m_wsValue = wsValue;
632         bRet = true;
633         break;
634     }
635   }
636   if (!bRet)
637     m_wsValue = wsValue;
638 
639   return bRet;
640 }
641 
GetNumericFormat(WideString & wsFormat,int32_t nIntLen,int32_t nDecLen)642 void CXFA_LocaleValue::GetNumericFormat(WideString& wsFormat,
643                                         int32_t nIntLen,
644                                         int32_t nDecLen) {
645   DCHECK(wsFormat.IsEmpty());
646   DCHECK(nIntLen >= -1);
647   DCHECK(nDecLen >= -1);
648 
649   int32_t nTotalLen = (nIntLen >= 0 ? nIntLen : 2) + 1 +
650                       (nDecLen >= 0 ? nDecLen : 2) + (nDecLen == 0 ? 0 : 1);
651   {
652     // Span's lifetime must end before ReleaseBuffer() below.
653     pdfium::span<wchar_t> lpBuf = wsFormat.GetBuffer(nTotalLen);
654     int32_t nPos = 0;
655     lpBuf[nPos++] = L's';
656 
657     if (nIntLen == -1) {
658       lpBuf[nPos++] = L'z';
659       lpBuf[nPos++] = L'*';
660     } else {
661       while (nIntLen) {
662         lpBuf[nPos++] = L'z';
663         nIntLen--;
664       }
665     }
666     if (nDecLen != 0) {
667       lpBuf[nPos++] = L'.';
668     }
669     if (nDecLen == -1) {
670       lpBuf[nPos++] = L'z';
671       lpBuf[nPos++] = L'*';
672     } else {
673       while (nDecLen) {
674         lpBuf[nPos++] = L'z';
675         nDecLen--;
676       }
677     }
678   }
679   wsFormat.ReleaseBuffer(nTotalLen);
680 }
681 
ValidateNumericTemp(const WideString & wsNumeric,const WideString & wsFormat,GCedLocaleIface * pLocale)682 bool CXFA_LocaleValue::ValidateNumericTemp(const WideString& wsNumeric,
683                                            const WideString& wsFormat,
684                                            GCedLocaleIface* pLocale) {
685   if (wsFormat.IsEmpty() || wsNumeric.IsEmpty())
686     return true;
687 
688   pdfium::span<const wchar_t> spNum = wsNumeric.span();
689   pdfium::span<const wchar_t> spFmt = wsFormat.span();
690   size_t n = 0;
691   size_t nf = 0;
692   wchar_t c = spNum[n];
693   wchar_t cf = spFmt[nf];
694   if (cf == L's') {
695     if (c == L'-' || c == L'+')
696       ++n;
697     ++nf;
698   }
699 
700   bool bLimit = true;
701   size_t nCount = wsNumeric.GetLength();
702   size_t nCountFmt = wsFormat.GetLength();
703   while (n < nCount && (!bLimit || nf < nCountFmt) &&
704          FXSYS_IsDecimalDigit(c = spNum[n])) {
705     if (bLimit) {
706       if ((cf = spFmt[nf]) == L'*')
707         bLimit = false;
708       else if (cf == L'z')
709         nf++;
710       else
711         return false;
712     }
713     n++;
714   }
715   if (n == nCount)
716     return true;
717   if (nf == nCountFmt)
718     return false;
719 
720   while (nf < nCountFmt && (cf = spFmt[nf]) != L'.') {
721     DCHECK(cf == L'z' || cf == L'*');
722     ++nf;
723   }
724 
725   WideString wsDecimalSymbol;
726   if (pLocale)
727     wsDecimalSymbol = pLocale->GetDecimalSymbol();
728   else
729     wsDecimalSymbol = WideString(L'.');
730 
731   if (spFmt[nf] != L'.')
732     return false;
733   if (wsDecimalSymbol != WideStringView(c) && c != L'.')
734     return false;
735 
736   ++nf;
737   ++n;
738   bLimit = true;
739   while (n < nCount && (!bLimit || nf < nCountFmt) &&
740          FXSYS_IsDecimalDigit(spNum[n])) {
741     if (bLimit) {
742       if ((cf = spFmt[nf]) == L'*')
743         bLimit = false;
744       else if (cf == L'z')
745         nf++;
746       else
747         return false;
748     }
749     n++;
750   }
751   return n == nCount;
752 }
753