/* -*- 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)
{
--> --------------------
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.