/* -*- 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 .
*/
sal_uInt32 SvXMLNumImpData::GetKeyForName( std::u16string_view rName )
{ for (constauto& rObj : m_NameEntries)
{ if (rObj.aName == rName) return rObj.nKey; // found
} return NUMBERFORMAT_ENTRY_NOT_FOUND;
}
void SvXMLNumImpData::AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse )
{ if ( bRemoveAfterUse )
{ // if there is already an entry for this key without the bRemoveAfterUse flag, // clear the flag for this entry, too
for (constauto& rObj : m_NameEntries)
{ if (rObj.nKey == nKey && !rObj.bRemoveAfterUse)
{
bRemoveAfterUse = false; // clear flag for new entry break;
}
}
} else
{ // call SetUsed to clear the bRemoveAfterUse flag for other entries for this key
SetUsed( nKey );
}
void SvXMLNumImpData::SetUsed( sal_uInt32 nKey )
{ for (auto& rObj : m_NameEntries)
{ if (rObj.nKey == nKey)
{
rObj.bRemoveAfterUse = false; // used -> don't remove
// continue searching - there may be several entries for the same key // (with different names), the format must not be deleted if any one of // them is used
}
}
}
void SvXMLNumImpData::RemoveVolatileFormats()
{ // remove temporary (volatile) formats from NumberFormatter // called at the end of each import (styles and content), so volatile formats // from styles can't be used in content
if ( !pFormatter ) return;
for (constauto& rObj : m_NameEntries)
{ if (rObj.bRemoveAfterUse )
{ const SvNumberformat* pFormat = pFormatter->GetEntry(rObj.nKey); if (pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED))
pFormatter->DeleteEntry(rObj.nKey);
}
}
}
// Treat space equal to non-breaking space separator. const sal_Unicode cNBSP = 0x00A0;
sal_Unicode cTS; if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
(cChar == (cTS = rParent.GetLocaleData().getNumThousandSep()[0]) ||
(cChar == ' ' && cTS == cNBSP)) )
{ // #i22394# Extra occurrences of thousands separator must be quoted, so they // aren't mis-interpreted as display-factor. // This must be limited to the format types that can contain a number element, // because the same character can be a date separator that should not be quoted // in date formats.
returnfalse; // force quotes
}
// see ImpSvNumberformatScan::Next_Symbol
// All format types except BOOLEAN may contain minus sign or delimiter. if ( cChar == '-' ) return nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE;
// percent sign must be used without quotes for percentage styles only if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && cChar == '%' ) returntrue;
// don't put quotes around single parentheses (often used for negative numbers) if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
( cChar == '(' || cChar == ')' ) ) returntrue;
if (nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE &&
((nLength == 1 && lcl_ValidChar( rContent[0], rParent)) ||
(nLength == 2 &&
((rContent[0] == ' ' && rContent[1] == '-') ||
(rContent[1] == ' ' && lcl_ValidChar( rContent[0], rParent))))))
{ // Don't quote single separator characters like space or percent, // or separator characters followed by space (used in date formats). // Or space followed by minus (used in currency formats) that would // lead to almost duplicated formats with built-in formats just with // the difference of quotes.
bQuote = false;
} elseif ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && nLength > 1 )
{ // the percent character in percentage styles must be left out of quoting // (one occurrence is enough even if there are several percent characters in the string)
sal_Int32 nPos = rContent.indexOf( '%' ); if ( nPos >= 0 )
{ if ( nPos + 1 < nLength )
{ if ( nPos + 2 == nLength && lcl_ValidChar( rContent[nPos + 1], rParent ) )
{ // single character that doesn't need quoting
} else
{ // quote text behind percent character
rContent.insert( nPos + 1, '"' );
rContent.append( '"' );
}
} if ( nPos > 0 )
{ if ( nPos == 1 && lcl_ValidChar( rContent[0], rParent ) )
{ // single character that doesn't need quoting
} else
{ // quote text before percent character
rContent.insert( nPos, '"' );
rContent.insert( 0, '"' );
}
}
bQuote = false;
} // else: normal quoting (below)
}
if ( !bQuote ) return;
// #i55469# quotes in the string itself have to be escaped bool bEscape = ( rContent.indexOf( '"' ) >= 0 ); if ( bEscape )
{ // A quote is turned into "\"" - a quote to end quoted text, an escaped quote, // and a quote to resume quoting.
OUString aInsert( u"\"\\\""_ustr );
namespace { void lcl_InsertBlankWidthChars( std::u16string_view rBlankWidthString, OUStringBuffer& rContent )
{
sal_Int32 nShiftPosition = 1; // rContent starts with a quote const size_t nLenBlank = rBlankWidthString.size(); for ( size_t i = 0 ; i < nLenBlank ; i++ )
{
sal_Unicode nChar = rBlankWidthString[ i ];
OUString aBlanks;
SvNumberformat::InsertBlanks( aBlanks, 0, nChar );
sal_Int32 nPositionContent = 0; if ( ++i < nLenBlank )
{
sal_Int32 nNext = rBlankWidthString.find( '_', i ); if ( static_cast<sal_Int32>( i ) < nNext )
{
nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i, nNext - i ) );
i = nNext;
} else
nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i ) );
}
nPositionContent += nShiftPosition; if ( nPositionContent >= 0 )
{
rContent.remove( nPositionContent, aBlanks.getLength() ); if ( nPositionContent >= 1 && rContent[ nPositionContent-1 ] == '\"' )
{
nPositionContent--;
rContent.insert( nPositionContent, nChar );
rContent.insert( nPositionContent, '_' );
} else
{
rContent.insert( nPositionContent, '\"' );
rContent.insert( nPositionContent, nChar );
rContent.insert( nPositionContent, "\"_" );
nShiftPosition += 2;
} // rContent length was modified: remove blanks, add "_x"
nShiftPosition += 2 - aBlanks.getLength();
}
} // remove empty string at the end of rContent if ( std::u16string_view( rContent ).substr( rContent.getLength() - 2 ) == u"\"\"" )
{
sal_Int32 nLen = rContent.getLength(); if ( nLen >= 3 && rContent[ nLen-3 ] != '\\' )
rContent.truncate( nLen - 2 );
}
}
}
void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContentEmbedded, std::u16string_view rBlankWidthString )
{ if ( rContentEmbedded.empty() ) return;
OUStringBuffer aContentEmbedded( rContentEmbedded ); // #107805# always quote embedded strings - even space would otherwise // be recognized as thousands separator in French.
aContentEmbedded.insert( 0, '"' );
aContentEmbedded.append( '"' ); if ( !rBlankWidthString.empty() )
lcl_InsertBlankWidthChars( rBlankWidthString, aContentEmbedded );
auto iterPair = aNumInfo.m_EmbeddedElements.emplace( nFormatPos, aContentEmbedded.toString() ); if (!iterPair.second)
{ // there's already an element at this position - append text to existing element if ( iterPair.first->second.endsWith( "\"" ) && aContentEmbedded[ 0 ] == '"' )
{ // remove double quote
iterPair.first->second = OUString::Concat( iterPair.first->second.subView( 0, iterPair.first->second.getLength() - 1 ) )
+ aContentEmbedded.subView( 1, aContentEmbedded.getLength() - 1 );
} else
iterPair.first->second += aContentEmbedded;
}
}
void SvXMLNumFmtElementContext::endFastElement(sal_Int32 )
{ bool bEffLong = bLong; switch (nType)
{ case SvXMLStyleTokens::Text: if ( rParent.HasLongDoW() &&
std::u16string_view(aContent) == rParent.GetLocaleData().getLongDateDayOfWeekSep() )
{ // skip separator constant after long day of week // (NF_KEY_NNNN contains the separator)
if ( rParent.ReplaceNfKeyword( NF_KEY_NNN, NF_KEY_NNNN ) )
{
aContent.truncate();
}
rParent.SetHasLongDoW( false ); // only once
} if ( !aContent.isEmpty() )
{
lcl_EnquoteIfNecessary( aContent, rParent ); if ( !sBlankWidthString.isEmpty() )
{
lcl_InsertBlankWidthChars( sBlankWidthString, aContent );
sBlankWidthString = "";
}
rParent.AddToCode( aContent );
aContent.setLength(0);
} else
{ // Quoted empty text may be significant to separate.
aContent.append("\"\"");
rParent.AddToCode( aContent );
aContent.setLength(0);
rParent.SetHasTrailingEmptyText(true); // *after* AddToCode()
} break;
case SvXMLStyleTokens::Number:
rParent.AddNumber( aNumInfo ); break;
case SvXMLStyleTokens::CurrencySymbol:
rParent.AddCurrency( aContent.makeStringAndClear(), nElementLang ); break;
case SvXMLStyleTokens::TextContent:
rParent.AddToCode( '@'); break; case SvXMLStyleTokens::FillCharacter: if ( !aContent.isEmpty() )
{
rParent.AddToCode( '*' );
rParent.AddToCode( aContent[0] );
} break; case SvXMLStyleTokens::Boolean:
rParent.AddNfKeyword( NF_KEY_BOOLEAN ); break;
case SvXMLStyleTokens::Day:
rParent.UpdateCalendar( sCalendar ); //! I18N doesn't provide SYSTEM or extended date information yet
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_DD : NF_KEY_D ) ); break; case SvXMLStyleTokens::Month:
rParent.UpdateCalendar( sCalendar ); //! I18N doesn't provide SYSTEM or extended date information yet
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bTextual
? ( bEffLong ? NF_KEY_MMMM : NF_KEY_MMM )
: ( bEffLong ? NF_KEY_MM : NF_KEY_M ) ) ); break; case SvXMLStyleTokens::Year: //! I18N doesn't provide SYSTEM or extended date information yet
{ // Y after G (era) is replaced by E for a secondary calendar. // Do not replace for default calendar. // Also replace Y by E if we're switching to the secondary // calendar of a locale if it is known to implicitly use E.
rParent.UpdateCalendar( sCalendar); const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState(); if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
|| eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
{
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_EEC : NF_KEY_EC ) );
} else
{
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_YYYY : NF_KEY_YY ) );
}
} break; case SvXMLStyleTokens::Era:
rParent.UpdateCalendar( sCalendar ); //! I18N doesn't provide SYSTEM or extended date information yet
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_GGG : NF_KEY_G ) ); // HasEra flag is set break; case SvXMLStyleTokens::DayOfWeek: //! I18N doesn't provide SYSTEM or extended date information yet
{ // Implicit secondary calendar uses A keyword, default and // explicit calendar N keyword.
rParent.UpdateCalendar( sCalendar); const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState(); if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
|| eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
{
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_AAAA : NF_KEY_AAA ) );
} else
{
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_NNNN : NF_KEY_NN ) );
}
} break; case SvXMLStyleTokens::WeekOfYear:
rParent.UpdateCalendar( sCalendar );
rParent.AddNfKeyword( NF_KEY_WW ); break; case SvXMLStyleTokens::Quarter:
rParent.UpdateCalendar( sCalendar );
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_QQ : NF_KEY_Q ) ); break; case SvXMLStyleTokens::Hours:
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_HH : NF_KEY_H ) ); break; case SvXMLStyleTokens::AmPm: //! short/long?
rParent.AddNfKeyword( NF_KEY_AMPM ); break; case SvXMLStyleTokens::Minutes:
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_MMI : NF_KEY_MI ) ); break; case SvXMLStyleTokens::Seconds:
rParent.AddNfKeyword(
sal::static_int_cast< sal_uInt16 >(
bEffLong ? NF_KEY_SS : NF_KEY_S ) ); if ( aNumInfo.nDecimals > 0 )
{ // manually add the decimal places
rParent.AddToCode(rParent.GetLocaleData().getNumDecimalSep()); for (sal_Int32 i=0; i<aNumInfo.nDecimals; i++)
{
rParent.AddToCode( '0');
}
} break;
case SvXMLStyleTokens::Fraction:
{ if ( aNumInfo.nInteger >= 0 )
{ // add integer part only if min-integer-digits attribute is there
aNumInfo.nDecimals = 0;
rParent.AddNumber( aNumInfo ); // number without decimals
OUStringBuffer sIntegerFractionDelimiter(aNumInfo.aIntegerFractionDelimiter);
lcl_EnquoteIfNecessary( sIntegerFractionDelimiter, rParent );
rParent.AddToCode( sIntegerFractionDelimiter ); // default is ' '
}
//! build string and add at once
sal_Int32 i; for (i=aNumInfo.nMaxNumerDigits; i > 0; i--)
{ if ( i > aNumInfo.nMinNumerDigits )
rParent.AddToCode( '#' ); elseif ( i > aNumInfo.nZerosNumerDigits )
rParent.AddToCode( '?' ); else
rParent.AddToCode( '0' );
}
rParent.AddToCode( '/' ); if ( aNumInfo.nFracDenominator > 0 )
{
rParent.AddToCode( OUString::number( aNumInfo.nFracDenominator ) );
} else
{ for (i=aNumInfo.nMaxDenomDigits; i > 0 ; i--)
{ if ( i > aNumInfo.nMinDenomDigits )
rParent.AddToCode( '#' ); elseif ( i > aNumInfo.nZerosDenomDigits )
rParent.AddToCode( '?' ); else
rParent.AddToCode( '0' );
}
}
} break;
case SvXMLStyleTokens::ScientificNumber:
{ // exponential interval for engineering notation if( !aNumInfo.bGrouping && aNumInfo.nExpInterval > aNumInfo.nInteger )
{ for (sal_Int32 i=aNumInfo.nInteger; i<aNumInfo.nExpInterval; i++)
{
rParent.AddToCode( '#' );
}
}
rParent.AddNumber( aNumInfo ); // number and exponent
} break;
default:
assert(false && "invalid element ID");
}
}
switch (nElement)
{ case XML_ELEMENT(LO_EXT, XML_TEXT): case XML_ELEMENT(NUMBER, XML_TEXT):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Text, xAttrList ); break; case XML_ELEMENT(LO_EXT, XML_FILL_CHARACTER): case XML_ELEMENT(NUMBER, XML_FILL_CHARACTER):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::FillCharacter, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_NUMBER):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Number, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_SCIENTIFIC_NUMBER):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::ScientificNumber, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_FRACTION):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Fraction, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_CURRENCY_SYMBOL):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::CurrencySymbol, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_DAY):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Day, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_MONTH):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Month, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_YEAR):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Year, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_ERA):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Era, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_DAY_OF_WEEK):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::DayOfWeek, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_WEEK_OF_YEAR):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::WeekOfYear, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_QUARTER):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Quarter, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_HOURS):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Hours, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_AM_PM):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::AmPm, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_MINUTES):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Minutes, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_SECONDS):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Seconds, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_BOOLEAN):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::Boolean, xAttrList ); break; case XML_ELEMENT(NUMBER, XML_TEXT_CONTENT):
pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
*this, SvXMLStyleTokens::TextContent, xAttrList ); break;
case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES):
pContext = new SvXMLNumFmtPropContext( GetImport(), nElement,
*this, xAttrList ); break; case XML_ELEMENT(STYLE, XML_MAP):
{ // SvXMLNumFmtMapContext::EndElement adds to aMyConditions, // so there's no need for an extra flag
pContext = new SvXMLNumFmtMapContext( GetImport(), nElement,
*this, xAttrList );
} break;
}
if( !pContext )
{
SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
pContext = new SvXMLImportContext(GetImport());
}
return pContext;
}
sal_Int32 SvXMLNumFormatContext::GetKey()
{ if (m_nKey > -1)
{ if (m_bRemoveAfterUse)
{ // format is used -> don't remove
m_bRemoveAfterUse = false; if (m_pData)
m_pData->SetUsed(m_nKey);
// Add to import's list of keys now - CreateAndInsert didn't add // the style if bRemoveAfterUse was set.
GetImport().AddNumberStyle( m_nKey, GetName() );
} return m_nKey;
} else
{ // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set
m_bRemoveAfterUse = false;
CreateAndInsert(true); return m_nKey;
}
}
sal_Int32 SvXMLNumFormatContext::PrivateGetKey(std::vector<SvXMLNumFormatContext*>& rCreateStack)
{ // used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag
sal_Int32 SvXMLNumFormatContext::CreateAndInsert(SvNumberFormatter* pFormatter, std::vector<SvXMLNumFormatContext*>& rCreateStack)
{ if (!pFormatter)
{
OSL_FAIL("no number formatter"); return -1;
}
rCreateStack.push_back(this);
sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
for (size_t i = 0; i < m_aMyConditions.size(); i++)
{
SvXMLNumFormatContext* pStyle = const_cast<SvXMLNumFormatContext*>( static_cast<const SvXMLNumFormatContext *>(m_pStyles->FindStyleChildContext(
XmlStyleFamily::DATA_STYLE, m_aMyConditions[i].sMapName))); if (std::find(rCreateStack.begin(), rCreateStack.end(), pStyle) != rCreateStack.end())
{
SAL_INFO("xmloff.style", "invalid style:map references containing style");
pStyle = nullptr;
} if (pStyle)
{ if (pStyle->PrivateGetKey(rCreateStack) > -1) // don't reset pStyle's bRemoveAfterUse flag
AddCondition(i);
}
}
sal_Int32 nBufLen; if ( m_aFormatCode.isEmpty() )
{ // insert empty format as empty string (with quotes) // #93901# this check has to be done before inserting the conditions
m_aFormatCode.append("\"\""); // ""
} elseif (m_bHasTrailingEmptyText && (nBufLen = m_aFormatCode.getLength()) >= 3)
{ // Remove a trailing empty text. Earlier this may had been written to // file, like in "General;General" written with elements for // 'General"";General""' (whyever); when reading, empty text was // ignored, which it isn't anymore, so get rid of those. if (m_aFormatCode[nBufLen-1] == '"' && m_aFormatCode[nBufLen-2] == '"')
m_aFormatCode.truncate( nBufLen - 2);
}
if ( m_bAutoDec ) // automatic decimal places
{ // #99391# adjust only if the format contains no text elements, no conditions // and no color definition (detected by the '[' at the start)
if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
m_aMyConditions.empty() && sFormat.toChar() != '[' )
nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
} if ( m_bAutoInt ) // automatic integer digits
{ //! only if two decimal places was set?
// check for default date formats if ( m_nType == SvXMLStylesTokens::DATE_STYLE && m_bAutoOrder && !m_bDateNoDefault )
{
NfIndexTableOffset eFormat = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
m_eDateDOW, m_eDateDay, m_eDateMonth, m_eDateYear,
m_eDateHours, m_eDateMins, m_eDateSecs, m_bFromSystem )); if ( eFormat < NF_INDEX_TABLE_RESERVED_START )
{ // #109651# if a date format has the automatic-order attribute and // contains exactly the elements of one of the default date formats, // use that default format, with the element order and separators // from the current locale settings
if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND && !sFormat.isEmpty() )
{ // insert by format string
OUString aFormatStr( sFormat );
nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang ); if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
{
sal_Int32 nErrPos = 0;
SvNumFormatType l_nType = SvNumFormatType::ALL; bool bOk = pFormatter->PutEntry( aFormatStr, nErrPos, l_nType, nIndex, m_nFormatLang ); if ( !bOk && nErrPos == 0 && aFormatStr != sFormat )
{ // if the string was modified by PutEntry, look for an existing format // with the modified string
nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang ); if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
bOk = true;
} if (!bOk)
nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
}
}
//! I18N doesn't provide SYSTEM or extended date information yet if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND && !m_bAutoOrder )
{ // use fixed-order formats instead of SYS... if bAutoOrder is false // (only if the format strings are equal for the locale)
// Add to import's list of keys (shared between styles and content import) // only if not volatile - formats are removed from NumberFormatter at the // end of each import (in SvXMLNumFmtHelper dtor). // If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there.
if (!m_bRemoveAfterUse)
GetImport().AddNumberStyle( m_nKey, GetName() );
void SvXMLNumFormatContext::AddToCode( sal_Unicode c )
{
m_aFormatCode.append( c );
m_bHasExtraText = true;
}
void SvXMLNumFormatContext::AddToCode( std::u16string_view rString )
{
m_aFormatCode.append( rString );
m_bHasExtraText = true;
m_bHasTrailingEmptyText = false; // is set by caller again if so
}
if ( m_bAutoDec )
{ if ( m_nType == SvXMLStylesTokens::CURRENCY_STYLE )
{ // for currency formats, "automatic decimals" is used for the automatic // currency format with (fixed) decimals from the locale settings
const LocaleDataWrapper& rLoc = m_pData->GetLocaleData( m_nFormatLang );
nPrec = rLoc.getCurrDigits();
} else
{ // for other types, "automatic decimals" means dynamic determination of // decimals, as achieved with the "general" keyword
sal_uInt16 nGenPrec = nPrec; if ( rInfo.nMinDecimalDigits >= 0 )
nGenPrec = rInfo.nMinDecimalDigits; if ( rInfo.bDecReplace )
nGenPrec = 0; // generate format without decimals...
bool bGrouping = rInfo.bGrouping;
size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size(); if ( nEmbeddedCount && rInfo.m_EmbeddedElements.rbegin()->first > 0 )
bGrouping = false; // grouping and embedded characters in integer part can't be used together
if ( rInfo.nExpDigits >= 0 && nLeading == 0 && !bGrouping && nEmbeddedCount == 0 )
{ // #i43959# For scientific numbers, "#" in the integer part forces a digit, // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0").
// Scientific number
sal_Int32 nExpPos = -1; if ( rInfo.nExpDigits > 0 )
{
nExpPos = aNumStr.getLength();
aNumStr.append( rInfo.bExponentLowercase ? u"e" : u"E" ); // exponent sign is required with embedded text in exponent if ( rInfo.bExpSign || ( nEmbeddedCount && ( rInfo.nDecimals + 1 < -rInfo.m_EmbeddedElements.begin()->first ) ) )
{
aNumStr.append( u"+" );
} for (sal_Int32 i=0; i<rInfo.nExpDigits; i++)
{ if ( i < rInfo.nBlankExp )
aNumStr.append( '?' ); else
aNumStr.append( '0' );
}
}
if ( nEmbeddedCount )
{ // insert embedded strings into number string // support integer (position >=0) and decimal (position <0) part // nZeroPos is the string position where format position 0 is inserted
// m_EmbeddedElements is sorted - last entry has the largest position (leftmost)
sal_Int32 const nLastFormatPos = rInfo.m_EmbeddedElements.rbegin()->first; if ( nLastFormatPos >= nZeroPos )
{ // add '#' characters so all embedded texts are really embedded in digits // (there always has to be a digit before the leftmost embedded text)
bAutomatic = true;
}
} elseif ( nLang == LANGUAGE_SYSTEM && aSymbol == "CCC" )
{ // "CCC" is used for automatic long symbol
bAutomatic = true;
}
if ( bAutomatic )
{ // remove unnecessary quotes before automatic symbol (formats like "-(0DM)") // otherwise the currency symbol isn't recognized (#94048#)
sal_Int32 nLength = m_aFormatCode.getLength(); if ( nLength > 1 && m_aFormatCode[nLength - 1] == '"' )
{ // find start of quoted string // When SvXMLNumFmtElementContext::EndElement creates escaped quotes, // they must be handled here, too.
if (!bAutomatic)
m_aFormatCode.append( "[$" ); // intro for "new" currency symbols
m_aFormatCode.append( aSymbol );
if (!bAutomatic)
{ if ( nLang != LANGUAGE_SYSTEM )
{ // '-' sign and language code in hex:
m_aFormatCode.append("-" + OUString(OUString::number(sal_uInt16(nLang), 16)).toAsciiUpperCase());
}
m_aFormatCode.append( ']' ); // end of "new" currency symbol
}
}
if ( nIndex == NF_KEY_H || nIndex == NF_KEY_HH ||
nIndex == NF_KEY_MI || nIndex == NF_KEY_MMI ||
nIndex == NF_KEY_S || nIndex == NF_KEY_SS )
{ if ( !m_bTruncate && !m_bHasDateTime )
{ // with truncate-on-overflow = false, add "[]" to first time part
m_aFormatCode.append("[" + sKeyword + "]");
} else
{
m_aFormatCode.append( sKeyword );
}
m_bHasDateTime = true;
} else
{
m_aFormatCode.append( sKeyword );
} // collect the date elements that the format contains, to recognize default date formats switch ( nIndex )
{ case NF_KEY_NN: m_eDateDOW = XML_DEA_SHORT; break; case NF_KEY_NNN: case NF_KEY_NNNN: m_eDateDOW = XML_DEA_LONG; break; case NF_KEY_D: m_eDateDay = XML_DEA_SHORT; break; case NF_KEY_DD: m_eDateDay = XML_DEA_LONG; break; case NF_KEY_M: m_eDateMonth = XML_DEA_SHORT; break; case NF_KEY_MM: m_eDateMonth = XML_DEA_LONG; break; case NF_KEY_MMM: m_eDateMonth = XML_DEA_TEXTSHORT; break; case NF_KEY_MMMM: m_eDateMonth = XML_DEA_TEXTLONG; break; case NF_KEY_YY: m_eDateYear = XML_DEA_SHORT; break; case NF_KEY_YYYY: m_eDateYear = XML_DEA_LONG; break; case NF_KEY_H: m_eDateHours = XML_DEA_SHORT; break; case NF_KEY_HH: m_eDateHours = XML_DEA_LONG; break; case NF_KEY_MI: m_eDateMins = XML_DEA_SHORT; break; case NF_KEY_MMI: m_eDateMins = XML_DEA_LONG; break; case NF_KEY_S: m_eDateSecs = XML_DEA_SHORT; break; case NF_KEY_SS: m_eDateSecs = XML_DEA_LONG; break; case NF_KEY_AP: case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself default:
m_bDateNoDefault = true; // any other element -> no default format
}
}
bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld, sal_uInt16 nNew )
{ // replaces one keyword with another if it is found at the end of the code
SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); if (!pFormatter) returnfalse;
//! test for valid conditions //! test for default conditions
bool bDefaultCond = false;
//! collect all conditions first and adjust default to >=0, >0 or <0 depending on count //! allow blanks in conditions if ( m_aConditions.isEmpty() && m_aMyConditions.size() == 1 && sRealCond == ">=0" )
bDefaultCond = true;
if ( m_nType == SvXMLStylesTokens::TEXT_STYLE && static_cast<size_t>(nIndex) == m_aMyConditions.size() - 1 )
{ // The last condition in a number format with a text part can only // be "all other numbers", the condition string must be empty.
bDefaultCond = true;
}
if (!bDefaultCond)
{ // Convert != to <>
sal_Int32 nPos = sRealCond.indexOf( "!=" ); if ( nPos >= 0 )
{
sRealCond = sRealCond.replaceAt( nPos, 2, u"<>" );
}
if (m_eImplicitCalendar != ImplicitCalendar::DEFAULT && m_eImplicitCalendar != ImplicitCalendar::SECONDARY)
{ // A switch from empty default calendar to named default calendar or // vice versa is not a switch. bool bSameDefault = false; if (m_sCalendar.isEmpty() || rNewCalendar.isEmpty())
{ // As both are not equal, only one can be empty here, the other // can not. const OUString& rDefaultCalendar = GetLocaleData().getDefaultCalendar()->Name; // So if one is the named default calendar the other is the // empty default calendar.
bSameDefault = (rNewCalendar == rDefaultCalendar || m_sCalendar == rDefaultCalendar);
} if (!bSameDefault)
{
m_aFormatCode.append( "[~" ); // intro for calendar code if (rNewCalendar.isEmpty())
{ // Empty calendar name here means switching to default calendar // from a different calendar. Needs to be explicitly stated in // format code.
m_aFormatCode.append( GetLocaleData().getDefaultCalendar()->Name );
} else
{
m_aFormatCode.append( rNewCalendar );
}
m_aFormatCode.append( ']' ); // end of calendar code
}
}
m_sCalendar = rNewCalendar;
}
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.