/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
constint MaxCntPost = 20; //max dec places allow by rtl_math_round
const NfKeywordTable ImpSvNumberformatScan::sEnglishKeyword =
{ // Syntax keywords in English (USA) //! All keywords MUST be UPPERCASE! In same order as NfKeywordIndex
u""_ustr, // NF_KEY_NONE 0
u"E"_ustr, // NF_KEY_E Exponent
u"AM/PM"_ustr, // NF_KEY_AMPM AM/PM
u"A/P"_ustr, // NF_KEY_AP AM/PM short
u"M"_ustr, // NF_KEY_MI Minute
u"MM"_ustr, // NF_KEY_MMI Minute 02
u"M"_ustr, // NF_KEY_M month (!)
u"MM"_ustr, // NF_KEY_MM month 02 (!)
u"MMM"_ustr, // NF_KEY_MMM month short name
u"MMMM"_ustr, // NF_KEY_MMMM month long name
u"MMMMM"_ustr, // NF_KEY_MMMMM first letter of month name
u"H"_ustr, // NF_KEY_H hour
u"HH"_ustr, // NF_KEY_HH hour 02
u"S"_ustr, // NF_KEY_S Second
u"SS"_ustr, // NF_KEY_SS Second 02
u"Q"_ustr, // NF_KEY_Q Quarter short 'Q'
u"QQ"_ustr, // NF_KEY_QQ Quarter long
u"D"_ustr, // NF_KEY_D day of month
u"DD"_ustr, // NF_KEY_DD day of month 02
u"DDD"_ustr, // NF_KEY_DDD day of week short
u"DDDD"_ustr, // NF_KEY_DDDD day of week long
u"YY"_ustr, // NF_KEY_YY year two digits
u"YYYY"_ustr, // NF_KEY_YYYY year four digits
u"NN"_ustr, // NF_KEY_NN Day of week short
u"NNN"_ustr, // NF_KEY_NNN Day of week long
u"NNNN"_ustr, // NF_KEY_NNNN Day of week long incl. separator
u"AAA"_ustr, // NF_KEY_AAA
u"AAAA"_ustr, // NF_KEY_AAAA
u"E"_ustr, // NF_KEY_EC
u"EE"_ustr, // NF_KEY_EEC
u"G"_ustr, // NF_KEY_G
u"GG"_ustr, // NF_KEY_GG
u"GGG"_ustr, // NF_KEY_GGG
u"R"_ustr, // NF_KEY_R
u"RR"_ustr, // NF_KEY_RR
u"WW"_ustr, // NF_KEY_WW Week of year
u"t"_ustr, // NF_KEY_THAI_T Thai T modifier, speciality of Thai Excel, only // used with Thai locale and converted to [NatNum1], only // exception as lowercase
u"CCC"_ustr, // NF_KEY_CCC Currency abbreviation
u"BOOLEAN"_ustr, // NF_KEY_BOOLEAN boolean
u"GENERAL"_ustr, // NF_KEY_GENERAL General / Standard
const CharClass* pCharClass = mrCurrentLanguageData.GetCharClass(); const LocaleDataWrapper* pLocaleData = mrCurrentLanguageData.GetLocaleData(); // #80023# be sure to generate keywords for the loaded Locale, not for the // requested Locale, otherwise number format codes might not match const LanguageTag aLoadedLocale = pLocaleData->getLoadedLanguageTag();
LanguageType eLang = aLoadedLocale.getLanguageType( false);
bool bL10n = (meKeywordLocalization != KeywordLocalization::EnglishOnly); if (bL10n)
{ // Check if this actually is a locale that uses any localized keywords, // if not then disable localized keywords completely. if ( !eLang.anyOf( LANGUAGE_GERMAN,
LANGUAGE_GERMAN_SWISS,
LANGUAGE_GERMAN_AUSTRIAN,
LANGUAGE_GERMAN_LUXEMBOURG,
LANGUAGE_GERMAN_LIECHTENSTEIN,
LANGUAGE_DUTCH,
LANGUAGE_DUTCH_BELGIAN,
LANGUAGE_FRENCH,
LANGUAGE_FRENCH_BELGIAN,
LANGUAGE_FRENCH_CANADIAN,
LANGUAGE_FRENCH_SWISS,
LANGUAGE_FRENCH_LUXEMBOURG,
LANGUAGE_FRENCH_MONACO,
LANGUAGE_FINNISH,
LANGUAGE_ITALIAN,
LANGUAGE_ITALIAN_SWISS,
LANGUAGE_DANISH,
LANGUAGE_NORWEGIAN,
LANGUAGE_NORWEGIAN_BOKMAL,
LANGUAGE_NORWEGIAN_NYNORSK,
LANGUAGE_SWEDISH,
LANGUAGE_SWEDISH_FINLAND,
LANGUAGE_PORTUGUESE,
LANGUAGE_PORTUGUESE_BRAZILIAN,
LANGUAGE_SPANISH_MODERN,
LANGUAGE_SPANISH_DATED,
LANGUAGE_SPANISH_MEXICAN,
LANGUAGE_SPANISH_GUATEMALA,
LANGUAGE_SPANISH_COSTARICA,
LANGUAGE_SPANISH_PANAMA,
LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
LANGUAGE_SPANISH_VENEZUELA,
LANGUAGE_SPANISH_COLOMBIA,
LANGUAGE_SPANISH_PERU,
LANGUAGE_SPANISH_ARGENTINA,
LANGUAGE_SPANISH_ECUADOR,
LANGUAGE_SPANISH_CHILE,
LANGUAGE_SPANISH_URUGUAY,
LANGUAGE_SPANISH_PARAGUAY,
LANGUAGE_SPANISH_BOLIVIA,
LANGUAGE_SPANISH_EL_SALVADOR,
LANGUAGE_SPANISH_HONDURAS,
LANGUAGE_SPANISH_NICARAGUA,
LANGUAGE_SPANISH_PUERTO_RICO ))
{
bL10n = false;
meKeywordLocalization = KeywordLocalization::EnglishOnly;
}
}
// Init the current NfKeywordTable with English keywords.
sKeyword = sEnglishKeyword;
// Set the uppercase localized General name, e.g. Standard -> STANDARD
i18n::NumberFormatCode aFormat = xNFC->getFormatCode( NF_NUMBER_STANDARD, aLoadedLocale.getLocale() );
sNameStandardFormat = lcl_extractStandardGeneralName( aFormat.Code );
sKeyword[NF_KEY_GENERAL] = pCharClass->uppercase( sNameStandardFormat );
// Thai T NatNum special. Other locale's small letter 't' results in upper // case comparison not matching but length does in conversion mode. Ugly. if (eLang == LANGUAGE_THAI)
{
sKeyword[NF_KEY_THAI_T] = "T";
} else
{
sKeyword[NF_KEY_THAI_T] = sEnglishKeyword[NF_KEY_THAI_T];
}
// Boolean equivalent format codes that are written to Excel files, may // have been written to ODF as well, specifically if such loaded Excel file // was saved as ODF, and shall result in proper Boolean again. // "TRUE";"TRUE";"FALSE"
sBooleanEquivalent1 = "\"" + sKeyword[NF_KEY_TRUE] + "\";\"" +
sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\""; // [>0]"TRUE";[<0]"TRUE";"FALSE"
sBooleanEquivalent2 = "[>0]\"" + sKeyword[NF_KEY_TRUE] + "\";[<0]\"" +
sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";
short ImpSvNumberformatScan::GetKeyWord( const OUString& sSymbol, sal_Int32 nPos, bool& rbFoundEnglish ) const
{
OUString sString = mrCurrentLanguageData.GetCharClass()->uppercase( sSymbol, nPos, sSymbol.getLength() - nPos ); const NfKeywordTable & rKeyword = GetKeywords(); // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere if (sString.startsWith( rKeyword[NF_KEY_GENERAL] ))
{ return NF_KEY_GENERAL;
} if ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
sString.startsWith( sEnglishKeyword[NF_KEY_GENERAL]))
{
rbFoundEnglish = true; return NF_KEY_GENERAL;
}
// MUST be a reverse search to find longer strings first, // new keywords take precedence over old keywords, // skip colors et al after keywords. short i = NF_KEY_LASTKEYWORD; while (i > 0 && !sString.startsWith( rKeyword[i]))
{
i--;
} if (i == 0 && meKeywordLocalization == KeywordLocalization::AllowEnglish)
{ // No localized (if so) keyword, try English keywords if keywords // are localized. That was already checked in SetDependentKeywords().
i = NF_KEY_LASTKEYWORD; while (i > 0 && !sString.startsWith( sEnglishKeyword[i]))
{
i--;
}
}
// The Thai T NatNum modifier during Xcl import. if (i == 0 && bConvertMode &&
sString[0] == 'T' &&
eTmpLnge == LANGUAGE_ENGLISH_US &&
MsLangId::getRealLanguage( eNewLnge) == LANGUAGE_THAI)
{
i = NF_KEY_THAI_T;
} return i; // 0 => not found
}
/** * Next_Symbol * * Splits up the input for further processing (by the Turing machine). * * Starting state = SsStar * * ---------------+-------------------+---------------------------+--------------- * Old state | Character read | Event | New state * ---------------+-------------------+---------------------------+--------------- * SsStart | Character | Symbol = Character | SsGetWord * | " | Type = String | SsGetString * | \ | Type = String | SsGetChar * | * | Type = Star | SsGetStar * | _ | Type = Blank | SsGetBlank * | @ # 0 ? / . , % [ | Symbol = Character; | * | ] ' Blank | Type = Control character | SsStop * | $ - + ( ) : | Type = String; | * | Else | Symbol = Character | SsStop * ---------------|-------------------+---------------------------+--------------- * SsGetChar | Else | Symbol = Character | SsStop * ---------------+-------------------+---------------------------+--------------- * GetString | " | | SsStop * | Else | Symbol += Character | GetString * ---------------+-------------------+---------------------------+--------------- * SsGetWord | Character | Symbol += Character | * | + - (E+ E-)| Symbol += Character | SsStop * | / (AM/PM)| Symbol += Character | * | Else | Pos--, if Key Type = Word | SsStop * ---------------+-------------------+---------------------------+--------------- * SsGetStar | Else | Symbol += Character | SsStop * | | Mark special case * | * ---------------+-------------------+---------------------------+--------------- * SsGetBlank | Else | Symbol + =Character | SsStop * | | Mark special case _ | * ---------------------------------------------------------------+-------------- * * If we recognize a keyword in the state SsGetWord (even as the symbol's start text) * we write back the rest of the characters!
*/
short ImpSvNumberformatScan::Next_Symbol( const OUString& rStr,
sal_Int32& nPos,
OUString& sSymbol ) const
{
InitKeywords(); const CharClass* pChrCls = mrCurrentLanguageData.GetCharClass(); const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData(); short eType = 0;
ScanState eState = SsStart;
OUStringBuffer sSymbolBuffer; while ( nPos < rStr.getLength() && eState != SsStop )
{
sal_Unicode cToken = rStr[nPos++]; switch (eState)
{ case SsStart: // Fetch any currency longer than one character and don't get // confused later on by "E/" or other combinations of letters // and meaningful symbols. Necessary for old automatic currency. // #96158# But don't do it if we're starting a "[...]" section, // for example a "[$...]" new currency symbol to not parse away // "$U" (symbol) of "[$UYU]" (abbreviation). if ( nCurrPos >= 0 && sCurString.getLength() > 1 &&
nPos-1 + sCurString.getLength() <= rStr.getLength() &&
(nPos <= 1 || rStr[nPos-2] != '[') )
{
OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) ); if ( aTest == sCurString )
{
sSymbol = rStr.copy( --nPos, sCurString.getLength() );
nPos = nPos + sSymbol.getLength();
eType = NF_SYMBOLTYPE_STRING; return eType;
}
} switch (cToken)
{ case'#': case'0': case'?': case'%': case'@': case'[': case']': case',': case'.': case'/': case'\'': case' ': case':': case'-':
eType = NF_SYMBOLTYPE_DEL;
sSymbolBuffer.append(OUStringChar(cToken));
eState = SsStop; break; case'*':
eType = NF_SYMBOLTYPE_STAR;
sSymbolBuffer.append(OUStringChar(cToken));
eState = SsGetStar; break; case'_':
eType = NF_SYMBOLTYPE_BLANK;
sSymbolBuffer.append(OUStringChar(cToken));
eState = SsGetBlank; break; case'"':
eType = NF_SYMBOLTYPE_STRING;
eState = SsGetString;
sSymbolBuffer.append(OUStringChar(cToken)); break; case'\\':
eType = NF_SYMBOLTYPE_STRING;
eState = SsGetChar;
sSymbolBuffer.append(OUStringChar(cToken)); break; case'$': case'+': case'(': case')':
eType = NF_SYMBOLTYPE_STRING;
eState = SsStop;
sSymbolBuffer.append(OUStringChar(cToken)); break; default : if (StringEqualsChar( mrCurrentLanguageData.GetNumDecimalSep(), cToken) ||
StringEqualsChar( mrCurrentLanguageData.GetNumThousandSep(), cToken) ||
StringEqualsChar( mrCurrentLanguageData.GetDateSep(), cToken) ||
StringEqualsChar( pLoc->getTimeSep(), cToken) ||
StringEqualsChar( pLoc->getTime100SecSep(), cToken))
{ // Another separator than pre-known ASCII
eType = NF_SYMBOLTYPE_DEL;
sSymbolBuffer.append(OUStringChar(cToken));
eState = SsStop;
} elseif ( pChrCls->isLetter( rStr, nPos-1 ) )
{ bool bFoundEnglish = false; short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish); if ( nTmpType )
{ bool bCurrency = false; // "Automatic" currency may start with keyword, // like "R" (Rand) and 'R' (era) if ( nCurrPos >= 0 &&
nPos-1 + sCurString.getLength() <= rStr.getLength() &&
sCurString.startsWith( bFoundEnglish ? sEnglishKeyword[nTmpType] : sKeyword[nTmpType]))
{
OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) ); if ( aTest == sCurString )
{
bCurrency = true;
}
} if ( bCurrency )
{
eState = SsGetWord;
sSymbolBuffer.append(OUStringChar(cToken));
} else
{
eType = nTmpType; // The code to be advanced is the detected keyword, // not necessarily the locale's keyword, but the // symbol is to be the locale's keyword.
sal_Int32 nLen; if (bFoundEnglish)
{
nLen = sEnglishKeyword[eType].getLength(); // Use the locale's General keyword name, not uppercase.
sSymbolBuffer = (eType == NF_KEY_GENERAL ? sNameStandardFormat : sKeyword[eType]);
} else
{
nLen = sKeyword[eType].getLength(); // Preserve a locale's keyword's case as entered.
sSymbolBuffer = rStr.subView( nPos-1, nLen);
} if ((eType == NF_KEY_E || IsAmbiguousE(eType)) && nPos < rStr.getLength())
{
sal_Unicode cNext = rStr[nPos]; switch ( cNext )
{ case'+' : case'-' : // E+ E- combine to one symbol
sSymbolBuffer.append(OUStringChar(cNext));
eType = NF_KEY_E;
nPos++; break; case'0' : case'#' : // scientific E without sign
eType = NF_KEY_E; break;
}
}
nPos--;
nPos = nPos + nLen;
eState = SsStop;
}
} else
{
eState = SsGetWord;
sSymbolBuffer.append(OUStringChar(cToken));
}
} else
{
eType = NF_SYMBOLTYPE_STRING;
eState = SsStop;
sSymbolBuffer.append(OUStringChar(cToken));
} break;
} break; case SsGetChar:
sSymbolBuffer.append(OUStringChar(cToken));
eState = SsStop; break; case SsGetString: if (cToken == '"')
{
eState = SsStop;
}
sSymbolBuffer.append(OUStringChar(cToken)); break; case SsGetWord: if ( pChrCls->isLetter( rStr, nPos-1 ) )
{ bool bFoundEnglish = false; short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish); if ( nTmpType )
{ // beginning of keyword, stop scan and put back
eType = NF_SYMBOLTYPE_STRING;
eState = SsStop;
nPos--;
} else
{
sSymbolBuffer.append(OUStringChar(cToken));
}
} else
{ bool bDontStop = false;
sal_Unicode cNext; switch (cToken)
{ case'/': // AM/PM, A/P if (nPos < rStr.getLength())
{
cNext = rStr[nPos]; if ( cNext == 'P' || cNext == 'p' )
{
sal_Int32 nLen = sSymbolBuffer.getLength(); if ( 1 <= nLen &&
(sSymbolBuffer[0] == 'A' || sSymbolBuffer[0] == 'a') &&
(nLen == 1 ||
(nLen == 2 && (sSymbolBuffer[1] == 'M' || sSymbolBuffer[1] == 'm')
&& (rStr[nPos + 1] == 'M' || rStr[nPos + 1] == 'm'))))
{
sSymbolBuffer.append(OUStringChar(cToken));
bDontStop = true;
}
}
} break;
} // anything not recognized will stop the scan if (!bDontStop)
{
eState = SsStop;
nPos--;
eType = NF_SYMBOLTYPE_STRING;
}
} break; case SsGetStar: case SsGetBlank:
eState = SsStop;
sSymbolBuffer.append(OUStringChar(cToken)); break; default: break;
} // of switch
} // of while if (eState == SsGetWord)
{
eType = NF_SYMBOLTYPE_STRING;
}
sSymbol = sSymbolBuffer.makeStringAndClear(); return eType;
}
sal_Int32 ImpSvNumberformatScan::Symbol_Division(const OUString& rString)
{
nCurrPos = -1; // Do we have some sort of currency?
OUString sString = mrCurrentLanguageData.GetCharClass()->uppercase(rString);
sal_Int32 nCPos = 0; while (nCPos >= 0 && nCPos < sString.getLength())
{
nCPos = sString.indexOf(GetCurString(),nCPos); if (nCPos >= 0)
{ // In Quotes?
sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sString, nCPos ); if ( nQ < 0 )
{
sal_Unicode c; if ( nCPos == 0 ||
((c = sString[nCPos-1]) != '"'
&& c != '\\') ) // dm can be protected by "dm \d
{
nCurrPos = nCPos;
nCPos = -1;
} else
{
nCPos++; // Continue search
}
} else
{
nCPos = nQ + 1; // Continue search
}
}
}
nStringsCnt = 0; bool bStar = false; // Is set on detecting '*'
Reset();
sal_Int32 nPos = 0; const sal_Int32 nLen = rString.getLength(); while (nPos < nLen && nStringsCnt < NF_MAX_FORMAT_SYMBOLS)
{
nTypeArray[nStringsCnt] = Next_Symbol(rString, nPos, sStrArray[nStringsCnt]); if (nTypeArray[nStringsCnt] == NF_SYMBOLTYPE_STAR)
{ // Monitoring the '*' if (bStar)
{ return nPos; // Error: double '*'
} else
{ // Valid only if there is a character following, else we are // at the end of a code that does not have a fill character // (yet?). if (sStrArray[nStringsCnt].getLength() < 2) return nPos;
bStar = true;
}
}
nStringsCnt++;
}
sal_uInt16 ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i) const
{ short res = 0; if (i > 0 && i < nStringsCnt)
{
i--; while (i > 0 && nTypeArray[i] <= 0)
{
i--;
} if (nTypeArray[i] > 0)
{
res = nTypeArray[i];
}
} return res;
}
sal_uInt16 ImpSvNumberformatScan::NextKeyword(sal_uInt16 i) const
{ short res = 0; if (i < nStringsCnt-1)
{
i++; while (i < nStringsCnt-1 && nTypeArray[i] <= 0)
{
i++;
} if (nTypeArray[i] > 0)
{
res = nTypeArray[i];
}
} return res;
}
short ImpSvNumberformatScan::PreviousType( sal_uInt16 i ) const
{ if ( i > 0 && i < nStringsCnt )
{ do
{
i--;
} while ( i > 0 && nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ); return nTypeArray[i];
} return 0;
}
sal_Unicode ImpSvNumberformatScan::PreviousChar(sal_uInt16 i) const
{
sal_Unicode res = ' '; if (i > 0 && i < nStringsCnt)
{
i--; while (i > 0 &&
( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
nTypeArray[i] == NF_SYMBOLTYPE_BLANK ))
{
i--;
} if (sStrArray[i].getLength() > 0)
{
res = sStrArray[i][sStrArray[i].getLength()-1];
}
} return res;
}
sal_Unicode ImpSvNumberformatScan::NextChar(sal_uInt16 i) const
{
sal_Unicode res = ' '; if (i < nStringsCnt-1)
{
i++; while (i < nStringsCnt-1 &&
( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
nTypeArray[i] == NF_SYMBOLTYPE_BLANK))
{
i++;
} if (sStrArray[i].getLength() > 0)
{
res = sStrArray[i][0];
}
} return res;
}
bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i) const
{ bool res = true; if (i < nStringsCnt-1)
{ bool bStop = false;
i++; while (i < nStringsCnt-1 && !bStop)
{
i++; if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
sStrArray[i][0] == '/')
{
bStop = true;
} elseif ( ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
sStrArray[i][0] == ' ') ||
nTypeArray[i] == NF_SYMBOLTYPE_STRING ) // integer/fraction delimiter can also be a string
{
res = false;
}
} if (!bStop) // no '/'{
{
res = false;
}
} else
{
res = false; // no '/' any more
} return res;
}
sal_Int32 nPos = 0;
sal_uInt16 i = 0;
SvNumFormatType eNewType; bool bMatchBracket = false; bool bHaveGeneral = false; // if General/Standard encountered bool bIsTimeDetected =false; // hour or second found in format bool bHaveMinute = false;
SkipStrings(i, nPos); while (i < nStringsCnt)
{ if (nTypeArray[i] > 0)
{ // keyword
sal_uInt16 nIndexPre;
sal_uInt16 nIndexNex;
switch (nTypeArray[i])
{ case NF_KEY_E: // E
eNewType = SvNumFormatType::SCIENTIFIC; break; case NF_KEY_H: // H case NF_KEY_HH: // HH
bIsTimeDetected = true;
[[fallthrough]]; case NF_KEY_S: // S case NF_KEY_SS: // SS if ( !bHaveMinute )
bIsTimeDetected = true;
[[fallthrough]]; case NF_KEY_AMPM: // AM,A,PM,P case NF_KEY_AP:
eNewType = SvNumFormatType::TIME; break; case NF_KEY_M: // M case NF_KEY_MM: // MM case NF_KEY_MI: // M minute detected in Finnish case NF_KEY_MMI: // MM /* Minute or month. Minute if one of: * preceded by time keyword H (ignoring separators) * followed by time keyword S (ignoring separators) * H or S was detected and this is the first M following * preceded by '[' amount bracket Else month. That are the Excel rules. BUT, we break it because certainly in something like {HH YYYY-MM-DD} the MM is NOT meant to be minute, so not if MM is between YY and DD or DD and YY. Actually not if any date specific keyword followed a time setting keyword.
*/
nIndexPre = PreviousKeyword(i);
nIndexNex = NextKeyword(i); if (nIndexPre == NF_KEY_H || // H
nIndexPre == NF_KEY_HH || // HH
nIndexNex == NF_KEY_S || // S
nIndexNex == NF_KEY_SS || // SS
bIsTimeDetected || // tdf#101147
PreviousChar(i) == '[' ) // [M
{
eNewType = SvNumFormatType::TIME; if ( nTypeArray[i] == NF_KEY_M || nTypeArray[i] == NF_KEY_MM )
{
nTypeArray[i] -= 2; // 6 -> 4, 7 -> 5
}
bIsTimeDetected = false; // next M should be month
bHaveMinute = true;
} else
{
eNewType = SvNumFormatType::DATE; if ( nTypeArray[i] == NF_KEY_MI || nTypeArray[i] == NF_KEY_MMI )
{ // follow resolution of tdf#33689 for Finnish
nTypeArray[i] += 2; // 4 -> 6, 5 -> 7
}
} break; case NF_KEY_MMM: // MMM case NF_KEY_MMMM: // MMMM case NF_KEY_MMMMM: // MMMMM case NF_KEY_Q: // Q case NF_KEY_QQ: // QQ case NF_KEY_D: // D case NF_KEY_DD: // DD case NF_KEY_DDD: // DDD case NF_KEY_DDDD: // DDDD case NF_KEY_YY: // YY case NF_KEY_YYYY: // YYYY case NF_KEY_NN: // NN case NF_KEY_NNN: // NNN case NF_KEY_NNNN: // NNNN case NF_KEY_WW : // WW case NF_KEY_AAA : // AAA case NF_KEY_AAAA : // AAAA case NF_KEY_EC : // E case NF_KEY_EEC : // EE case NF_KEY_G : // G case NF_KEY_GG : // GG case NF_KEY_GGG : // GGG case NF_KEY_R : // R case NF_KEY_RR : // RR
eNewType = SvNumFormatType::DATE;
bIsTimeDetected = false; break; case NF_KEY_CCC: // CCC
eNewType = SvNumFormatType::CURRENCY; break; case NF_KEY_BOOLEAN: // BOOLEAN
eNewType = SvNumFormatType::LOGICAL; break; case NF_KEY_GENERAL: // General
eNewType = SvNumFormatType::NUMBER;
bHaveGeneral = true; break; default:
eNewType = SvNumFormatType::UNDEFINED; break;
}
} else
{ // control character switch ( sStrArray[i][0] )
{ case'#': case'?':
eNewType = SvNumFormatType::NUMBER; break; case'0': if ( eScannedType & SvNumFormatType::TIME )
{ if ( Is100SecZero( i, bDecSep ) )
{
bDecSep = true; // subsequent 0's
eNewType = SvNumFormatType::TIME;
} else
{ return nPos; // Error
}
} else
{
eNewType = SvNumFormatType::NUMBER;
} break; case'%':
eNewType = SvNumFormatType::PERCENT; break; case'/':
eNewType = SvNumFormatType::FRACTION; break; case'[': if ( i < nStringsCnt-1 &&
nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
sStrArray[i+1][0] == '$' )
{
eNewType = SvNumFormatType::CURRENCY;
bMatchBracket = true;
} elseif ( i < nStringsCnt-1 &&
nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
sStrArray[i+1][0] == '~' )
{
eNewType = SvNumFormatType::DATE;
bMatchBracket = true;
} else
{
sal_uInt16 nIndexNex = NextKeyword(i); if (nIndexNex == NF_KEY_H || // H
nIndexNex == NF_KEY_HH || // HH
nIndexNex == NF_KEY_M || // M
nIndexNex == NF_KEY_MM || // MM
nIndexNex == NF_KEY_S || // S
nIndexNex == NF_KEY_SS ) // SS
eNewType = SvNumFormatType::TIME; else
{ return nPos; // Error
}
} break; case'@':
eNewType = SvNumFormatType::TEXT; break; default: // Separator for SS,0 if ((eScannedType & SvNumFormatType::TIME)
&& 0 < i && (nTypeArray[i-1] == NF_KEY_S || nTypeArray[i-1] == NF_KEY_SS))
{ // For ISO 8601 only YYYY-MM-DD"T"HH:MM:SS,0 accept both // ',' and '.' regardless of locale's separator, and only // those. // XXX NOTE: this catches only known separators of // NF_SYMBOLTYPE_DEL as all NF_SYMBOLTYPE_STRING are // skipped during the loop. Meant to error out if the // Time100SecSep or decimal separator differ and were used. if ((eScannedType & SvNumFormatType::DATE)
&& 11 <= i && i < nStringsCnt-1
&& (nTypeArray[i-6] == NF_SYMBOLTYPE_STRING
&& (sStrArray[i-6] == "\"T\"" || sStrArray[i-6] == "\\T"
|| sStrArray[i-6] == "T"))
&& (nTypeArray[i-11] == NF_KEY_YYYY)
&& (nTypeArray[i-9] == NF_KEY_M || nTypeArray[i-9] == NF_KEY_MM)
&& (nTypeArray[i-7] == NF_KEY_D || nTypeArray[i-7] == NF_KEY_DD)
&& (nTypeArray[i-5] == NF_KEY_H || nTypeArray[i-5] == NF_KEY_HH)
&& (nTypeArray[i-3] == NF_KEY_MI || nTypeArray[i-3] == NF_KEY_MMI)
&& (nTypeArray[i+1] == NF_SYMBOLTYPE_DEL && sStrArray[i+1][0] == '0'))
{ if (sStrArray[i].getLength() == 1 && (sStrArray[i][0] == ',' || sStrArray[i][0] == '.'))
bDecSep = true; else return nPos; // Error
} elseif (pLoc->getTime100SecSep() == sStrArray[i])
bDecSep = true; elseif ( sStrArray[i][0] == ']' && i < nStringsCnt - 1 && pLoc->getTime100SecSep() == sStrArray[i+1] )
{
bDecSep = true;
i++;
}
}
eNewType = SvNumFormatType::UNDEFINED; break;
}
} if (eScannedType == SvNumFormatType::UNDEFINED)
{
eScannedType = eNewType;
} elseif (eScannedType == SvNumFormatType::TEXT || eNewType == SvNumFormatType::TEXT)
{
eScannedType = SvNumFormatType::TEXT; // Text always remains text
} elseif (eNewType == SvNumFormatType::UNDEFINED)
{ // Remains as is
} elseif (eScannedType != eNewType)
{ switch (eScannedType)
{ case SvNumFormatType::DATE: switch (eNewType)
{ case SvNumFormatType::TIME:
eScannedType = SvNumFormatType::DATETIME; break; case SvNumFormatType::FRACTION: // DD/MM break; default: if (nCurrPos >= 0)
{
eScannedType = SvNumFormatType::UNDEFINED;
} elseif ( sStrArray[i] != mrCurrentLanguageData.GetDateSep() )
{ return nPos;
}
} break; case SvNumFormatType::TIME: switch (eNewType)
{ case SvNumFormatType::DATE:
eScannedType = SvNumFormatType::DATETIME; break; case SvNumFormatType::FRACTION: // MM/SS break; default: if (nCurrPos >= 0)
{
eScannedType = SvNumFormatType::UNDEFINED;
} elseif (pLoc->getTimeSep() != sStrArray[i])
{ return nPos;
} break;
} break; case SvNumFormatType::DATETIME: switch (eNewType)
{ case SvNumFormatType::TIME: case SvNumFormatType::DATE: break; case SvNumFormatType::FRACTION: // DD/MM break; default: if (nCurrPos >= 0)
{
eScannedType = SvNumFormatType::UNDEFINED;
} elseif ( mrCurrentLanguageData.GetDateSep() != sStrArray[i] &&
pLoc->getTimeSep() != sStrArray[i] )
{ return nPos;
}
} break; case SvNumFormatType::PERCENT: case SvNumFormatType::SCIENTIFIC: case SvNumFormatType::FRACTION: switch (eNewType)
{ case SvNumFormatType::NUMBER: break; default: return nPos;
} break; case SvNumFormatType::NUMBER: switch (eNewType)
{ case SvNumFormatType::SCIENTIFIC: case SvNumFormatType::PERCENT: case SvNumFormatType::FRACTION: case SvNumFormatType::CURRENCY:
eScannedType = eNewType; break; default: if (nCurrPos >= 0)
{
eScannedType = SvNumFormatType::UNDEFINED;
} else
{ return nPos;
}
} break; default: break;
}
}
nPos = nPos + sStrArray[i].getLength(); // Position of correction
i++; if ( bMatchBracket )
{ // no type detection inside of matching brackets if [$...], [~...] while ( bMatchBracket && i < nStringsCnt )
{ if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL
&& sStrArray[i][0] == ']' )
{
bMatchBracket = false;
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
}
nPos = nPos + sStrArray[i].getLength();
i++;
} if ( bMatchBracket )
{ return nPos; // missing closing bracket at end of code
}
}
SkipStrings(i, nPos);
}
if ((eScannedType == SvNumFormatType::NUMBER ||
eScannedType == SvNumFormatType::UNDEFINED) &&
nCurrPos >= 0 && !bHaveGeneral)
{
eScannedType = SvNumFormatType::CURRENCY; // old "automatic" currency
} if (eScannedType == SvNumFormatType::UNDEFINED)
{
eScannedType = SvNumFormatType::DEFINED;
} return 0; // All is fine
}
// If the group separator is a No-Break Space (French) continue with a // normal space instead so queries on space work correctly. // The same for Narrow No-Break Space just in case some locale uses it. // The format string is adjusted to allow both. // For output of the format code string the LocaleData characters are used. if ( (sOldThousandSep[0] == cNoBreakSpace || sOldThousandSep[0] == cNarrowNoBreakSpace) &&
sOldThousandSep.getLength() == 1 )
{
sOldThousandSep = " ";
} bool bNewDateOrder = false; // change locale data et al if (bConvertMode)
{
mrCurrentLanguageData.ChangeIntl(eNewLnge); //! pointer may have changed
pLoc = mrCurrentLanguageData.GetLocaleData(); //! init new keywords
InitKeywords(); // Adapt date order to target locale, but Excel does not handle date // particle re-ordering for the target locale when loading documents, // though it does exchange separators, tdf#113889
bNewDateOrder = (mbConvertDateOrder && eOldDateOrder != pLoc->getDateOrder());
} const CharClass* pChrCls = mrCurrentLanguageData.GetCharClass();
sal_Int32 nPos = 0; // error correction position
sal_uInt16 i = 0; // symbol loop counter
sal_uInt16 nCounter = 0; // counts digits
nResultStringsCnt = nStringsCnt; // counts remaining symbols
bDecSep = false; // reset in case already used in TypeCheck bool bThaiT = false; // Thai T NatNum modifier present bool bTimePart = false; bool bDenomin = false; // Set when reading end of denominator
switch (eScannedType)
{ case SvNumFormatType::TEXT: case SvNumFormatType::DEFINED: while (i < nStringsCnt)
{ switch (nTypeArray[i])
{ case NF_SYMBOLTYPE_BLANK: case NF_SYMBOLTYPE_STAR: break; case NF_KEY_GENERAL : // #77026# "General" is the same as "@" break; default: if ( nTypeArray[i] != NF_SYMBOLTYPE_DEL ||
sStrArray[i][0] != '@' )
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
} break;
}
nPos = nPos + sStrArray[i].getLength();
i++;
} // of while break;
case SvNumFormatType::NUMBER: case SvNumFormatType::PERCENT: case SvNumFormatType::CURRENCY: case SvNumFormatType::SCIENTIFIC: case SvNumFormatType::FRACTION: while (i < nStringsCnt)
{ // TODO: rechecking eScannedType is unnecessary. // This switch-case is for eScannedType == SvNumFormatType::FRACTION anyway if (eScannedType == SvNumFormatType::FRACTION && // special case
nTypeArray[i] == NF_SYMBOLTYPE_DEL && // # ### #/#
StringEqualsChar( sOldThousandSep, ' ' ) && // e.g. France or Sweden
StringEqualsChar( sStrArray[i], ' ' ) &&
!bFrac &&
IsLastBlankBeforeFrac(i) )
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING; // del->string
} // No thousands marker
if (nTypeArray[i] == NF_SYMBOLTYPE_BLANK ||
nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
nTypeArray[i] == NF_KEY_CCC || // CCC
nTypeArray[i] == NF_KEY_GENERAL ) // Standard
{ if (nTypeArray[i] == NF_KEY_GENERAL)
{
nThousand = FLAG_STANDARD_IN_FORMAT; if ( bConvertMode )
{
sStrArray[i] = sNameStandardFormat;
}
}
nPos = nPos + sStrArray[i].getLength();
i++;
} elseif (nTypeArray[i] == NF_SYMBOLTYPE_STRING || // No Strings or
nTypeArray[i] > 0) // Keywords
{ if (eScannedType == SvNumFormatType::SCIENTIFIC &&
nTypeArray[i] == NF_KEY_E) // E+
{ if (bExp) // Double
{ return nPos;
}
bExp = true;
nExpPos = i; if (bDecSep)
{
nCntPost = nCounter;
} else
{
nCntPre = nCounter;
}
nCounter = 0;
nTypeArray[i] = NF_SYMBOLTYPE_EXP;
} elseif (eScannedType == SvNumFormatType::FRACTION &&
(sStrArray[i][0] == ' ' || ( nTypeArray[i] == NF_SYMBOLTYPE_STRING && (sStrArray[i][0] < '0' || sStrArray[i][0] > '9') ) ) )
{ if (!bBlank && !bFrac) // Not double or after a /
{ if (bDecSep && nCounter > 0) // Decimal places
{ return nPos; // Error
} if (sStrArray[i][0] == ' ' || nCounter > 0 ) // treat string as integer/fraction delimiter only if there is integer
{
bBlank = true;
nBlankPos = i;
nCntPre = nCounter;
nCounter = 0;
nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
}
} elseif ( sStrArray[i][0] == ' ' )
nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK; elseif ( bFrac && ( nCounter > 0 ) )
bDenomin = true; // following elements are no more part of denominator
} elseif (nTypeArray[i] == NF_KEY_THAI_T)
{
bThaiT = true;
sStrArray[i] = sKeyword[nTypeArray[i]];
} elseif (sStrArray[i][0] >= '0' &&
sStrArray[i][0] <= '9' && !bDenomin) // denominator was not yet found
{
OUString sDiv;
sal_uInt16 j = i; while(j < nStringsCnt && sStrArray[j][0] >= '0' && sStrArray[j][0] <= '9')
{
sDiv += sStrArray[j++];
}
assert(j > 0 && "if i is 0, first iteration through loop is guaranteed by surrounding if condition"); if (std::u16string_view(OUString::number(sDiv.toInt32())) == sDiv)
{ // Found a Divisor while (i < j)
{
nTypeArray[i++] = NF_SYMBOLTYPE_FRAC_FDIV;
}
i = j - 1; // Stop the loop if (nCntPost)
{
nCounter = nCntPost;
} elseif (nCntPre)
{
nCounter = nCntPre;
} // don't artificially increment nCntPre for forced denominator if ( ( eScannedType != SvNumFormatType::FRACTION ) && (!nCntPre) )
{
nCntPre++;
} if ( bFrac )
bDenomin = true; // next content should be treated as outside denominator
}
} else
{ if ( bFrac && ( nCounter > 0 ) )
bDenomin = true; // next content should be treated as outside denominator
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
}
nPos = nPos + sStrArray[i].getLength();
i++;
} elseif (nTypeArray[i] == NF_SYMBOLTYPE_DEL)
{
sal_Unicode cHere = sStrArray[i][0];
sal_Unicode cSaved = cHere; // Handle not pre-known separators in switch.
sal_Unicode cSimplified; if (StringEqualsChar( mrCurrentLanguageData.GetNumThousandSep(), cHere))
{
cSimplified = ',';
} elseif (StringEqualsChar( mrCurrentLanguageData.GetNumDecimalSep(), cHere))
{
cSimplified = '.';
} else
{
cSimplified = cHere;
}
OUString& rStr = sStrArray[i];
switch ( cSimplified )
{ case'#': case'0': case'?': if (nThousand > 0) // #... #
{ return nPos; // Error
} if ( !bDenomin )
{
nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
nPos = nPos + rStr.getLength();
i++;
nCounter++; while (i < nStringsCnt &&
(sStrArray[i][0] == '#' ||
sStrArray[i][0] == '0' ||
sStrArray[i][0] == '?'))
{
nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
nPos = nPos + sStrArray[i].getLength();
nCounter++;
i++;
}
} else// after denominator, treat any character as text
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
} break; case'-': if ( bDecSep && nDecPos+1 == i &&
nTypeArray[nDecPos] == NF_SYMBOLTYPE_DECSEP )
{ // "0.--"
nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
nPos = nPos + rStr.getLength();
i++;
nCounter++; while (i < nStringsCnt &&
(sStrArray[i][0] == '-') )
{ // If more than two dashes are present in // currency formats the last dash will be // interpreted literally as a minus sign. // Has to be this ugly. Period. if ( eScannedType == SvNumFormatType::CURRENCY
&& rStr.getLength() >= 2 &&
(i == nStringsCnt-1 ||
sStrArray[i+1][0] != '-') )
{ break;
}
rStr += sStrArray[i];
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
nCounter++;
i++;
}
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++;
} break; case'.': case',': case'\'': case' ': if ( StringEqualsChar( sOldThousandSep, cSaved ) )
{ // previous char with skip empty
sal_Unicode cPre = PreviousChar(i);
sal_Unicode cNext; if (bExp || bBlank || bFrac)
{ // after E, / or ' ' if ( !StringEqualsChar( sOldThousandSep, ' ' ) )
{
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
i++; // eat it
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING; if ( bFrac && (nCounter > 0) )
bDenomin = true; // end of denominator
}
} elseif (i > 0 && i < nStringsCnt-1 &&
(cPre == '#' || cPre == '0' || cPre == '?') &&
((cNext = NextChar(i)) == '#' || cNext == '0' || cNext == '?')) // #,#
{
nPos = nPos + sStrArray[i].getLength(); if (!bThousand) // only once
{
bThousand = true;
} // Eat it, will be reinserted at proper grouping positions further down.
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
i++;
} elseif (i > 0 && (cPre == '#' || cPre == '0' || cPre == '?')
&& PreviousType(i) == NF_SYMBOLTYPE_DIGIT
&& nThousand < FLAG_STANDARD_IN_FORMAT )
{ // #,,,, if ( StringEqualsChar( sOldThousandSep, ' ' ) )
{ // strange, those French... bool bFirst = true; // set a hard No-Break Space or ConvertMode const OUString& rSepF = mrCurrentLanguageData.GetNumThousandSep(); while ( i < nStringsCnt &&
sStrArray[i] == sOldThousandSep &&
StringEqualsChar( sOldThousandSep, NextChar(i) ) )
{ // last was a space or another space // is following => separator
nPos = nPos + sStrArray[i].getLength(); if ( bFirst )
{
bFirst = false;
rStr = rSepF;
nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
} else
{
rStr += rSepF;
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
}
nThousand++;
i++;
} if ( i < nStringsCnt-1 &&
sStrArray[i] == sOldThousandSep )
{ // something following last space // => space if currency contained, // else separator
nPos = nPos + sStrArray[i].getLength(); if ( (nPos <= nCurrPos &&
nCurrPos < nPos + sStrArray[i+1].getLength()) ||
nTypeArray[i+1] == NF_KEY_CCC ||
(i < nStringsCnt-2 &&
sStrArray[i+1][0] == '[' &&
sStrArray[i+2][0] == '$') )
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
} else
{ if ( bFirst )
{
rStr = rSepF;
nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
} else
{
rStr += rSepF;
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
}
nThousand++;
}
i++;
}
} else
{ do
{
nThousand++;
nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
nPos = nPos + sStrArray[i].getLength();
sStrArray[i] = mrCurrentLanguageData.GetNumThousandSep();
i++;
} while (i < nStringsCnt && sStrArray[i] == sOldThousandSep);
}
} else// any grsep
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + rStr.getLength();
i++; while ( i < nStringsCnt && sStrArray[i] == sOldThousandSep )
{
rStr += sStrArray[i];
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
i++;
}
}
} elseif ( StringEqualsChar( sOldDecSep, cSaved ) )
{ if (bBlank || bFrac) // . behind / or ' '
{ return nPos; // error
} elseif (bExp) // behind E
{
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
i++; // eat it
} elseif (bDecSep) // any .
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + rStr.getLength();
i++; while ( i < nStringsCnt && sStrArray[i] == sOldDecSep )
{
rStr += sStrArray[i];
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
i++;
}
} else
{
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_DECSEP;
sStrArray[i] = mrCurrentLanguageData.GetNumDecimalSep();
bDecSep = true;
nDecPos = i;
nCntPre = nCounter;
nCounter = 0;
i++;
}
} // of else = DecSep else// . without meaning
{ if (cSaved == ' ' &&
eScannedType == SvNumFormatType::FRACTION &&
StringEqualsChar( sStrArray[i], ' ' ) )
{ if (!bBlank && !bFrac) // no dups
{ // or behind / if (bDecSep && nCounter > 0) // dec.
{ return nPos; // error
}
bBlank = true;
nBlankPos = i;
nCntPre = nCounter;
nCounter = 0;
} if ( bFrac && (nCounter > 0) )
bDenomin = true; // next content is not part of denominator
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING; if ( bFrac && (nCounter > 0) )
bDenomin = true; // next content is not part of denominator
nPos = nPos + rStr.getLength();
i++; while (i < nStringsCnt && StringEqualsChar( sStrArray[i], cSaved ) )
{
rStr += sStrArray[i];
nPos = nPos + sStrArray[i].getLength();
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
i++;
}
}
} break; case'/': if (eScannedType == SvNumFormatType::FRACTION)
{ if ( i == 0 ||
(nTypeArray[i-1] != NF_SYMBOLTYPE_DIGIT &&
nTypeArray[i-1] != NF_SYMBOLTYPE_EMPTY) )
{ return nPos ? nPos : 1; // /? not allowed
} elseif (!bFrac || (bDecSep && nCounter > 0))
{
bFrac = true;
nCntPost = nCounter;
nCounter = 0;
nTypeArray[i] = NF_SYMBOLTYPE_FRAC;
nPos = nPos + sStrArray[i].getLength();
i++;
} else// / double or in , in the denominator
{ return nPos; // Error
}
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++;
} break; case'[' : if ( eScannedType == SvNumFormatType::CURRENCY &&
i < nStringsCnt-1 &&
nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
sStrArray[i+1][0] == '$' )
{ // [$DM-xxx]
nPos = nPos + sStrArray[i].getLength(); // [
nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
nPos = nPos + sStrArray[++i].getLength(); // $
sStrArray[i-1] += sStrArray[i]; // [$
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--; if ( ++i >= nStringsCnt )
{ return nPos; // Error
}
nPos = nPos + sStrArray[i].getLength(); // DM
OUString* pStr = &sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_CURRENCY; // convert bool bHadDash = false;
i++; while ( i < nStringsCnt && sStrArray[i][0] != ']' )
{
nPos = nPos + sStrArray[i].getLength(); if ( bHadDash )
{
*pStr += sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
} else
{ if ( sStrArray[i][0] == '-' )
{
bHadDash = true;
pStr = &sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_CURREXT;
} else
{
*pStr += sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
}
}
i++;
} if ( rStr.getLength() && i < nStringsCnt && sStrArray[i][0] == ']' )
{
nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
nPos = nPos + sStrArray[i].getLength();
i++;
} else
{ return nPos; // Error
}
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++;
} break; default: // Other Dels if (eScannedType == SvNumFormatType::PERCENT && cHere == '%')
{
nTypeArray[i] = NF_SYMBOLTYPE_PERCENT;
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
}
nPos = nPos + sStrArray[i].getLength();
i++; break;
} // of switch (Del)
} // of else Del else
{
SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
nPos = nPos + sStrArray[i].getLength();
i++;
}
} // of while if (eScannedType == SvNumFormatType::FRACTION)
{ if (bFrac)
{
nCntExp = nCounter;
} elseif (bBlank)
{
nCntPost = nCounter;
} else
{
nCntPre = nCounter;
}
} else
{ if (bExp)
{
nCntExp = nCounter;
} elseif (bDecSep)
{
nCntPost = nCounter;
} else
{
nCntPre = nCounter;
}
} if (bThousand) // Expansion of grouping separators
{
sal_uInt16 nMaxPos; if (bFrac)
{ if (bBlank)
{
nMaxPos = nBlankPos;
} else
{
nMaxPos = 0; // no grouping
}
} elseif (bDecSep) // decimal separator present
{
nMaxPos = nDecPos;
} elseif (bExp) // 'E' exponent present
{
nMaxPos = nExpPos;
} else// up to end
{
nMaxPos = i;
} // Insert separators at proper positions.
sal_Int32 nCount = 0;
utl::DigitGroupingIterator aGrouping( pLoc->getDigitGrouping());
size_t nFirstDigitSymbol = nMaxPos;
size_t nFirstGroupingSymbol = nMaxPos;
i = nMaxPos; while (i-- > 0)
{ if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
{
nFirstDigitSymbol = i;
nCount = nCount + sStrArray[i].getLength(); // MSC converts += to int and then warns, so ... // Insert separator only if not leftmost symbol. if (i > 0 && nCount >= aGrouping.getPos())
{
DBG_ASSERT( sStrArray[i].getLength() == 1, "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion"); if (!InsertSymbol( i, NF_SYMBOLTYPE_THSEP, mrCurrentLanguageData.GetNumThousandSep()))
{ // nPos isn't correct here, but signals error return nPos;
} // i may have been decremented by 1
nFirstDigitSymbol = i + 1;
nFirstGroupingSymbol = i;
aGrouping.advance();
}
}
} // Generated something like "string",000; remove separator again. if (nFirstGroupingSymbol < nFirstDigitSymbol)
{
nTypeArray[nFirstGroupingSymbol] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
}
} // Combine digits into groups to save memory (Info will be copied // later, taking only non-empty symbols). for (i = 0; i < nStringsCnt; ++i)
{ if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
{
OUString& rStr = sStrArray[i]; while (++i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
{
rStr += sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
}
}
} break; // of SvNumFormatType::NUMBER case SvNumFormatType::DATE: while (i < nStringsCnt)
{ switch (nTypeArray[i])
{ case NF_SYMBOLTYPE_BLANK: case NF_SYMBOLTYPE_STAR: case NF_SYMBOLTYPE_STRING:
nPos = nPos + sStrArray[i].getLength();
i++; break; case NF_SYMBOLTYPE_DEL: int nCalRet; if (sStrArray[i] == sOldDateSep)
{
nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
nPos = nPos + sStrArray[i].getLength(); if (bConvertMode)
{
sStrArray[i] = mrCurrentLanguageData.GetDateSep();
}
i++;
} elseif ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
{ if ( nCalRet < 0 )
{ return nPos; // error
}
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++;
} break; case NF_KEY_THAI_T :
bThaiT = true;
[[fallthrough]]; case NF_KEY_M: // M case NF_KEY_MM: // MM case NF_KEY_MMM: // MMM case NF_KEY_MMMM: // MMMM case NF_KEY_MMMMM: // MMMMM case NF_KEY_Q: // Q case NF_KEY_QQ: // QQ case NF_KEY_D: // D case NF_KEY_DD: // DD case NF_KEY_DDD: // DDD case NF_KEY_DDDD: // DDDD case NF_KEY_YY: // YY case NF_KEY_YYYY: // YYYY case NF_KEY_NN: // NN case NF_KEY_NNN: // NNN case NF_KEY_NNNN: // NNNN case NF_KEY_WW : // WW case NF_KEY_AAA : // AAA case NF_KEY_AAAA : // AAAA case NF_KEY_EC : // E case NF_KEY_EEC : // EE case NF_KEY_G : // G case NF_KEY_GG : // GG case NF_KEY_GGG : // GGG case NF_KEY_R : // R case NF_KEY_RR : // RR
sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
nPos = nPos + sStrArray[i].getLength(); if (bNewDateOrder)
{ // For simple numeric date formats record date order and // later rearrange. switch (nTypeArray[i])
{ case NF_KEY_M: case NF_KEY_MM: if (nMonthPos == SAL_MAX_UINT16)
nMonthPos = i; else
bNewDateOrder = false; break; case NF_KEY_D: case NF_KEY_DD: if (nDayPos == SAL_MAX_UINT16)
nDayPos = i; else
bNewDateOrder = false; break; case NF_KEY_YY: case NF_KEY_YYYY: if (nYearPos == SAL_MAX_UINT16)
nYearPos = i; else
bNewDateOrder = false; break; default:
; // nothing
}
}
i++; break; default: // Other keywords
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++; break;
}
} // of while break; // of SvNumFormatType::DATE case SvNumFormatType::TIME: while (i < nStringsCnt)
{
sal_Unicode cChar;
switch (nTypeArray[i])
{ case NF_SYMBOLTYPE_BLANK: case NF_SYMBOLTYPE_STAR: case NF_SYMBOLTYPE_STRING:
nPos = nPos + sStrArray[i].getLength();
i++; break; case NF_SYMBOLTYPE_DEL: switch( sStrArray[i][0] )
{ case'0': if ( Is100SecZero( i, bDecSep ) )
{
bDecSep = true;
nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
OUString& rStr = sStrArray[i];
nCounter++;
i++; while (i < nStringsCnt &&
sStrArray[i][0] == '0')
{
rStr += sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
nCounter++;
i++;
}
nPos += rStr.getLength();
} else
{ return nPos;
} break; case'#': case'?': return nPos; case'[': if (bThousand) // Double
{ return nPos;
}
bThousand = true; // Empty for Time
cChar = pChrCls->uppercase(OUString(NextChar(i)))[0]; if ( cChar == cOldKeyH )
{
nThousand = 1; // H
} elseif ( cChar == cOldKeyMI )
{
nThousand = 2; // M
} elseif ( cChar == cOldKeyS )
{
nThousand = 3; // S
} else
{ return nPos;
}
nPos = nPos + sStrArray[i].getLength();
i++; break; case']': if (!bThousand) // No preceding [
{ return nPos;
}
nPos = nPos + sStrArray[i].getLength();
i++; break; default:
nPos = nPos + sStrArray[i].getLength(); if ( sStrArray[i] == sOldTimeSep )
{
nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP; if ( bConvertMode )
{
sStrArray[i] = pLoc->getTimeSep();
}
} elseif ( sStrArray[i] == sOldTime100SecSep )
{
bDecSep = true;
nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP; if ( bConvertMode )
{
sStrArray[i] = pLoc->getTime100SecSep();
}
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
}
i++; break;
} break; case NF_KEY_AMPM: // AM/PM case NF_KEY_AP: // A/P
bExp = true; // Abuse for A/P
sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
nPos = nPos + sStrArray[i].getLength();
i++; break; case NF_KEY_THAI_T :
bThaiT = true;
[[fallthrough]]; case NF_KEY_MI: // M case NF_KEY_MMI: // MM case NF_KEY_H: // H case NF_KEY_HH: // HH case NF_KEY_S: // S case NF_KEY_SS: // SS
sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
nPos = nPos + sStrArray[i].getLength();
i++; break; default: // Other keywords
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++; break;
}
} // of while
nCntPost = nCounter; // Zero counter if (bExp)
{
nCntExp = 1; // Remembers AM/PM
} break; // of SvNumFormatType::TIME case SvNumFormatType::DATETIME: while (i < nStringsCnt)
{ int nCalRet; switch (nTypeArray[i])
{ case NF_SYMBOLTYPE_BLANK: case NF_SYMBOLTYPE_STAR: case NF_SYMBOLTYPE_STRING:
nPos = nPos + sStrArray[i].getLength();
i++; break; case NF_SYMBOLTYPE_DEL: if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
{ if ( nCalRet < 0 )
{ return nPos; // Error
}
} else
{ switch( sStrArray[i][0] )
{ case'0': if (bTimePart && Is100SecZero(i, bDecSep) && nCounter < MaxCntPost)
{
bDecSep = true;
nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
OUString& rStr = sStrArray[i];
nCounter++;
i++; while (i < nStringsCnt &&
sStrArray[i][0] == '0' && nCounter < MaxCntPost)
{
rStr += sStrArray[i];
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
nCounter++;
i++;
}
nPos += rStr.getLength();
} else
{ return nPos;
} break; case'#': case'?': return nPos; default:
nPos = nPos + sStrArray[i].getLength(); if (bTimePart)
{ if ( sStrArray[i] == sOldTimeSep )
{
nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP; if ( bConvertMode )
{
sStrArray[i] = pLoc->getTimeSep();
}
} elseif ( sStrArray[i] == sOldTime100SecSep )
{
bDecSep = true;
nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP; if ( bConvertMode )
{
sStrArray[i] = pLoc->getTime100SecSep();
}
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
}
} else
{ if ( sStrArray[i] == sOldDateSep )
{
nTypeArray[i] = NF_SYMBOLTYPE_DATESEP; if (bConvertMode)
sStrArray[i] = mrCurrentLanguageData.GetDateSep();
} else
{
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
}
}
i++; break;
}
} break; case NF_KEY_AMPM: // AM/PM case NF_KEY_AP: // A/P
bTimePart = true;
bExp = true; // Abuse for A/P
sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
nPos = nPos + sStrArray[i].getLength();
i++; break; case NF_KEY_MI: // M case NF_KEY_MMI: // MM case NF_KEY_H: // H case NF_KEY_HH: // HH case NF_KEY_S: // S case NF_KEY_SS: // SS
bTimePart = true;
sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
nPos = nPos + sStrArray[i].getLength();
i++; break; case NF_KEY_M: // M case NF_KEY_MM: // MM case NF_KEY_MMM: // MMM case NF_KEY_MMMM: // MMMM case NF_KEY_MMMMM: // MMMMM case NF_KEY_Q: // Q case NF_KEY_QQ: // QQ case NF_KEY_D: // D case NF_KEY_DD: // DD case NF_KEY_DDD: // DDD case NF_KEY_DDDD: // DDDD case NF_KEY_YY: // YY case NF_KEY_YYYY: // YYYY case NF_KEY_NN: // NN case NF_KEY_NNN: // NNN case NF_KEY_NNNN: // NNNN case NF_KEY_WW : // WW case NF_KEY_AAA : // AAA case NF_KEY_AAAA : // AAAA case NF_KEY_EC : // E case NF_KEY_EEC : // EE case NF_KEY_G : // G case NF_KEY_GG : // GG case NF_KEY_GGG : // GGG case NF_KEY_R : // R case NF_KEY_RR : // RR
bTimePart = false;
sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
nPos = nPos + sStrArray[i].getLength(); if (bNewDateOrder)
{ // For simple numeric date formats record date order and // later rearrange. switch (nTypeArray[i])
{ case NF_KEY_M: case NF_KEY_MM: if (nMonthPos == SAL_MAX_UINT16)
nMonthPos = i; else
bNewDateOrder = false; break; case NF_KEY_D: case NF_KEY_DD: if (nDayPos == SAL_MAX_UINT16)
nDayPos = i; else
bNewDateOrder = false; break; case NF_KEY_YY: case NF_KEY_YYYY: if (nYearPos == SAL_MAX_UINT16)
nYearPos = i; else
bNewDateOrder = false; break; default:
; // nothing
}
}
i++; break; case NF_KEY_THAI_T :
bThaiT = true;
sStrArray[i] = sKeyword[nTypeArray[i]];
nPos = nPos + sStrArray[i].getLength();
i++; break; default: // Other keywords
nTypeArray[i] = NF_SYMBOLTYPE_STRING;
nPos = nPos + sStrArray[i].getLength();
i++; break;
}
} // of while
nCntPost = nCounter; // decimals (100th seconds) if (bExp)
{
nCntExp = 1; // Remembers AM/PM
} break; // of SvNumFormatType::DATETIME default: break;
} if (eScannedType == SvNumFormatType::SCIENTIFIC &&
(nCntPre + nCntPost == 0 || nCntExp == 0))
{ return nPos;
} elseif (eScannedType == SvNumFormatType::FRACTION && (nCntExp > 8 || nCntExp == 0))
{ return nPos;
} if (bThaiT && !GetNatNumModifier())
{
SetNatNumModifier(1);
} if ( bConvertMode )
{ if (bNewDateOrder && sOldDateSep == "-")
{ // Keep ISO formats Y-M-D, Y-M and M-D if (IsDateFragment( nYearPos, nMonthPos))
{
nTypeArray[nYearPos+1] = NF_SYMBOLTYPE_STRING;
sStrArray[nYearPos+1] = sOldDateSep;
bNewDateOrder = false;
} if (IsDateFragment( nMonthPos, nDayPos))
{
nTypeArray[nMonthPos+1] = NF_SYMBOLTYPE_STRING;
sStrArray[nMonthPos+1] = sOldDateSep;
bNewDateOrder = false;
}
} if (bNewDateOrder)
{ // Rearrange date order to the target locale if the original order // includes date separators and is adjacent. /* TODO: for incomplete dates trailing separators need to be * handled according to the locale's usage, e.g. en-US M/D should * be converted to de-DE D.M. and vice versa. As is, it's * M/D -> D.M and D.M. -> M/D/ where specifically the latter looks
* odd. Check accepted date patterns and append/remove? */ switch (eOldDateOrder)
{ case DateOrder::DMY: switch (pLoc->getDateOrder())
{ case DateOrder::MDY: // Convert only if the actual format is not of YDM // order (which would be a completely unusual order // anyway, but..), e.g. YYYY.DD.MM not to // YYYY/MM/DD if (IsDateFragment( nDayPos, nMonthPos) && !IsDateFragment( nYearPos, nDayPos))
SwapArrayElements( nDayPos, nMonthPos); break; case DateOrder::YMD: if (nYearPos != SAL_MAX_UINT16)
{ if (IsDateFragment( nDayPos, nMonthPos) && IsDateFragment( nMonthPos, nYearPos))
SwapArrayElements( nDayPos, nYearPos);
} else
{ if (IsDateFragment( nDayPos, nMonthPos))
SwapArrayElements( nDayPos, nMonthPos);
} break; default:
; // nothing
} break; case DateOrder::MDY: switch (pLoc->getDateOrder())
{ case DateOrder::DMY: // Convert only if the actual format is not of YMD // order, e.g. YYYY/MM/DD not to YYYY.DD.MM /* TODO: convert such to DD.MM.YYYY instead? */ if (IsDateFragment( nMonthPos, nDayPos) && !IsDateFragment( nYearPos, nMonthPos))
SwapArrayElements( nMonthPos, nDayPos); break; case DateOrder::YMD: if (nYearPos != SAL_MAX_UINT16)
{ if (IsDateFragment( nMonthPos, nDayPos) && IsDateFragment( nDayPos, nYearPos))
{
SwapArrayElements( nYearPos, nMonthPos); // YDM
SwapArrayElements( nYearPos, nDayPos); // YMD
}
} break; default:
; // nothing
} break; case DateOrder::YMD: switch (pLoc->getDateOrder())
{ case DateOrder::DMY: if (nYearPos != SAL_MAX_UINT16)
{ if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
SwapArrayElements( nYearPos, nDayPos);
} else
{ if (IsDateFragment( nMonthPos, nDayPos))
SwapArrayElements( nMonthPos, nDayPos);
} break; case DateOrder::MDY: if (nYearPos != SAL_MAX_UINT16)
{ if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
{
SwapArrayElements( nYearPos, nDayPos); // DMY
SwapArrayElements( nYearPos, nMonthPos); // MDY
}
} break; default:
; // nothing
} break; default:
; // nothing
}
} // strings containing keywords of the target locale must be quoted, so // the user sees the difference and is able to edit the format string for ( i=0; i < nStringsCnt; i++ )
{ if ( nTypeArray[i] == NF_SYMBOLTYPE_STRING &&
sStrArray[i][0] != '\"' )
{ if ( bConvertSystemToSystem && eScannedType == SvNumFormatType::CURRENCY )
{ // don't stringize automatic currency, will be converted if ( sStrArray[i] == sOldCurSymbol )
{ continue; // for
} // DM might be split into D and M if ( sStrArray[i].getLength() < sOldCurSymbol.getLength() &&
pChrCls->uppercase( sStrArray[i], 0, 1 )[0] ==
sOldCurString[0] )
{
OUString aTmp( sStrArray[i] );
sal_uInt16 j = i + 1; while ( aTmp.getLength() < sOldCurSymbol.getLength() &&
j < nStringsCnt &&
nTypeArray[j] == NF_SYMBOLTYPE_STRING )
{
aTmp += sStrArray[j++];
} if ( pChrCls->uppercase( aTmp ) == sOldCurString )
{
sStrArray[i++] = aTmp; for ( ; i<j; i++ )
{
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
}
i = j - 1; continue; // for
}
}
}
OUString& rStr = sStrArray[i];
sal_Int32 nLen = rStr.getLength(); for ( sal_Int32 j = 0; j < nLen; j++ )
{ bool bFoundEnglish = false; if ( (j == 0 || rStr[j - 1] != '\\') && GetKeyWord( rStr, j, bFoundEnglish) )
{
rStr = "\"" + rStr + "\""; break; // for
}
}
}
}
} // Concatenate strings, remove quotes for output, and rebuild the format string
rString.clear();
i = 0; while (i < nStringsCnt)
{
sal_Int32 nStringPos;
sal_Int32 nArrPos = 0;
sal_uInt16 iPos = i; switch ( nTypeArray[i] )
{ case NF_SYMBOLTYPE_STRING : case NF_SYMBOLTYPE_FRACBLANK :
nStringPos = rString.getLength(); do
{ if (sStrArray[i].getLength() == 2 &&
sStrArray[i][0] == '\\')
{ // Unescape some simple forms of symbols even in the UI // visible string to prevent duplicates that differ // only in notation, originating from import. // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical, // but 0\ 000 0 and 0 000 0 in a French locale are not.
if ( i < nStringsCnt )
{
i--; // enter switch on next symbol again
} if ( eScannedType == SvNumFormatType::CURRENCY && nStringPos < rString.getLength() )
{ // same as above, since last RemoveQuotes
OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
sStrArray[iPos].getLength()-nArrPos ) );
sal_Int32 nCPos = aTmp.indexOf( sOldCurString ); if ( nCPos >= 0 )
{ const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
GetCurSymbol() : sOldCurSymbol;
sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
sOldCurString.getLength(),
rCur );
rString = rString.replaceAt( nStringPos + nCPos,
sOldCurString.getLength(), rCur );
}
} break; case NF_SYMBOLTYPE_CURRENCY :
rString += sStrArray[i];
RemoveQuotes( sStrArray[i] ); break; case NF_KEY_THAI_T: if (bThaiT && GetNatNumModifier() == 1)
{ // Remove T from format code, will be replaced with a [NatNum1] prefix.
nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
nResultStringsCnt--;
} else
{
rString += sStrArray[i];
} break; case NF_SYMBOLTYPE_EMPTY : // nothing break; default:
rString += sStrArray[i];
}
i++;
} return 0;
}
sal_Int32 ImpSvNumberformatScan::RemoveQuotes( OUString& rStr )
{ if ( rStr.getLength() > 1 )
{
sal_Unicode c = rStr[0];
sal_Int32 n = rStr.getLength() - 1; if ( c == '"' && rStr[n] == '"' )
{
rStr = rStr.copy( 1, n-1); return 2;
} elseif ( c == '\\' )
{
rStr = rStr.copy(1); return 1;
}
} return 0;
}
sal_Int32 ImpSvNumberformatScan::ScanFormat( OUString& rString )
{
sal_Int32 res = Symbol_Division(rString); // Lexical analysis if (!res)
{
res = ScanType(); // Recognizing the Format type
} if (!res)
{
res = FinalScan( rString ); // Type dependent final analysis
} return res; // res = control position; res = 0 => Format ok
}
bool ImpSvNumberformatScan::ReplaceBooleanEquivalent( OUString& rString )
{
InitKeywords(); /* TODO: compare case insensitive? Or rather leave as is and case not * matching indicates user supplied on purpose? Written to file / generated
* was always uppercase. */ if (rString == sBooleanEquivalent1 || rString == sBooleanEquivalent2)
{
rString = GetKeywords()[NF_KEY_BOOLEAN]; returntrue;
} returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.