| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 PDFium Authors. All rights reserved. | |
| 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/src/fxfa/parser/xfa_localevalue.h" | |
| 8 | |
| 9 #include "xfa/src/fxfa/fm2js/xfa_fm2jsapi.h" | |
| 10 #include "xfa/src/fxfa/parser/xfa_docdata.h" | |
| 11 #include "xfa/src/fxfa/parser/xfa_doclayout.h" | |
| 12 #include "xfa/src/fxfa/parser/xfa_document.h" | |
| 13 #include "xfa/src/fxfa/parser/xfa_localemgr.h" | |
| 14 #include "xfa/src/fxfa/parser/xfa_object.h" | |
| 15 #include "xfa/src/fxfa/parser/xfa_parser.h" | |
| 16 #include "xfa/src/fxfa/parser/xfa_script.h" | |
| 17 #include "xfa/src/fxfa/parser/xfa_utils.h" | |
| 18 | |
| 19 static const FX_DOUBLE fraction_scales[] = {0.1, | |
| 20 0.01, | |
| 21 0.001, | |
| 22 0.0001, | |
| 23 0.00001, | |
| 24 0.000001, | |
| 25 0.0000001, | |
| 26 0.00000001, | |
| 27 0.000000001, | |
| 28 0.0000000001, | |
| 29 0.00000000001, | |
| 30 0.000000000001, | |
| 31 0.0000000000001, | |
| 32 0.00000000000001, | |
| 33 0.000000000000001, | |
| 34 0.0000000000000001}; | |
| 35 CXFA_LocaleValue::CXFA_LocaleValue() { | |
| 36 m_dwType = XFA_VT_NULL; | |
| 37 m_bValid = TRUE; | |
| 38 m_pLocaleMgr = NULL; | |
| 39 } | |
| 40 CXFA_LocaleValue::CXFA_LocaleValue(const CXFA_LocaleValue& value) { | |
| 41 m_dwType = XFA_VT_NULL; | |
| 42 m_bValid = TRUE; | |
| 43 m_pLocaleMgr = NULL; | |
| 44 *this = value; | |
| 45 } | |
| 46 CXFA_LocaleValue::CXFA_LocaleValue(FX_DWORD dwType, | |
| 47 CXFA_LocaleMgr* pLocaleMgr) { | |
| 48 m_dwType = dwType; | |
| 49 m_bValid = (m_dwType != XFA_VT_NULL); | |
| 50 m_pLocaleMgr = pLocaleMgr; | |
| 51 } | |
| 52 CXFA_LocaleValue::CXFA_LocaleValue(FX_DWORD dwType, | |
| 53 const CFX_WideString& wsValue, | |
| 54 CXFA_LocaleMgr* pLocaleMgr) { | |
| 55 m_wsValue = wsValue; | |
| 56 m_dwType = dwType; | |
| 57 m_pLocaleMgr = pLocaleMgr; | |
| 58 m_bValid = ValidateCanonicalValue(wsValue, dwType); | |
| 59 } | |
| 60 CXFA_LocaleValue::CXFA_LocaleValue(FX_DWORD dwType, | |
| 61 const CFX_WideString& wsValue, | |
| 62 const CFX_WideString& wsFormat, | |
| 63 IFX_Locale* pLocale, | |
| 64 CXFA_LocaleMgr* pLocaleMgr) { | |
| 65 m_pLocaleMgr = pLocaleMgr; | |
| 66 m_bValid = TRUE; | |
| 67 m_dwType = dwType; | |
| 68 m_bValid = ParsePatternValue(wsValue, wsFormat, pLocale); | |
| 69 } | |
| 70 CXFA_LocaleValue& CXFA_LocaleValue::operator=(const CXFA_LocaleValue& value) { | |
| 71 m_wsValue = value.m_wsValue; | |
| 72 m_dwType = value.m_dwType; | |
| 73 m_bValid = value.m_bValid; | |
| 74 m_pLocaleMgr = value.m_pLocaleMgr; | |
| 75 return *this; | |
| 76 } | |
| 77 CXFA_LocaleValue::~CXFA_LocaleValue() {} | |
| 78 static FX_LOCALECATEGORY XFA_ValugeCategory(FX_LOCALECATEGORY eCategory, | |
| 79 FX_DWORD dwValueType) { | |
| 80 if (eCategory == FX_LOCALECATEGORY_Unknown) { | |
| 81 switch (dwValueType) { | |
| 82 case XFA_VT_BOOLEAN: | |
| 83 case XFA_VT_INTEGER: | |
| 84 case XFA_VT_DECIMAL: | |
| 85 case XFA_VT_FLOAT: | |
| 86 return FX_LOCALECATEGORY_Num; | |
| 87 case XFA_VT_TEXT: | |
| 88 return FX_LOCALECATEGORY_Text; | |
| 89 case XFA_VT_DATE: | |
| 90 return FX_LOCALECATEGORY_Date; | |
| 91 case XFA_VT_TIME: | |
| 92 return FX_LOCALECATEGORY_Time; | |
| 93 case XFA_VT_DATETIME: | |
| 94 return FX_LOCALECATEGORY_DateTime; | |
| 95 } | |
| 96 } | |
| 97 return eCategory; | |
| 98 } | |
| 99 FX_BOOL CXFA_LocaleValue::ValidateValue(const CFX_WideString& wsValue, | |
| 100 const CFX_WideString& wsPattern, | |
| 101 IFX_Locale* pLocale, | |
| 102 CFX_WideString* pMatchFormat) { | |
| 103 CFX_WideString wsOutput; | |
| 104 IFX_Locale* locale = m_pLocaleMgr->GetDefLocale(); | |
| 105 if (pLocale) { | |
| 106 m_pLocaleMgr->SetDefLocale(pLocale); | |
| 107 } | |
| 108 IFX_FormatString* pFormat = IFX_FormatString::Create(m_pLocaleMgr, FALSE); | |
| 109 CFX_WideStringArray wsPatterns; | |
| 110 pFormat->SplitFormatString(wsPattern, wsPatterns); | |
| 111 FX_BOOL bRet = FALSE; | |
| 112 int32_t iCount = wsPatterns.GetSize(); | |
| 113 int32_t i = 0; | |
| 114 for (; i < iCount && !bRet; i++) { | |
| 115 CFX_WideString wsFormat = wsPatterns[i]; | |
| 116 FX_LOCALECATEGORY eCategory = pFormat->GetCategory(wsFormat); | |
| 117 eCategory = XFA_ValugeCategory(eCategory, m_dwType); | |
| 118 switch (eCategory) { | |
| 119 case FX_LOCALECATEGORY_Null: | |
| 120 bRet = pFormat->ParseNull(wsValue, wsFormat); | |
| 121 if (!bRet) { | |
| 122 bRet = wsValue.IsEmpty(); | |
| 123 } | |
| 124 break; | |
| 125 case FX_LOCALECATEGORY_Zero: | |
| 126 bRet = pFormat->ParseZero(wsValue, wsFormat); | |
| 127 if (!bRet) { | |
| 128 bRet = wsValue == FX_WSTRC(L"0"); | |
| 129 } | |
| 130 break; | |
| 131 case FX_LOCALECATEGORY_Num: { | |
| 132 CFX_WideString fNum; | |
| 133 bRet = pFormat->ParseNum(wsValue, wsFormat, fNum); | |
| 134 if (!bRet) { | |
| 135 bRet = pFormat->FormatNum(wsValue, wsFormat, wsOutput); | |
| 136 } | |
| 137 break; | |
| 138 } | |
| 139 case FX_LOCALECATEGORY_Text: | |
| 140 bRet = pFormat->ParseText(wsValue, wsFormat, wsOutput); | |
| 141 wsOutput.Empty(); | |
| 142 if (!bRet) { | |
| 143 bRet = pFormat->FormatText(wsValue, wsFormat, wsOutput); | |
| 144 } | |
| 145 break; | |
| 146 case FX_LOCALECATEGORY_Date: { | |
| 147 CFX_Unitime dt; | |
| 148 bRet = ValidateCanonicalDate(wsValue, dt); | |
| 149 if (!bRet) { | |
| 150 bRet = pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Date, | |
| 151 dt); | |
| 152 if (!bRet) { | |
| 153 bRet = pFormat->FormatDateTime(wsValue, wsFormat, wsOutput, | |
| 154 FX_DATETIMETYPE_Date); | |
| 155 } | |
| 156 } | |
| 157 break; | |
| 158 } | |
| 159 case FX_LOCALECATEGORY_Time: { | |
| 160 CFX_Unitime dt; | |
| 161 bRet = | |
| 162 pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Time, dt); | |
| 163 if (!bRet) { | |
| 164 bRet = pFormat->FormatDateTime(wsValue, wsFormat, wsOutput, | |
| 165 FX_DATETIMETYPE_Time); | |
| 166 } | |
| 167 break; | |
| 168 } | |
| 169 case FX_LOCALECATEGORY_DateTime: { | |
| 170 CFX_Unitime dt; | |
| 171 bRet = pFormat->ParseDateTime(wsValue, wsFormat, | |
| 172 FX_DATETIMETYPE_DateTime, dt); | |
| 173 if (!bRet) { | |
| 174 bRet = pFormat->FormatDateTime(wsValue, wsFormat, wsOutput, | |
| 175 FX_DATETIMETYPE_DateTime); | |
| 176 } | |
| 177 break; | |
| 178 } | |
| 179 default: | |
| 180 bRet = FALSE; | |
| 181 break; | |
| 182 } | |
| 183 } | |
| 184 if (bRet && pMatchFormat) { | |
| 185 *pMatchFormat = wsPatterns[i - 1]; | |
| 186 } | |
| 187 pFormat->Release(); | |
| 188 if (pLocale) { | |
| 189 m_pLocaleMgr->SetDefLocale(locale); | |
| 190 } | |
| 191 return bRet; | |
| 192 } | |
| 193 CFX_WideString CXFA_LocaleValue::GetValue() const { | |
| 194 return m_wsValue; | |
| 195 } | |
| 196 FX_DWORD CXFA_LocaleValue::GetType() const { | |
| 197 return m_dwType; | |
| 198 } | |
| 199 void CXFA_LocaleValue::SetValue(const CFX_WideString& wsValue, | |
| 200 FX_DWORD dwType) { | |
| 201 m_wsValue = wsValue; | |
| 202 m_dwType = dwType; | |
| 203 } | |
| 204 CFX_WideString CXFA_LocaleValue::GetText() const { | |
| 205 if (m_bValid && m_dwType == XFA_VT_TEXT) { | |
| 206 return m_wsValue; | |
| 207 } | |
| 208 return CFX_WideString(); | |
| 209 } | |
| 210 FX_FLOAT CXFA_LocaleValue::GetNum() const { | |
| 211 if (m_bValid && (m_dwType == XFA_VT_BOOLEAN || m_dwType == XFA_VT_INTEGER || | |
| 212 m_dwType == XFA_VT_DECIMAL || m_dwType == XFA_VT_FLOAT)) { | |
| 213 int64_t nIntegral = 0; | |
| 214 FX_DWORD dwFractional = 0; | |
| 215 int32_t nExponent = 0; | |
| 216 int cc = 0; | |
| 217 FX_BOOL bNegative = FALSE, bExpSign = FALSE; | |
| 218 const FX_WCHAR* str = (const FX_WCHAR*)m_wsValue; | |
| 219 int len = m_wsValue.GetLength(); | |
| 220 while (XFA_IsSpace(str[cc]) && cc < len) { | |
| 221 cc++; | |
| 222 } | |
| 223 if (cc >= len) { | |
| 224 return 0; | |
| 225 } | |
| 226 if (str[0] == '+') { | |
| 227 cc++; | |
| 228 } else if (str[0] == '-') { | |
| 229 bNegative = TRUE; | |
| 230 cc++; | |
| 231 } | |
| 232 int nIntegralLen = 0; | |
| 233 while (cc < len) { | |
| 234 if (str[cc] == '.' || !XFA_IsDigit(str[cc]) || nIntegralLen > 17) { | |
| 235 break; | |
| 236 } | |
| 237 nIntegral = nIntegral * 10 + str[cc] - '0'; | |
| 238 cc++; | |
| 239 nIntegralLen++; | |
| 240 } | |
| 241 nIntegral = bNegative ? -nIntegral : nIntegral; | |
| 242 int scale = 0; | |
| 243 double fraction = 0.0; | |
| 244 if (cc < len && str[cc] == '.') { | |
| 245 cc++; | |
| 246 while (cc < len) { | |
| 247 fraction += fraction_scales[scale] * (str[cc] - '0'); | |
| 248 scale++; | |
| 249 cc++; | |
| 250 if (scale == sizeof fraction_scales / sizeof(double) || | |
| 251 !XFA_IsDigit(str[cc])) { | |
| 252 break; | |
| 253 } | |
| 254 } | |
| 255 dwFractional = (FX_DWORD)(fraction * 4294967296.0); | |
| 256 } | |
| 257 if (cc < len && (str[cc] == 'E' || str[cc] == 'e')) { | |
| 258 cc++; | |
| 259 if (cc < len) { | |
| 260 if (str[cc] == '+') { | |
| 261 cc++; | |
| 262 } else if (str[cc] == '-') { | |
| 263 bExpSign = TRUE; | |
| 264 cc++; | |
| 265 } | |
| 266 } | |
| 267 while (cc < len) { | |
| 268 if (str[cc] == '.' || !XFA_IsDigit(str[cc])) { | |
| 269 break; | |
| 270 } | |
| 271 nExponent = nExponent * 10 + str[cc] - '0'; | |
| 272 cc++; | |
| 273 } | |
| 274 nExponent = bExpSign ? -nExponent : nExponent; | |
| 275 } | |
| 276 FX_FLOAT fValue = (FX_FLOAT)(dwFractional / 4294967296.0); | |
| 277 fValue = nIntegral + (nIntegral >= 0 ? fValue : -fValue); | |
| 278 if (nExponent != 0) { | |
| 279 fValue *= FXSYS_pow(10, (FX_FLOAT)nExponent); | |
| 280 } | |
| 281 return fValue; | |
| 282 } | |
| 283 return 0; | |
| 284 } | |
| 285 FX_DOUBLE CXFA_LocaleValue::GetDoubleNum() const { | |
| 286 if (m_bValid && (m_dwType == XFA_VT_BOOLEAN || m_dwType == XFA_VT_INTEGER || | |
| 287 m_dwType == XFA_VT_DECIMAL || m_dwType == XFA_VT_FLOAT)) { | |
| 288 int64_t nIntegral = 0; | |
| 289 FX_DWORD dwFractional = 0; | |
| 290 int32_t nExponent = 0; | |
| 291 int32_t cc = 0; | |
| 292 FX_BOOL bNegative = FALSE, bExpSign = FALSE; | |
| 293 const FX_WCHAR* str = (const FX_WCHAR*)m_wsValue; | |
| 294 int len = m_wsValue.GetLength(); | |
| 295 while (XFA_IsSpace(str[cc]) && cc < len) { | |
| 296 cc++; | |
| 297 } | |
| 298 if (cc >= len) { | |
| 299 return 0; | |
| 300 } | |
| 301 if (str[0] == '+') { | |
| 302 cc++; | |
| 303 } else if (str[0] == '-') { | |
| 304 bNegative = TRUE; | |
| 305 cc++; | |
| 306 } | |
| 307 int32_t nIntegralLen = 0; | |
| 308 while (cc < len) { | |
| 309 if (str[cc] == '.' || !XFA_IsDigit(str[cc]) || nIntegralLen > 17) { | |
| 310 break; | |
| 311 } | |
| 312 nIntegral = nIntegral * 10 + str[cc] - '0'; | |
| 313 cc++; | |
| 314 nIntegralLen++; | |
| 315 } | |
| 316 nIntegral = bNegative ? -nIntegral : nIntegral; | |
| 317 int32_t scale = 0; | |
| 318 FX_DOUBLE fraction = 0.0; | |
| 319 if (cc < len && str[cc] == '.') { | |
| 320 cc++; | |
| 321 while (cc < len) { | |
| 322 fraction += fraction_scales[scale] * (str[cc] - '0'); | |
| 323 scale++; | |
| 324 cc++; | |
| 325 if (scale == sizeof fraction_scales / sizeof(FX_DOUBLE) || | |
| 326 !XFA_IsDigit(str[cc])) { | |
| 327 break; | |
| 328 } | |
| 329 } | |
| 330 dwFractional = (FX_DWORD)(fraction * 4294967296.0); | |
| 331 } | |
| 332 if (cc < len && (str[cc] == 'E' || str[cc] == 'e')) { | |
| 333 cc++; | |
| 334 if (cc < len) { | |
| 335 if (str[cc] == '+') { | |
| 336 cc++; | |
| 337 } else if (str[cc] == '-') { | |
| 338 bExpSign = TRUE; | |
| 339 cc++; | |
| 340 } | |
| 341 } | |
| 342 while (cc < len) { | |
| 343 if (str[cc] == '.' || !XFA_IsDigit(str[cc])) { | |
| 344 break; | |
| 345 } | |
| 346 nExponent = nExponent * 10 + str[cc] - '0'; | |
| 347 cc++; | |
| 348 } | |
| 349 nExponent = bExpSign ? -nExponent : nExponent; | |
| 350 } | |
| 351 FX_DOUBLE dValue = (dwFractional / 4294967296.0); | |
| 352 dValue = nIntegral + (nIntegral >= 0 ? dValue : -dValue); | |
| 353 if (nExponent != 0) { | |
| 354 dValue *= FXSYS_pow(10, (FX_FLOAT)nExponent); | |
| 355 } | |
| 356 return dValue; | |
| 357 } | |
| 358 return 0; | |
| 359 } | |
| 360 CFX_Unitime CXFA_LocaleValue::GetDate() const { | |
| 361 if (m_bValid && m_dwType == XFA_VT_DATE) { | |
| 362 CFX_Unitime dt; | |
| 363 FX_DateFromCanonical(m_wsValue, dt); | |
| 364 return dt; | |
| 365 } | |
| 366 return CFX_Unitime(); | |
| 367 } | |
| 368 CFX_Unitime CXFA_LocaleValue::GetTime() const { | |
| 369 if (m_bValid && m_dwType == XFA_VT_TIME) { | |
| 370 CFX_Unitime dt(0); | |
| 371 FXSYS_assert(m_pLocaleMgr); | |
| 372 FX_TimeFromCanonical(m_wsValue, dt, m_pLocaleMgr->GetDefLocale()); | |
| 373 return dt; | |
| 374 } | |
| 375 return CFX_Unitime(); | |
| 376 } | |
| 377 CFX_Unitime CXFA_LocaleValue::GetDateTime() const { | |
| 378 if (m_bValid && m_dwType == XFA_VT_DATETIME) { | |
| 379 int32_t index = m_wsValue.Find('T'); | |
| 380 CFX_Unitime dt; | |
| 381 FX_DateFromCanonical(m_wsValue.Left(index), dt); | |
| 382 FXSYS_assert(m_pLocaleMgr); | |
| 383 FX_TimeFromCanonical(m_wsValue.Right(m_wsValue.GetLength() - index - 1), dt, | |
| 384 m_pLocaleMgr->GetDefLocale()); | |
| 385 return dt; | |
| 386 } | |
| 387 return CFX_Unitime(); | |
| 388 } | |
| 389 FX_BOOL CXFA_LocaleValue::SetText(const CFX_WideString& wsText) { | |
| 390 m_dwType = XFA_VT_TEXT; | |
| 391 m_wsValue = wsText; | |
| 392 return TRUE; | |
| 393 } | |
| 394 FX_BOOL CXFA_LocaleValue::SetText(const CFX_WideString& wsText, | |
| 395 const CFX_WideString& wsFormat, | |
| 396 IFX_Locale* pLocale) { | |
| 397 m_dwType = XFA_VT_TEXT; | |
| 398 return m_bValid = ParsePatternValue(wsText, wsFormat, pLocale); | |
| 399 } | |
| 400 FX_BOOL CXFA_LocaleValue::SetNum(FX_FLOAT fNum) { | |
| 401 m_dwType = XFA_VT_FLOAT; | |
| 402 m_wsValue.Format(L"%.8g", (FX_DOUBLE)fNum); | |
| 403 return TRUE; | |
| 404 } | |
| 405 FX_BOOL CXFA_LocaleValue::SetNum(const CFX_WideString& wsNum, | |
| 406 const CFX_WideString& wsFormat, | |
| 407 IFX_Locale* pLocale) { | |
| 408 m_dwType = XFA_VT_FLOAT; | |
| 409 return m_bValid = ParsePatternValue(wsNum, wsFormat, pLocale); | |
| 410 } | |
| 411 FX_BOOL CXFA_LocaleValue::SetDate(const CFX_Unitime& d) { | |
| 412 m_dwType = XFA_VT_DATE; | |
| 413 m_wsValue.Format(L"%04d-%02d-%02d", d.GetYear(), d.GetMonth(), d.GetDay()); | |
| 414 return TRUE; | |
| 415 } | |
| 416 FX_BOOL CXFA_LocaleValue::SetDate(const CFX_WideString& wsDate, | |
| 417 const CFX_WideString& wsFormat, | |
| 418 IFX_Locale* pLocale) { | |
| 419 m_dwType = XFA_VT_DATE; | |
| 420 return m_bValid = ParsePatternValue(wsDate, wsFormat, pLocale); | |
| 421 } | |
| 422 FX_BOOL CXFA_LocaleValue::SetTime(const CFX_Unitime& t) { | |
| 423 m_dwType = XFA_VT_TIME; | |
| 424 m_wsValue.Format(L"%02d:%02d:%02d", t.GetHour(), t.GetMinute(), | |
| 425 t.GetSecond()); | |
| 426 if (t.GetMillisecond() > 0) { | |
| 427 CFX_WideString wsTemp; | |
| 428 wsTemp.Format(L"%:03d", t.GetMillisecond()); | |
| 429 m_wsValue += wsTemp; | |
| 430 } | |
| 431 return TRUE; | |
| 432 } | |
| 433 FX_BOOL CXFA_LocaleValue::SetTime(const CFX_WideString& wsTime, | |
| 434 const CFX_WideString& wsFormat, | |
| 435 IFX_Locale* pLocale) { | |
| 436 m_dwType = XFA_VT_TIME; | |
| 437 return m_bValid = ParsePatternValue(wsTime, wsFormat, pLocale); | |
| 438 } | |
| 439 FX_BOOL CXFA_LocaleValue::SetDateTime(const CFX_Unitime& dt) { | |
| 440 m_dwType = XFA_VT_DATETIME; | |
| 441 m_wsValue.Format(L"%04d-%02d-%02dT%02d:%02d:%02d", dt.GetYear(), | |
| 442 dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute(), | |
| 443 dt.GetSecond()); | |
| 444 if (dt.GetMillisecond() > 0) { | |
| 445 CFX_WideString wsTemp; | |
| 446 wsTemp.Format(L"%:03d", dt.GetMillisecond()); | |
| 447 m_wsValue += wsTemp; | |
| 448 } | |
| 449 return TRUE; | |
| 450 } | |
| 451 FX_BOOL CXFA_LocaleValue::SetDateTime(const CFX_WideString& wsDateTime, | |
| 452 const CFX_WideString& wsFormat, | |
| 453 IFX_Locale* pLocale) { | |
| 454 m_dwType = XFA_VT_DATETIME; | |
| 455 return m_bValid = ParsePatternValue(wsDateTime, wsFormat, pLocale); | |
| 456 } | |
| 457 FX_BOOL CXFA_LocaleValue::FormatPatterns(CFX_WideString& wsResult, | |
| 458 const CFX_WideString& wsFormat, | |
| 459 IFX_Locale* pLocale, | |
| 460 XFA_VALUEPICTURE eValueType) const { | |
| 461 wsResult.Empty(); | |
| 462 FX_BOOL bRet = FALSE; | |
| 463 IFX_FormatString* pFormat = IFX_FormatString::Create(m_pLocaleMgr, FALSE); | |
| 464 CFX_WideStringArray wsPatterns; | |
| 465 pFormat->SplitFormatString(wsFormat, wsPatterns); | |
| 466 int32_t iCount = wsPatterns.GetSize(); | |
| 467 for (int32_t i = 0; i < iCount; i++) { | |
| 468 bRet = FormatSinglePattern(wsResult, wsPatterns[i], pLocale, eValueType); | |
| 469 if (bRet) { | |
| 470 break; | |
| 471 } | |
| 472 } | |
| 473 pFormat->Release(); | |
| 474 return bRet; | |
| 475 } | |
| 476 FX_BOOL CXFA_LocaleValue::FormatSinglePattern( | |
| 477 CFX_WideString& wsResult, | |
| 478 const CFX_WideString& wsFormat, | |
| 479 IFX_Locale* pLocale, | |
| 480 XFA_VALUEPICTURE eValueType) const { | |
| 481 IFX_Locale* locale = m_pLocaleMgr->GetDefLocale(); | |
| 482 if (pLocale) { | |
| 483 m_pLocaleMgr->SetDefLocale(pLocale); | |
| 484 } | |
| 485 wsResult.Empty(); | |
| 486 FX_BOOL bRet = FALSE; | |
| 487 IFX_FormatString* pFormat = IFX_FormatString::Create(m_pLocaleMgr, FALSE); | |
| 488 FX_LOCALECATEGORY eCategory = pFormat->GetCategory(wsFormat); | |
| 489 eCategory = XFA_ValugeCategory(eCategory, m_dwType); | |
| 490 switch (eCategory) { | |
| 491 case FX_LOCALECATEGORY_Null: | |
| 492 if (m_wsValue.IsEmpty()) { | |
| 493 bRet = pFormat->FormatNull(wsFormat, wsResult); | |
| 494 } | |
| 495 break; | |
| 496 case FX_LOCALECATEGORY_Zero: | |
| 497 if (m_wsValue == FX_WSTRC(L"0")) { | |
| 498 bRet = pFormat->FormatZero(wsFormat, wsResult); | |
| 499 } | |
| 500 break; | |
| 501 case FX_LOCALECATEGORY_Num: | |
| 502 bRet = pFormat->FormatNum(m_wsValue, wsFormat, wsResult); | |
| 503 break; | |
| 504 case FX_LOCALECATEGORY_Text: | |
| 505 bRet = pFormat->FormatText(m_wsValue, wsFormat, wsResult); | |
| 506 break; | |
| 507 case FX_LOCALECATEGORY_Date: | |
| 508 bRet = pFormat->FormatDateTime(m_wsValue, wsFormat, wsResult, | |
| 509 FX_DATETIMETYPE_Date); | |
| 510 break; | |
| 511 case FX_LOCALECATEGORY_Time: | |
| 512 bRet = pFormat->FormatDateTime(m_wsValue, wsFormat, wsResult, | |
| 513 FX_DATETIMETYPE_Time); | |
| 514 break; | |
| 515 case FX_LOCALECATEGORY_DateTime: | |
| 516 bRet = pFormat->FormatDateTime(m_wsValue, wsFormat, wsResult, | |
| 517 FX_DATETIMETYPE_DateTime); | |
| 518 break; | |
| 519 default: | |
| 520 wsResult = m_wsValue; | |
| 521 bRet = TRUE; | |
| 522 } | |
| 523 pFormat->Release(); | |
| 524 if (!bRet && (eCategory != FX_LOCALECATEGORY_Num || | |
| 525 eValueType != XFA_VALUEPICTURE_Display)) { | |
| 526 wsResult = m_wsValue; | |
| 527 } | |
| 528 if (pLocale) { | |
| 529 m_pLocaleMgr->SetDefLocale(locale); | |
| 530 } | |
| 531 return bRet; | |
| 532 } | |
| 533 static FX_BOOL XFA_ValueSplitDateTime(const CFX_WideString& wsDateTime, | |
| 534 CFX_WideString& wsDate, | |
| 535 CFX_WideString& wsTime) { | |
| 536 wsDate = L""; | |
| 537 wsTime = L""; | |
| 538 if (wsDateTime.IsEmpty()) { | |
| 539 return FALSE; | |
| 540 } | |
| 541 int nSplitIndex = -1; | |
| 542 nSplitIndex = wsDateTime.Find('T'); | |
| 543 if (nSplitIndex < 0) { | |
| 544 nSplitIndex = wsDateTime.Find(' '); | |
| 545 } | |
| 546 if (nSplitIndex < 0) { | |
| 547 return FALSE; | |
| 548 } | |
| 549 wsDate = wsDateTime.Left(nSplitIndex); | |
| 550 wsTime = wsDateTime.Right(wsDateTime.GetLength() - nSplitIndex - 1); | |
| 551 return TRUE; | |
| 552 } | |
| 553 FX_BOOL CXFA_LocaleValue::ValidateCanonicalValue(const CFX_WideString& wsValue, | |
| 554 FX_DWORD dwVType) { | |
| 555 if (wsValue.IsEmpty()) { | |
| 556 return TRUE; | |
| 557 } | |
| 558 CFX_Unitime dt; | |
| 559 switch (dwVType) { | |
| 560 case XFA_VT_DATE: { | |
| 561 if (ValidateCanonicalDate(wsValue, dt)) { | |
| 562 return TRUE; | |
| 563 } | |
| 564 CFX_WideString wsDate, wsTime; | |
| 565 if (XFA_ValueSplitDateTime(wsValue, wsDate, wsTime) && | |
| 566 ValidateCanonicalDate(wsDate, dt)) { | |
| 567 return TRUE; | |
| 568 } | |
| 569 return FALSE; | |
| 570 } | |
| 571 case XFA_VT_TIME: { | |
| 572 if (ValidateCanonicalTime(wsValue)) { | |
| 573 return TRUE; | |
| 574 } | |
| 575 CFX_WideString wsDate, wsTime; | |
| 576 if (XFA_ValueSplitDateTime(wsValue, wsDate, wsTime) && | |
| 577 ValidateCanonicalTime(wsTime)) { | |
| 578 return TRUE; | |
| 579 } | |
| 580 return FALSE; | |
| 581 } | |
| 582 case XFA_VT_DATETIME: { | |
| 583 CFX_WideString wsDate, wsTime; | |
| 584 if (XFA_ValueSplitDateTime(wsValue, wsDate, wsTime) && | |
| 585 ValidateCanonicalDate(wsDate, dt) && ValidateCanonicalTime(wsTime)) { | |
| 586 return TRUE; | |
| 587 } | |
| 588 } break; | |
| 589 } | |
| 590 return TRUE; | |
| 591 } | |
| 592 FX_BOOL CXFA_LocaleValue::ValidateCanonicalDate(const CFX_WideString& wsDate, | |
| 593 CFX_Unitime& unDate) { | |
| 594 const FX_WORD LastDay[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | |
| 595 const FX_WORD wCountY = 4, wCountM = 2, wCountD = 2; | |
| 596 int nLen = wsDate.GetLength(); | |
| 597 if (nLen < wCountY || nLen > wCountY + wCountM + wCountD + 2) { | |
| 598 return FALSE; | |
| 599 } | |
| 600 const bool bSymbol = wsDate.Find(0x2D) != -1; | |
| 601 FX_WORD wYear = 0; | |
| 602 FX_WORD wMonth = 0; | |
| 603 FX_WORD wDay = 0; | |
| 604 const FX_WCHAR* pDate = (const FX_WCHAR*)wsDate; | |
| 605 int nIndex = 0, nStart = 0; | |
| 606 while (pDate[nIndex] != '\0' && nIndex < wCountY) { | |
| 607 if (!XFA_IsDigit(pDate[nIndex])) { | |
| 608 return FALSE; | |
| 609 } | |
| 610 wYear = (pDate[nIndex] - '0') + wYear * 10; | |
| 611 nIndex++; | |
| 612 } | |
| 613 if (bSymbol) { | |
| 614 if (pDate[nIndex] != 0x2D) { | |
| 615 return FALSE; | |
| 616 } | |
| 617 nIndex++; | |
| 618 } | |
| 619 nStart = nIndex; | |
| 620 while (pDate[nIndex] != '\0' && nIndex - nStart < wCountM && nIndex < nLen) { | |
| 621 if (!XFA_IsDigit(pDate[nIndex])) { | |
| 622 return FALSE; | |
| 623 } | |
| 624 wMonth = (pDate[nIndex] - '0') + wMonth * 10; | |
| 625 nIndex++; | |
| 626 } | |
| 627 if (bSymbol) { | |
| 628 if (pDate[nIndex] != 0x2D) { | |
| 629 return FALSE; | |
| 630 } | |
| 631 nIndex++; | |
| 632 } | |
| 633 nStart = nIndex; | |
| 634 while (pDate[nIndex] != '\0' && nIndex - nStart < wCountD && nIndex < nLen) { | |
| 635 if (!XFA_IsDigit(pDate[nIndex])) { | |
| 636 return FALSE; | |
| 637 } | |
| 638 wDay = (pDate[nIndex] - '0') + wDay * 10; | |
| 639 nIndex++; | |
| 640 } | |
| 641 if (nIndex != nLen) { | |
| 642 return FALSE; | |
| 643 } | |
| 644 if (wYear < 1900 || wYear > 2029) { | |
| 645 return FALSE; | |
| 646 } | |
| 647 if (wMonth < 1 || wMonth > 12) { | |
| 648 if (wMonth == 0 && nLen == wCountY) { | |
| 649 return TRUE; | |
| 650 } | |
| 651 return FALSE; | |
| 652 } | |
| 653 if (wDay < 1) { | |
| 654 if (wDay == 0 && (nLen == wCountY + wCountM)) { | |
| 655 return TRUE; | |
| 656 } | |
| 657 return FALSE; | |
| 658 } | |
| 659 if (wMonth == 2) { | |
| 660 if (wYear % 400 == 0 || (wYear % 100 != 0 && wYear % 4 == 0)) { | |
| 661 if (wDay > 29) { | |
| 662 return FALSE; | |
| 663 } | |
| 664 } else { | |
| 665 if (wDay > 28) { | |
| 666 return FALSE; | |
| 667 } | |
| 668 } | |
| 669 } else if (wDay > LastDay[wMonth - 1]) { | |
| 670 return FALSE; | |
| 671 } | |
| 672 CFX_Unitime ut; | |
| 673 ut.Set(wYear, static_cast<uint8_t>(wMonth), static_cast<uint8_t>(wDay)); | |
| 674 unDate = unDate + ut; | |
| 675 return TRUE; | |
| 676 } | |
| 677 FX_BOOL CXFA_LocaleValue::ValidateCanonicalTime(const CFX_WideString& wsTime) { | |
| 678 int nLen = wsTime.GetLength(); | |
| 679 if (nLen < 2) | |
| 680 return FALSE; | |
| 681 const FX_WORD wCountH = 2; | |
| 682 const FX_WORD wCountM = 2; | |
| 683 const FX_WORD wCountS = 2; | |
| 684 const FX_WORD wCountF = 3; | |
| 685 const bool bSymbol = wsTime.Find(':') != -1; | |
| 686 FX_WORD wHour = 0; | |
| 687 FX_WORD wMinute = 0; | |
| 688 FX_WORD wSecond = 0; | |
| 689 FX_WORD wFraction = 0; | |
| 690 const FX_WCHAR* pTime = (const FX_WCHAR*)wsTime; | |
| 691 int nIndex = 0; | |
| 692 int nStart = 0; | |
| 693 while (nIndex - nStart < wCountH && pTime[nIndex]) { | |
| 694 if (!XFA_IsDigit(pTime[nIndex])) | |
| 695 return FALSE; | |
| 696 wHour = pTime[nIndex] - '0' + wHour * 10; | |
| 697 nIndex++; | |
| 698 } | |
| 699 if (bSymbol) { | |
| 700 if (nIndex < nLen && pTime[nIndex] != ':') | |
| 701 return FALSE; | |
| 702 nIndex++; | |
| 703 } | |
| 704 nStart = nIndex; | |
| 705 while (nIndex - nStart < wCountM && nIndex < nLen && pTime[nIndex]) { | |
| 706 if (!XFA_IsDigit(pTime[nIndex])) | |
| 707 return FALSE; | |
| 708 wMinute = pTime[nIndex] - '0' + wMinute * 10; | |
| 709 nIndex++; | |
| 710 } | |
| 711 if (bSymbol) { | |
| 712 if (nIndex < nLen && pTime[nIndex] != ':') | |
| 713 return FALSE; | |
| 714 nIndex++; | |
| 715 } | |
| 716 nStart = nIndex; | |
| 717 while (nIndex - nStart < wCountS && nIndex < nLen && pTime[nIndex]) { | |
| 718 if (!XFA_IsDigit(pTime[nIndex])) | |
| 719 return FALSE; | |
| 720 wSecond = pTime[nIndex] - '0' + wSecond * 10; | |
| 721 nIndex++; | |
| 722 } | |
| 723 if (wsTime.Find('.') > 0) { | |
| 724 if (pTime[nIndex] != '.') | |
| 725 return FALSE; | |
| 726 nIndex++; | |
| 727 nStart = nIndex; | |
| 728 while (nIndex - nStart < wCountF && nIndex < nLen && pTime[nIndex]) { | |
| 729 if (!XFA_IsDigit(pTime[nIndex])) | |
| 730 return FALSE; | |
| 731 wFraction = pTime[nIndex] - '0' + wFraction * 10; | |
| 732 nIndex++; | |
| 733 } | |
| 734 } | |
| 735 if (nIndex < nLen) { | |
| 736 if (pTime[nIndex] == 'Z') { | |
| 737 nIndex++; | |
| 738 } else if (pTime[nIndex] == '-' || pTime[nIndex] == '+') { | |
| 739 int16_t nOffsetH = 0; | |
| 740 int16_t nOffsetM = 0; | |
| 741 nIndex++; | |
| 742 nStart = nIndex; | |
| 743 while (nIndex - nStart < wCountH && nIndex < nLen && pTime[nIndex]) { | |
| 744 if (!XFA_IsDigit(pTime[nIndex])) | |
| 745 return FALSE; | |
| 746 nOffsetH = pTime[nIndex] - '0' + nOffsetH * 10; | |
| 747 nIndex++; | |
| 748 } | |
| 749 if (bSymbol) { | |
| 750 if (nIndex < nLen && pTime[nIndex] != ':') | |
| 751 return FALSE; | |
| 752 nIndex++; | |
| 753 } | |
| 754 nStart = nIndex; | |
| 755 while (nIndex - nStart < wCountM && nIndex < nLen && pTime[nIndex]) { | |
| 756 if (!XFA_IsDigit(pTime[nIndex])) | |
| 757 return FALSE; | |
| 758 nOffsetM = pTime[nIndex] - '0' + nOffsetM * 10; | |
| 759 nIndex++; | |
| 760 } | |
| 761 if (nOffsetH > 12 || nOffsetM >= 60) | |
| 762 return FALSE; | |
| 763 } | |
| 764 } | |
| 765 return nIndex == nLen && wHour < 24 && wMinute < 60 && wSecond < 60 && | |
| 766 wFraction <= 999; | |
| 767 } | |
| 768 FX_BOOL CXFA_LocaleValue::ValidateCanonicalDateTime( | |
| 769 const CFX_WideString& wsDateTime) { | |
| 770 CFX_WideString wsDate, wsTime; | |
| 771 if (wsDateTime.IsEmpty()) { | |
| 772 return FALSE; | |
| 773 } | |
| 774 int nSplitIndex = -1; | |
| 775 nSplitIndex = wsDateTime.Find('T'); | |
| 776 if (nSplitIndex < 0) { | |
| 777 nSplitIndex = wsDateTime.Find(' '); | |
| 778 } | |
| 779 if (nSplitIndex < 0) { | |
| 780 return FALSE; | |
| 781 } | |
| 782 wsDate = wsDateTime.Left(nSplitIndex); | |
| 783 wsTime = wsDateTime.Right(wsDateTime.GetLength() - nSplitIndex - 1); | |
| 784 CFX_Unitime dt; | |
| 785 return ValidateCanonicalDate(wsDate, dt) && ValidateCanonicalTime(wsTime); | |
| 786 } | |
| 787 FX_BOOL CXFA_LocaleValue::ParsePatternValue(const CFX_WideString& wsValue, | |
| 788 const CFX_WideString& wsPattern, | |
| 789 IFX_Locale* pLocale) { | |
| 790 IFX_Locale* locale = m_pLocaleMgr->GetDefLocale(); | |
| 791 if (pLocale) { | |
| 792 m_pLocaleMgr->SetDefLocale(pLocale); | |
| 793 } | |
| 794 IFX_FormatString* pFormat = IFX_FormatString::Create(m_pLocaleMgr, FALSE); | |
| 795 CFX_WideStringArray wsPatterns; | |
| 796 pFormat->SplitFormatString(wsPattern, wsPatterns); | |
| 797 FX_BOOL bRet = FALSE; | |
| 798 int32_t iCount = wsPatterns.GetSize(); | |
| 799 for (int32_t i = 0; i < iCount && !bRet; i++) { | |
| 800 CFX_WideString wsFormat = wsPatterns[i]; | |
| 801 FX_LOCALECATEGORY eCategory = pFormat->GetCategory(wsFormat); | |
| 802 eCategory = XFA_ValugeCategory(eCategory, m_dwType); | |
| 803 switch (eCategory) { | |
| 804 case FX_LOCALECATEGORY_Null: | |
| 805 bRet = pFormat->ParseNull(wsValue, wsFormat); | |
| 806 if (bRet) { | |
| 807 m_wsValue.Empty(); | |
| 808 } | |
| 809 break; | |
| 810 case FX_LOCALECATEGORY_Zero: | |
| 811 bRet = pFormat->ParseZero(wsValue, wsFormat); | |
| 812 if (bRet) { | |
| 813 m_wsValue = FX_WSTRC(L"0"); | |
| 814 } | |
| 815 break; | |
| 816 case FX_LOCALECATEGORY_Num: { | |
| 817 CFX_WideString fNum; | |
| 818 bRet = pFormat->ParseNum(wsValue, wsFormat, fNum); | |
| 819 if (bRet) { | |
| 820 m_wsValue = fNum; | |
| 821 } | |
| 822 break; | |
| 823 } | |
| 824 case FX_LOCALECATEGORY_Text: | |
| 825 bRet = pFormat->ParseText(wsValue, wsFormat, m_wsValue); | |
| 826 break; | |
| 827 case FX_LOCALECATEGORY_Date: { | |
| 828 CFX_Unitime dt; | |
| 829 bRet = ValidateCanonicalDate(wsValue, dt); | |
| 830 if (!bRet) { | |
| 831 bRet = pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Date, | |
| 832 dt); | |
| 833 } | |
| 834 if (bRet) { | |
| 835 SetDate(dt); | |
| 836 } | |
| 837 break; | |
| 838 } | |
| 839 case FX_LOCALECATEGORY_Time: { | |
| 840 CFX_Unitime dt; | |
| 841 bRet = | |
| 842 pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Time, dt); | |
| 843 if (bRet) { | |
| 844 SetTime(dt); | |
| 845 } | |
| 846 break; | |
| 847 } | |
| 848 case FX_LOCALECATEGORY_DateTime: { | |
| 849 CFX_Unitime dt; | |
| 850 bRet = pFormat->ParseDateTime(wsValue, wsFormat, | |
| 851 FX_DATETIMETYPE_DateTime, dt); | |
| 852 if (bRet) { | |
| 853 SetDateTime(dt); | |
| 854 } | |
| 855 break; | |
| 856 } | |
| 857 default: | |
| 858 m_wsValue = wsValue; | |
| 859 bRet = TRUE; | |
| 860 break; | |
| 861 } | |
| 862 } | |
| 863 if (!bRet) { | |
| 864 m_wsValue = wsValue; | |
| 865 } | |
| 866 pFormat->Release(); | |
| 867 if (pLocale) { | |
| 868 m_pLocaleMgr->SetDefLocale(locale); | |
| 869 } | |
| 870 return bRet; | |
| 871 } | |
| 872 void CXFA_LocaleValue::GetNumbericFormat(CFX_WideString& wsFormat, | |
| 873 int32_t nIntLen, | |
| 874 int32_t nDecLen, | |
| 875 FX_BOOL bSign) { | |
| 876 FXSYS_assert(wsFormat.IsEmpty()); | |
| 877 FXSYS_assert(nIntLen >= -1 && nDecLen >= -1); | |
| 878 int32_t nTotalLen = (nIntLen >= 0 ? nIntLen : 2) + (bSign ? 1 : 0) + | |
| 879 (nDecLen >= 0 ? nDecLen : 2) + (nDecLen == 0 ? 0 : 1); | |
| 880 FX_WCHAR* lpBuf = wsFormat.GetBuffer(nTotalLen); | |
| 881 int32_t nPos = 0; | |
| 882 if (bSign) { | |
| 883 lpBuf[nPos++] = L's'; | |
| 884 } | |
| 885 if (nIntLen == -1) { | |
| 886 lpBuf[nPos++] = L'z'; | |
| 887 lpBuf[nPos++] = L'*'; | |
| 888 } else { | |
| 889 while (nIntLen) { | |
| 890 lpBuf[nPos++] = L'z'; | |
| 891 nIntLen--; | |
| 892 } | |
| 893 } | |
| 894 if (nDecLen != 0) { | |
| 895 lpBuf[nPos++] = L'.'; | |
| 896 } | |
| 897 if (nDecLen == -1) { | |
| 898 lpBuf[nPos++] = L'z'; | |
| 899 lpBuf[nPos++] = L'*'; | |
| 900 } else { | |
| 901 while (nDecLen) { | |
| 902 lpBuf[nPos++] = L'z'; | |
| 903 nDecLen--; | |
| 904 } | |
| 905 } | |
| 906 wsFormat.ReleaseBuffer(nTotalLen); | |
| 907 } | |
| 908 FX_BOOL CXFA_LocaleValue::ValidateNumericTemp(CFX_WideString& wsNumeric, | |
| 909 CFX_WideString& wsFormat, | |
| 910 IFX_Locale* pLocale, | |
| 911 int32_t* pos) { | |
| 912 if (wsFormat.IsEmpty() || wsNumeric.IsEmpty()) { | |
| 913 return TRUE; | |
| 914 } | |
| 915 const FX_WCHAR* pNum = wsNumeric.c_str(); | |
| 916 const FX_WCHAR* pFmt = wsFormat.c_str(); | |
| 917 int32_t n = 0, nf = 0; | |
| 918 FX_WCHAR c = pNum[n]; | |
| 919 FX_WCHAR cf = pFmt[nf]; | |
| 920 if (cf == L's') { | |
| 921 if (c == L'-' || c == L'+') { | |
| 922 ++n; | |
| 923 } | |
| 924 ++nf; | |
| 925 } | |
| 926 FX_BOOL bLimit = TRUE; | |
| 927 int32_t nCount = wsNumeric.GetLength(); | |
| 928 int32_t nCountFmt = wsFormat.GetLength(); | |
| 929 while (n < nCount && (bLimit ? nf < nCountFmt : TRUE) && | |
| 930 XFA_IsDigit(c = pNum[n])) { | |
| 931 if (bLimit == TRUE) { | |
| 932 if ((cf = pFmt[nf]) == L'*') { | |
| 933 bLimit = FALSE; | |
| 934 } else if (cf == L'z') { | |
| 935 nf++; | |
| 936 } else { | |
| 937 return FALSE; | |
| 938 } | |
| 939 } | |
| 940 n++; | |
| 941 } | |
| 942 if (n == nCount) { | |
| 943 return TRUE; | |
| 944 } | |
| 945 if (nf == nCountFmt) { | |
| 946 return FALSE; | |
| 947 } | |
| 948 while (nf < nCountFmt && (cf = pFmt[nf]) != L'.') { | |
| 949 FXSYS_assert(cf == L'z' || cf == L'*'); | |
| 950 ++nf; | |
| 951 } | |
| 952 CFX_WideString wsDecimalSymbol; | |
| 953 if (pLocale) { | |
| 954 pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsDecimalSymbol); | |
| 955 } else { | |
| 956 wsDecimalSymbol = CFX_WideString(L'.'); | |
| 957 } | |
| 958 if (pFmt[nf] != L'.') { | |
| 959 return FALSE; | |
| 960 } | |
| 961 if (wsDecimalSymbol != CFX_WideStringC(c) && c != L'.') { | |
| 962 return FALSE; | |
| 963 } | |
| 964 ++nf; | |
| 965 ++n; | |
| 966 bLimit = TRUE; | |
| 967 while (n < nCount && (bLimit ? nf < nCountFmt : TRUE) && | |
| 968 XFA_IsDigit(c = pNum[n])) { | |
| 969 if (bLimit == TRUE) { | |
| 970 if ((cf = pFmt[nf]) == L'*') { | |
| 971 bLimit = FALSE; | |
| 972 } else if (cf == L'z') { | |
| 973 nf++; | |
| 974 } else { | |
| 975 return FALSE; | |
| 976 } | |
| 977 } | |
| 978 n++; | |
| 979 } | |
| 980 return n == nCount; | |
| 981 } | |
| OLD | NEW |