diff options
Diffstat (limited to 'xmloff/source/style/xmlnumfe.cxx')
-rw-r--r-- | xmloff/source/style/xmlnumfe.cxx | 1820 |
1 files changed, 1820 insertions, 0 deletions
diff --git a/xmloff/source/style/xmlnumfe.cxx b/xmloff/source/style/xmlnumfe.cxx new file mode 100644 index 000000000000..93355e914759 --- /dev/null +++ b/xmloff/source/style/xmlnumfe.cxx @@ -0,0 +1,1820 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_xmloff.hxx" + +#define _SVSTDARR_ULONGS +#define _ZFORLIST_DECLARE_TABLE + +#include <svl/svstdarr.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <svl/numuno.hxx> +#include <i18npool/mslangid.hxx> +#include <tools/debug.hxx> +#include <rtl/math.hxx> +#include <unotools/calendarwrapper.hxx> +#include <unotools/charclass.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <rtl/ustrbuf.hxx> + +// #110680# +//#include <comphelper/processfactory.hxx> + +#include <com/sun/star/i18n/NativeNumberXmlAttributes.hpp> + +#include <xmloff/xmlnumfe.hxx> +#include "xmlnmspe.hxx" +#include <xmloff/xmluconv.hxx> +#include <xmloff/attrlist.hxx> +#include <xmloff/nmspmap.hxx> +#include <xmloff/families.hxx> +#include <xmloff/xmlnumfi.hxx> // SvXMLNumFmtDefaults + +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> +#include <svl/nfsymbol.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlexp.hxx> + +#include <set> + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +using namespace ::com::sun::star; +using namespace ::xmloff::token; +using namespace ::svt; + +//------------------------------------------------------------------------- + +// 4th condition for text formats doesn't work +//#define XMLNUM_MAX_PARTS 4 +#define XMLNUM_MAX_PARTS 3 + +//------------------------------------------------------------------------- + +struct LessuInt32 +{ + sal_Bool operator() (const sal_uInt32 rValue1, const sal_uInt32 rValue2) const + { + return rValue1 < rValue2; + } +}; + +typedef std::set< sal_uInt32, LessuInt32 > SvXMLuInt32Set; + +class SvXMLNumUsedList_Impl +{ + SvXMLuInt32Set aUsed; + SvXMLuInt32Set aWasUsed; + SvXMLuInt32Set::iterator aCurrentUsedPos; + sal_uInt32 nUsedCount; + sal_uInt32 nWasUsedCount; + +public: + SvXMLNumUsedList_Impl(); + ~SvXMLNumUsedList_Impl(); + + void SetUsed( sal_uInt32 nKey ); + sal_Bool IsUsed( sal_uInt32 nKey ) const; + sal_Bool IsWasUsed( sal_uInt32 nKey ) const; + void Export(); + + sal_Bool GetFirstUsed(sal_uInt32& nKey); + sal_Bool GetNextUsed(sal_uInt32& nKey); + + void GetWasUsed(uno::Sequence<sal_Int32>& rWasUsed); + void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed); +}; + +//------------------------------------------------------------------------- + +struct SvXMLEmbeddedTextEntry +{ + sal_uInt16 nSourcePos; // position in NumberFormat (to skip later) + sal_Int32 nFormatPos; // resulting position in embedded-text element + rtl::OUString aText; + + SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, const rtl::OUString& rT ) : + nSourcePos(nSP), nFormatPos(nFP), aText(rT) {} +}; + +typedef SvXMLEmbeddedTextEntry* SvXMLEmbeddedTextEntryPtr; +SV_DECL_PTRARR_DEL( SvXMLEmbeddedTextEntryArr, SvXMLEmbeddedTextEntryPtr, 4, 4 ) + +//------------------------------------------------------------------------- + +SV_IMPL_PTRARR( SvXMLEmbeddedTextEntryArr, SvXMLEmbeddedTextEntryPtr ); + +//------------------------------------------------------------------------- + +// +//! SvXMLNumUsedList_Impl should be optimized! +// + +SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() : + nUsedCount(0), + nWasUsedCount(0) +{ +} + +SvXMLNumUsedList_Impl::~SvXMLNumUsedList_Impl() +{ +} + +void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey ) +{ + if ( !IsWasUsed(nKey) ) + { + std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey ); + if (aPair.second) + nUsedCount++; + } +} + +sal_Bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const +{ + SvXMLuInt32Set::iterator aItr = aUsed.find(nKey); + return (aItr != aUsed.end()); +} + +sal_Bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const +{ + SvXMLuInt32Set::iterator aItr = aWasUsed.find(nKey); + return (aItr != aWasUsed.end()); +} + +void SvXMLNumUsedList_Impl::Export() +{ + SvXMLuInt32Set::iterator aItr = aUsed.begin(); + while (aItr != aUsed.end()) + { + std::pair<SvXMLuInt32Set::iterator, bool> aPair = aWasUsed.insert( *aItr ); + if (aPair.second) + nWasUsedCount++; + aItr++; + } + aUsed.clear(); + nUsedCount = 0; +} + +sal_Bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey) +{ + sal_Bool bRet(sal_False); + aCurrentUsedPos = aUsed.begin(); + if(nUsedCount) + { + DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong"); + nKey = *aCurrentUsedPos; + bRet = sal_True; + } + return bRet; +} + +sal_Bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey) +{ + sal_Bool bRet(sal_False); + if (aCurrentUsedPos != aUsed.end()) + { + aCurrentUsedPos++; + if (aCurrentUsedPos != aUsed.end()) + { + nKey = *aCurrentUsedPos; + bRet = sal_True; + } + } + return bRet; +} + +void SvXMLNumUsedList_Impl::GetWasUsed(uno::Sequence<sal_Int32>& rWasUsed) +{ + rWasUsed.realloc(nWasUsedCount); + sal_Int32* pWasUsed = rWasUsed.getArray(); + if (pWasUsed) + { + SvXMLuInt32Set::iterator aItr = aWasUsed.begin(); + while (aItr != aWasUsed.end()) + { + *pWasUsed = *aItr; + aItr++; + pWasUsed++; + } + } +} + +void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed) +{ + DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty"); + sal_Int32 nCount(rWasUsed.getLength()); + const sal_Int32* pWasUsed = rWasUsed.getConstArray(); + for (sal_uInt16 i = 0; i < nCount; i++, pWasUsed++) + { + std::pair<SvXMLuInt32Set::iterator, bool> aPair = aWasUsed.insert( *pWasUsed ); + if (aPair.second) + nWasUsedCount++; + } +} + +//------------------------------------------------------------------------- + +SvXMLNumFmtExport::SvXMLNumFmtExport( + SvXMLExport& rExp, + const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) : + rExport( rExp ), + sPrefix( OUString::createFromAscii( "N" ) ), + pFormatter( NULL ), + pCharClass( NULL ), + pLocaleData( NULL ) +{ + // supplier must be SvNumberFormatsSupplierObj + SvNumberFormatsSupplierObj* pObj = + SvNumberFormatsSupplierObj::getImplementation( rSupp ); + if (pObj) + pFormatter = pObj->GetNumberFormatter(); + + if ( pFormatter ) + { + pCharClass = new CharClass( pFormatter->GetServiceManager(), + pFormatter->GetLocale() ); + pLocaleData = new LocaleDataWrapper( pFormatter->GetServiceManager(), + pFormatter->GetLocale() ); + } + else + { + lang::Locale aLocale( MsLangId::convertLanguageToLocale( MsLangId::getSystemLanguage() ) ); + + // #110680# + // pCharClass = new CharClass( ::comphelper::getProcessServiceFactory(), aLocale ); + // pLocaleData = new LocaleDataWrapper( ::comphelper::getProcessServiceFactory(), aLocale ); + pCharClass = new CharClass( rExport.getServiceFactory(), aLocale ); + pLocaleData = new LocaleDataWrapper( rExport.getServiceFactory(), aLocale ); + } + + pUsedList = new SvXMLNumUsedList_Impl; +} + +SvXMLNumFmtExport::SvXMLNumFmtExport( + SvXMLExport& rExp, + const ::com::sun::star::uno::Reference< + ::com::sun::star::util::XNumberFormatsSupplier >& rSupp, + const rtl::OUString& rPrefix ) : + rExport( rExp ), + sPrefix( rPrefix ), + pFormatter( NULL ), + pCharClass( NULL ), + pLocaleData( NULL ) +{ + // supplier must be SvNumberFormatsSupplierObj + SvNumberFormatsSupplierObj* pObj = + SvNumberFormatsSupplierObj::getImplementation( rSupp ); + if (pObj) + pFormatter = pObj->GetNumberFormatter(); + + if ( pFormatter ) + { + pCharClass = new CharClass( pFormatter->GetServiceManager(), + pFormatter->GetLocale() ); + pLocaleData = new LocaleDataWrapper( pFormatter->GetServiceManager(), + pFormatter->GetLocale() ); + } + else + { + lang::Locale aLocale( MsLangId::convertLanguageToLocale( MsLangId::getSystemLanguage() ) ); + + // #110680# + // pCharClass = new CharClass( ::comphelper::getProcessServiceFactory(), aLocale ); + // pLocaleData = new LocaleDataWrapper( ::comphelper::getProcessServiceFactory(), aLocale ); + pCharClass = new CharClass( rExport.getServiceFactory(), aLocale ); + pLocaleData = new LocaleDataWrapper( rExport.getServiceFactory(), aLocale ); + } + + pUsedList = new SvXMLNumUsedList_Impl; +} + +SvXMLNumFmtExport::~SvXMLNumFmtExport() +{ + delete pUsedList; + delete pLocaleData; + delete pCharClass; +} + +//------------------------------------------------------------------------- + +// +// helper methods +// + +OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, sal_Bool bDefPart, const rtl::OUString& rPrefix ) +{ + OUStringBuffer aFmtName( 10L ); + aFmtName.append( rPrefix ); + aFmtName.append( nKey ); + if (!bDefPart) + { + aFmtName.append( (sal_Unicode)'P' ); + aFmtName.append( nPart ); + } + return aFmtName.makeStringAndClear(); +} + +void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar ) +{ + if ( rCalendar.getLength() ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar ); + } +} + +void SvXMLNumFmtExport::AddTextualAttr_Impl( sal_Bool bText ) +{ + if ( bText ) // non-textual + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE ); + } +} + +void SvXMLNumFmtExport::AddStyleAttr_Impl( sal_Bool bLong ) +{ + if ( bLong ) // short is default + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG ); + } +} + +void SvXMLNumFmtExport::AddLanguageAttr_Impl( sal_Int32 nLang ) +{ + if ( nLang != LANGUAGE_SYSTEM ) + { + OUString aLangStr, aCountryStr; + MsLangId::convertLanguageToIsoNames( (LanguageType)nLang, aLangStr, aCountryStr ); + + if (aLangStr.getLength()) + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_LANGUAGE, aLangStr ); + if (aCountryStr.getLength()) + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_COUNTRY, aCountryStr ); + } +} + +//------------------------------------------------------------------------- + +// +// methods to write individual elements within a format +// + +void SvXMLNumFmtExport::AddToTextElement_Impl( const OUString& rString ) +{ + // append to sTextContent, write element in FinishTextElement_Impl + // to avoid several text elements following each other + + sTextContent.append( rString ); +} + +void SvXMLNumFmtExport::FinishTextElement_Impl() +{ + if ( sTextContent.getLength() ) + { + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT, + sal_True, sal_False ); + rExport.Characters( sTextContent.makeStringAndClear() ); + } +} + +void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor ) +{ + FinishTextElement_Impl(); + + OUStringBuffer aColStr( 7 ); + SvXMLUnitConverter::convertColor( aColStr, rColor ); + rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR, + aColStr.makeStringAndClear() ); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString, + const OUString& rExt ) +{ + FinishTextElement_Impl(); + + if ( rExt.getLength() ) + { + sal_Int32 nLang = rExt.toInt32(16); // hex + if ( nLang < 0 ) // extension string may contain "-" separator + nLang = -nLang; + AddLanguageAttr_Impl( nLang ); // adds to pAttrList + } + + SvXMLElementExport aElem( rExport, + XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL, + sal_True, sal_False ); + rExport.Characters( rString ); +} + +void SvXMLNumFmtExport::WriteBooleanElement_Impl() +{ + FinishTextElement_Impl(); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteTextContentElement_Impl() +{ + FinishTextElement_Impl(); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT, + sal_True, sal_False ); +} + +// date elements + +void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, sal_Bool bLong, sal_Bool bText ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + AddStyleAttr_Impl( bLong ); // adds to pAttrList + AddTextualAttr_Impl( bText ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MONTH, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_YEAR, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString& rCalendar, sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_ERA, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString& rCalendar, sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY_OF_WEEK, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_QUARTER, + sal_True, sal_False ); +} + +// time elements + +void SvXMLNumFmtExport::WriteHoursElement_Impl( sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_HOURS, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteMinutesElement_Impl( sal_Bool bLong ) +{ + FinishTextElement_Impl(); + + AddStyleAttr_Impl( bLong ); // adds to pAttrList + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MINUTES, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteSecondsElement_Impl( sal_Bool bLong, sal_uInt16 nDecimals ) +{ + FinishTextElement_Impl(); + + AddStyleAttr_Impl( bLong ); // adds to pAttrList + if ( nDecimals > 0 ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES, + OUString::valueOf( (sal_Int32) nDecimals ) ); + } + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_SECONDS, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteAMPMElement_Impl() +{ + FinishTextElement_Impl(); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_AM_PM, + sal_True, sal_False ); +} + +// numbers + +void SvXMLNumFmtExport::WriteNumberElement_Impl( + sal_Int32 nDecimals, sal_Int32 nInteger, + const OUString& rDashStr, sal_Bool bVarDecimals, + sal_Bool bGrouping, sal_Int32 nTrailingThousands, + const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries ) +{ + FinishTextElement_Impl(); + + // decimals + if ( nDecimals >= 0 ) // negative = automatic + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES, + OUString::valueOf( nDecimals ) ); + } + + // integer digits + if ( nInteger >= 0 ) // negative = automatic + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS, + OUString::valueOf( nInteger ) ); + } + + // decimal replacement (dashes) or variable decimals (#) + if ( rDashStr.getLength() || bVarDecimals ) + { + // variable decimals means an empty replacement string + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT, + rDashStr ); + } + + // (automatic) grouping separator + if ( bGrouping ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE ); + } + + // display-factor if there are trailing thousands separators + if ( nTrailingThousands ) + { + // each separator character removes three digits + double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands ); + + OUStringBuffer aFactStr; + SvXMLUnitConverter::convertDouble( aFactStr, fFactor ); + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() ); + } + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_NUMBER, + sal_True, sal_True ); + + // number:embedded-text as child elements + + sal_uInt16 nEntryCount = rEmbeddedEntries.Count(); + for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++) + { + SvXMLEmbeddedTextEntry* pObj = rEmbeddedEntries[nEntry]; + + // position attribute + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION, + OUString::valueOf( pObj->nFormatPos ) ); + SvXMLElementExport aChildElem( rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT, + sal_True, sal_False ); + + // text as element content + rtl::OUString aContent( pObj->aText ); + while ( nEntry+1 < nEntryCount && rEmbeddedEntries[nEntry+1]->nFormatPos == pObj->nFormatPos ) + { + // The array can contain several elements for the same position in the number + // (for example, literal text and space from underscores). They must be merged + // into a single embedded-text element. + aContent += rEmbeddedEntries[nEntry+1]->aText; + ++nEntry; + } + rExport.Characters( aContent ); + } +} + +void SvXMLNumFmtExport::WriteScientificElement_Impl( + sal_Int32 nDecimals, sal_Int32 nInteger, + sal_Bool bGrouping, sal_Int32 nExp ) +{ + FinishTextElement_Impl(); + + // decimals + if ( nDecimals >= 0 ) // negative = automatic + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES, + OUString::valueOf( nDecimals ) ); + } + + // integer digits + if ( nInteger >= 0 ) // negative = automatic + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS, + OUString::valueOf( nInteger ) ); + } + + // (automatic) grouping separator + if ( bGrouping ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE ); + } + + // exponent digits + if ( nExp >= 0 ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS, + OUString::valueOf( nExp ) ); + } + + SvXMLElementExport aElem( rExport, + XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER, + sal_True, sal_False ); +} + +void SvXMLNumFmtExport::WriteFractionElement_Impl( + sal_Int32 nInteger, sal_Bool bGrouping, + sal_Int32 nNumerator, sal_Int32 nDenominator ) +{ + FinishTextElement_Impl(); + + // integer digits + if ( nInteger >= 0 ) // negative = default (no integer part) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS, + OUString::valueOf( nInteger ) ); + } + + // (automatic) grouping separator + if ( bGrouping ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE ); + } + + // numerator digits + if ( nNumerator >= 0 ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS, + OUString::valueOf( nNumerator ) ); + } + + // denominator digits + if ( nDenominator >= 0 ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS, + OUString::valueOf( nDenominator ) ); + } + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_FRACTION, + sal_True, sal_False ); +} + +// mapping (condition) + +void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit, + sal_Int32 nKey, sal_Int32 nPart ) +{ + FinishTextElement_Impl(); + + if ( nOp != NUMBERFORMAT_OP_NO ) + { + // style namespace + + OUStringBuffer aCondStr( 20L ); + aCondStr.appendAscii( "value()" ); //! define constant + switch ( nOp ) + { + case NUMBERFORMAT_OP_EQ: aCondStr.append( (sal_Unicode) '=' ); break; + case NUMBERFORMAT_OP_NE: aCondStr.appendAscii( "<>" ); break; + case NUMBERFORMAT_OP_LT: aCondStr.append( (sal_Unicode) '<' ); break; + case NUMBERFORMAT_OP_LE: aCondStr.appendAscii( "<=" ); break; + case NUMBERFORMAT_OP_GT: aCondStr.append( (sal_Unicode) '>' ); break; + case NUMBERFORMAT_OP_GE: aCondStr.appendAscii( ">=" ); break; + default: + DBG_ERROR("unknown operator"); + } + ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit, + rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, + '.', true ); + + rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION, + aCondStr.makeStringAndClear() ); + + rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME, + rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, sal_False, + sPrefix ) ) ); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_MAP, + sal_True, sal_False ); + } +} + +//------------------------------------------------------------------------- +// for old (automatic) currency formats: parse currency symbol from text + +xub_StrLen lcl_FindSymbol( const String& sUpperStr, const String& sCurString ) +{ + // search for currency symbol + // Quoting as in ImpSvNumberformatScan::Symbol_Division + + xub_StrLen nCPos = 0; + while (nCPos != STRING_NOTFOUND) + { + nCPos = sUpperStr.Search( sCurString, nCPos ); + if (nCPos != STRING_NOTFOUND) + { + // in Quotes? + xub_StrLen nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos ); + if ( nQ == STRING_NOTFOUND ) + { + // dm can be escaped as "dm or \d + sal_Unicode c; + if ( nCPos == 0 || + ((c = sUpperStr.GetChar(xub_StrLen(nCPos-1))) != '"' + && c != '\\') ) + { + return nCPos; // found + } + else + nCPos++; // continue + } + else + nCPos = nQ + 1; // continue after quote end + } + } + return STRING_NOTFOUND; // not found +} + +sal_Bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString, + const ::com::sun::star::lang::Locale& rLocale ) +{ + // returns TRUE if currency element was written + + sal_Bool bRet = sal_False; + +// pLocaleData->setLocale( rLocale ); +// String sCurString = pLocaleData->getCurrSymbol(); + + LanguageType nLang = MsLangId::convertLocaleToLanguage( rLocale ); + pFormatter->ChangeIntl( nLang ); + String sCurString, sDummy; + pFormatter->GetCompatibilityCurrency( sCurString, sDummy ); + + pCharClass->setLocale( rLocale ); + String sUpperStr = pCharClass->upper(rString); + xub_StrLen nPos = lcl_FindSymbol( sUpperStr, sCurString ); + if ( nPos != STRING_NOTFOUND ) + { + sal_Int32 nLength = rString.getLength(); + sal_Int32 nCurLen = sCurString.Len(); + sal_Int32 nCont = nPos + nCurLen; + + // text before currency symbol + if ( nPos > 0 ) + AddToTextElement_Impl( rString.copy( 0, nPos ) ); + + // currency symbol (empty string -> default) + OUString sEmpty; + WriteCurrencyElement_Impl( sEmpty, sEmpty ); + bRet = sal_True; + + // text after currency symbol + if ( nCont < nLength ) + AddToTextElement_Impl( rString.copy( nCont, nLength-nCont ) ); + } + else + AddToTextElement_Impl( rString ); // simple text + + return bRet; // TRUE: currency element written +} + +//------------------------------------------------------------------------- + +OUString lcl_GetDefaultCalendar( SvNumberFormatter* pFormatter, LanguageType nLang ) +{ + // get name of first non-gregorian calendar for the language + + OUString aCalendar; + CalendarWrapper* pCalendar = pFormatter->GetCalendar(); + if (pCalendar) + { + lang::Locale aLocale( MsLangId::convertLanguageToLocale( nLang ) ); + + uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale ); + sal_Int32 nCnt = aCals.getLength(); + sal_Bool bFound = sal_False; + for ( sal_Int32 j=0; j < nCnt && !bFound; j++ ) + { + if ( !aCals[j].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("gregorian") ) ) + { + aCalendar = aCals[j]; + bFound = sal_True; + } + } + } + return aCalendar; +} + +//------------------------------------------------------------------------- + +sal_Bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos ) +{ + sal_uInt16 nCount = rEmbeddedEntries.Count(); + for (sal_uInt16 i=0; i<nCount; i++) + if ( rEmbeddedEntries[i]->nSourcePos == nPos ) + return sal_True; + + return sal_False; // not found +} + +BOOL lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, sal_Bool bSystemDate, NfIndexTableOffset eBuiltIn ) +{ + // make an extra loop to collect date elements, to check if it is a default format + // before adding the automatic-order attribute + + SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE; + SvXMLDateElementAttributes eDateDay = XML_DEA_NONE; + SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE; + SvXMLDateElementAttributes eDateYear = XML_DEA_NONE; + SvXMLDateElementAttributes eDateHours = XML_DEA_NONE; + SvXMLDateElementAttributes eDateMins = XML_DEA_NONE; + SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE; + sal_Bool bDateNoDefault = sal_False; + + sal_uInt16 nPos = 0; + sal_Bool bEnd = sal_False; + short nLastType = 0; + while (!bEnd) + { + short nElemType = rFormat.GetNumForType( 0, nPos, sal_False ); + switch ( nElemType ) + { + case 0: + if ( nLastType == NF_SYMBOLTYPE_STRING ) + bDateNoDefault = sal_True; // text at the end -> no default date format + bEnd = sal_True; // end of format reached + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_DATESEP: + case NF_SYMBOLTYPE_TIMESEP: + case NF_SYMBOLTYPE_TIME100SECSEP: + // text is ignored, except at the end + break; + // same mapping as in SvXMLNumFormatContext::AddNfKeyword: + case NF_KEY_NN: eDateDOW = XML_DEA_SHORT; break; + case NF_KEY_NNN: + case NF_KEY_NNNN: eDateDOW = XML_DEA_LONG; break; + case NF_KEY_D: eDateDay = XML_DEA_SHORT; break; + case NF_KEY_DD: eDateDay = XML_DEA_LONG; break; + case NF_KEY_M: eDateMonth = XML_DEA_SHORT; break; + case NF_KEY_MM: eDateMonth = XML_DEA_LONG; break; + case NF_KEY_MMM: eDateMonth = XML_DEA_TEXTSHORT; break; + case NF_KEY_MMMM: eDateMonth = XML_DEA_TEXTLONG; break; + case NF_KEY_YY: eDateYear = XML_DEA_SHORT; break; + case NF_KEY_YYYY: eDateYear = XML_DEA_LONG; break; + case NF_KEY_H: eDateHours = XML_DEA_SHORT; break; + case NF_KEY_HH: eDateHours = XML_DEA_LONG; break; + case NF_KEY_MI: eDateMins = XML_DEA_SHORT; break; + case NF_KEY_MMI: eDateMins = XML_DEA_LONG; break; + case NF_KEY_S: eDateSecs = XML_DEA_SHORT; break; + case NF_KEY_SS: 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: + bDateNoDefault = sal_True; // any other element -> no default format + } + nLastType = nElemType; + ++nPos; + } + + if ( bDateNoDefault ) + return FALSE; // additional elements + else + { + NfIndexTableOffset eFound = (NfIndexTableOffset) SvXMLNumFmtDefaults::GetDefaultDateFormat( + eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate ); + + return ( eFound == eBuiltIn ); + } +} + +// +// export one part (condition) +// + +void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, + sal_uInt16 nPart, sal_Bool bDefPart ) +{ + //! for the default part, pass the coditions from the other parts! + + // + // element name + // + + NfIndexTableOffset eBuiltIn = pFormatter->GetIndexTableOffset( nKey ); + + short nFmtType = 0; + sal_Bool bThousand = sal_False; + sal_uInt16 nPrecision = 0; + sal_uInt16 nLeading = 0; + rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading); + nFmtType &= ~NUMBERFORMAT_DEFINED; + + // special treatment of builtin formats that aren't detected by normal parsing + // (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats) + if ( eBuiltIn == NF_NUMBER_STANDARD ) + nFmtType = NUMBERFORMAT_NUMBER; + else if ( eBuiltIn == NF_BOOLEAN ) + nFmtType = NUMBERFORMAT_LOGICAL; + else if ( eBuiltIn == NF_TEXT ) + nFmtType = NUMBERFORMAT_TEXT; + + // #101606# An empty subformat is a valid number-style resulting in an + // empty display string for the condition of the subformat. + if ( nFmtType == NUMBERFORMAT_UNDEFINED && rFormat.GetNumForType( nPart, + 0, sal_False ) == 0 ) + nFmtType = 0; + + XMLTokenEnum eType = XML_TOKEN_INVALID; + switch ( nFmtType ) + { + // type is 0 if a format contains no recognized elements + // (like text only) - this is handled as a number-style. + case 0: + case NUMBERFORMAT_NUMBER: + case NUMBERFORMAT_SCIENTIFIC: + case NUMBERFORMAT_FRACTION: + eType = XML_NUMBER_STYLE; + break; + case NUMBERFORMAT_PERCENT: + eType = XML_PERCENTAGE_STYLE; + break; + case NUMBERFORMAT_CURRENCY: + eType = XML_CURRENCY_STYLE; + break; + case NUMBERFORMAT_DATE: + case NUMBERFORMAT_DATETIME: + eType = XML_DATE_STYLE; + break; + case NUMBERFORMAT_TIME: + eType = XML_TIME_STYLE; + break; + case NUMBERFORMAT_TEXT: + eType = XML_TEXT_STYLE; + break; + case NUMBERFORMAT_LOGICAL: + eType = XML_BOOLEAN_STYLE; + break; + } + DBG_ASSERT( eType != XML_TOKEN_INVALID, "unknown format type" ); + + OUString sAttrValue; + sal_Bool bUserDef = ( ( rFormat.GetType() & NUMBERFORMAT_DEFINED ) != 0 ); + + // + // common attributes for format + // + + // format name (generated from key) - style namespace + rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, + lcl_CreateStyleName( nKey, nPart, bDefPart, sPrefix ) ); + + // "volatile" attribute for styles used only in maps + if ( !bDefPart ) + rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE ); + + // language / country + LanguageType nLang = rFormat.GetLanguage(); + AddLanguageAttr_Impl( nLang ); // adds to pAttrList + + // title (comment) + // titles for builtin formats are not written + sAttrValue = rFormat.GetComment(); + if ( sAttrValue.getLength() && bUserDef && bDefPart ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue ); + } + + // automatic ordering for currency and date formats + // only used for some built-in formats + BOOL bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT || eBuiltIn == NF_CURRENCY_1000DEC2 || + eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED || + eBuiltIn == NF_CURRENCY_1000DEC2_DASHED || + eBuiltIn == NF_DATE_SYSTEM_SHORT || eBuiltIn == NF_DATE_SYSTEM_LONG || + eBuiltIn == NF_DATE_SYS_MMYY || eBuiltIn == NF_DATE_SYS_DDMMM || + eBuiltIn == NF_DATE_SYS_DDMMYYYY || eBuiltIn == NF_DATE_SYS_DDMMYY || + eBuiltIn == NF_DATE_SYS_DMMMYY || eBuiltIn == NF_DATE_SYS_DMMMYYYY || + eBuiltIn == NF_DATE_SYS_DMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNDMMMYY || + eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY || + eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS ); + + // format source (for date and time formats) + // only used for some built-in formats + BOOL bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT || + eBuiltIn == NF_DATE_SYSTEM_LONG || + eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM ); + BOOL bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG ); + + // check if the format definition matches the key + if ( bAutoOrder && ( nFmtType == NUMBERFORMAT_DATE || nFmtType == NUMBERFORMAT_DATETIME ) && + !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) ) + { + bAutoOrder = bSystemDate = bLongSysDate = FALSE; // don't write automatic-order attribute then + } + + if ( bAutoOrder && + ( nFmtType == NUMBERFORMAT_CURRENCY || nFmtType == NUMBERFORMAT_DATE || nFmtType == NUMBERFORMAT_DATETIME ) ) + { + // #85109# format type must be checked to avoid dtd errors if + // locale data contains other format types at the built-in positions + + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER, + XML_TRUE ); + } + + if ( bSystemDate && bAutoOrder && + ( nFmtType == NUMBERFORMAT_DATE || nFmtType == NUMBERFORMAT_DATETIME ) ) + { + // #85109# format type must be checked to avoid dtd errors if + // locale data contains other format types at the built-in positions + + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE, + XML_LANGUAGE ); + } + + // overflow for time formats as in [hh]:mm + // controlled by bThousand from number format info + // default for truncate-on-overflow is true + if ( nFmtType == NUMBERFORMAT_TIME && bThousand ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW, + XML_FALSE ); + } + + // + // Native number transliteration + // + ::com::sun::star::i18n::NativeNumberXmlAttributes aAttr; + rFormat.GetNatNumXml( aAttr, nPart ); + if ( aAttr.Format.getLength() ) + { + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT, + aAttr.Format ); + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE, + aAttr.Locale.Language ); + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY, + aAttr.Locale.Country ); + rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE, + aAttr.Style ); + } + + // + // The element + // + SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, eType, + sal_True, sal_True ); + + // + // color (properties element) + // + + const Color* pCol = rFormat.GetColor( nPart ); + if (pCol) + WriteColorElement_Impl(*pCol); + + + // detect if there is "real" content, excluding color and maps + //! move to implementation of Write... methods? + sal_Bool bAnyContent = sal_False; + + // + // format elements + // + + SvXMLEmbeddedTextEntryArr aEmbeddedEntries(0); + if ( eBuiltIn == NF_NUMBER_STANDARD ) + { + // default number format contains just one number element + WriteNumberElement_Impl( -1, 1, OUString(), sal_False, sal_False, 0, aEmbeddedEntries ); + bAnyContent = sal_True; + } + else if ( eBuiltIn == NF_BOOLEAN ) + { + // boolean format contains just one boolean element + WriteBooleanElement_Impl(); + bAnyContent = sal_True; + } + else + { + // first loop to collect attributes + + sal_Bool bDecDashes = sal_False; + sal_Bool bVarDecimals = sal_False; + sal_Bool bExpFound = sal_False; + sal_Bool bCurrFound = sal_False; + sal_Bool bInInteger = sal_True; + sal_Int32 nExpDigits = 0; + sal_Int32 nIntegerSymbols = 0; // for embedded-text, including "#" + sal_Int32 nTrailingThousands = 0; // thousands-separators after all digits + OUString sCurrExt; + OUString aCalendar; + sal_uInt16 nPos = 0; + sal_Bool bEnd = sal_False; + while (!bEnd) + { + short nElemType = rFormat.GetNumForType( nPart, nPos, sal_False ); + const XubString* pElemStr = rFormat.GetNumForString( nPart, nPos, sal_False ); + + switch ( nElemType ) + { + case 0: + bEnd = sal_True; // end of format reached + break; + case NF_SYMBOLTYPE_DIGIT: + if ( bExpFound && pElemStr ) + nExpDigits += pElemStr->Len(); + else if ( !bDecDashes && pElemStr && pElemStr->GetChar(0) == '-' ) + bDecDashes = TRUE; + else if ( !bVarDecimals && !bInInteger && pElemStr && pElemStr->GetChar(0) == '#' ) + { + // If the decimal digits string starts with a '#', variable + // decimals is assumed (for 0.###, but not 0.0##). + bVarDecimals = sal_True; + } + if ( bInInteger && pElemStr ) + nIntegerSymbols += pElemStr->Len(); + nTrailingThousands = 0; + break; + case NF_SYMBOLTYPE_DECSEP: + bInInteger = sal_False; + break; + case NF_SYMBOLTYPE_THSEP: + if (pElemStr) + nTrailingThousands += pElemStr->Len(); // is reset to 0 if digits follow + break; + case NF_SYMBOLTYPE_EXP: + bExpFound = sal_True; // following digits are exponent digits + bInInteger = sal_False; + break; + case NF_SYMBOLTYPE_CURRENCY: + bCurrFound = TRUE; + break; + case NF_SYMBOLTYPE_CURREXT: + if (pElemStr) + sCurrExt = *pElemStr; + break; + + // E, EE, R, RR: select non-gregorian calendar + // AAA, AAAA: calendar is switched at the position of the element + case NF_KEY_EC: + case NF_KEY_EEC: + case NF_KEY_R: + case NF_KEY_RR: + if (!aCalendar.getLength()) + aCalendar = lcl_GetDefaultCalendar( pFormatter, nLang ); + break; + } + ++nPos; + } + + // collect strings for embedded-text (must be known before number element is written) + + sal_Bool bAllowEmbedded = ( nFmtType == 0 || nFmtType == NUMBERFORMAT_NUMBER || + nFmtType == NUMBERFORMAT_CURRENCY || + nFmtType == NUMBERFORMAT_PERCENT ); + if ( bAllowEmbedded ) + { + sal_Int32 nDigitsPassed = 0; + nPos = 0; + bEnd = sal_False; + while (!bEnd) + { + short nElemType = rFormat.GetNumForType( nPart, nPos, sal_False ); + const XubString* pElemStr = rFormat.GetNumForString( nPart, nPos, sal_False ); + + switch ( nElemType ) + { + case 0: + bEnd = sal_True; // end of format reached + break; + case NF_SYMBOLTYPE_DIGIT: + if ( pElemStr ) + nDigitsPassed += pElemStr->Len(); + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_BLANK: + case NF_SYMBOLTYPE_PERCENT: + if ( nDigitsPassed > 0 && nDigitsPassed < nIntegerSymbols && pElemStr ) + { + // text (literal or underscore) within the integer part of a number:number element + + String aEmbeddedStr; + if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT ) + aEmbeddedStr = *pElemStr; + else + SvNumberformat::InsertBlanks( aEmbeddedStr, 0, pElemStr->GetChar(1) ); + + sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed; + + SvXMLEmbeddedTextEntry* pObj = new SvXMLEmbeddedTextEntry( nPos, nEmbedPos, aEmbeddedStr ); + aEmbeddedEntries.Insert( pObj, aEmbeddedEntries.Count() ); + } + break; + } + ++nPos; + } + } + + // final loop to write elements + + sal_Bool bNumWritten = sal_False; + sal_Bool bCurrencyWritten = sal_False; + short nPrevType = 0; + nPos = 0; + bEnd = sal_False; + while (!bEnd) + { + short nElemType = rFormat.GetNumForType( nPart, nPos, sal_False ); + const XubString* pElemStr = rFormat.GetNumForString( nPart, nPos, sal_False ); + + switch ( nElemType ) + { + case 0: + bEnd = sal_True; // end of format reached + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_DATESEP: + case NF_SYMBOLTYPE_TIMESEP: + case NF_SYMBOLTYPE_TIME100SECSEP: + case NF_SYMBOLTYPE_PERCENT: + if (pElemStr) + { + if ( ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ) && + ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) && + nPrecision > 0 ) + { + // decimal separator after seconds is implied by + // "decimal-places" attribute and must not be written + // as text element + //! difference between '.' and ',' is lost here + } + else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) ) + { + // text is written as embedded-text child of the number, + // don't create a text element + } + else if ( nFmtType == NUMBERFORMAT_CURRENCY && !bCurrFound && !bCurrencyWritten ) + { + // automatic currency symbol is implemented as part of + // normal text -> search for the symbol + bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr, + MsLangId::convertLanguageToLocale( nLang ) ); + bAnyContent = sal_True; + } + else + AddToTextElement_Impl( *pElemStr ); + } + break; + case NF_SYMBOLTYPE_BLANK: + if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) ) + { + // turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat + // (#i20396# the spaces may also be in embedded-text elements) + + String aBlanks; + SvNumberformat::InsertBlanks( aBlanks, 0, pElemStr->GetChar(1) ); + AddToTextElement_Impl( aBlanks ); + } + break; + case NF_KEY_GENERAL : + WriteNumberElement_Impl( -1, 1, OUString(), sal_False, sal_False, 0, aEmbeddedEntries ); + break; + case NF_KEY_CCC: + if (pElemStr) + { + if ( bCurrencyWritten ) + AddToTextElement_Impl( *pElemStr ); // never more than one currency element + else + { + //! must be different from short automatic format + //! but should still be empty (meaning automatic) + // pElemStr is "CCC" + + WriteCurrencyElement_Impl( *pElemStr, OUString() ); + bAnyContent = sal_True; + bCurrencyWritten = sal_True; + } + } + break; + case NF_SYMBOLTYPE_CURRENCY: + if (pElemStr) + { + if ( bCurrencyWritten ) + AddToTextElement_Impl( *pElemStr ); // never more than one currency element + else + { + WriteCurrencyElement_Impl( *pElemStr, sCurrExt ); + bAnyContent = sal_True; + bCurrencyWritten = sal_True; + } + } + break; + case NF_SYMBOLTYPE_DIGIT: + if (!bNumWritten) // write number part + { + switch ( nFmtType ) + { + // for type 0 (not recognized as a special type), + // write a "normal" number + case 0: + case NUMBERFORMAT_NUMBER: + case NUMBERFORMAT_CURRENCY: + case NUMBERFORMAT_PERCENT: + { + // decimals + // only some built-in formats have automatic decimals + sal_Int32 nDecimals = nPrecision; // from GetFormatSpecialInfo + if ( eBuiltIn == NF_NUMBER_STANDARD || + eBuiltIn == NF_CURRENCY_1000DEC2 || + eBuiltIn == NF_CURRENCY_1000DEC2_RED || + eBuiltIn == NF_CURRENCY_1000DEC2_CCC || + eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ) + nDecimals = -1; + + // integer digits + // only one built-in format has automatic integer digits + sal_Int32 nInteger = nLeading; + if ( eBuiltIn == NF_NUMBER_SYSTEM ) + nInteger = -1; + + // string for decimal replacement + // has to be taken from nPrecision + // (positive number even for automatic decimals) + String sDashStr; + if ( bDecDashes && nPrecision > 0 ) + sDashStr.Fill( nPrecision, '-' ); + + WriteNumberElement_Impl( nDecimals, nInteger, sDashStr, bVarDecimals, + bThousand, nTrailingThousands, aEmbeddedEntries ); + bAnyContent = sal_True; + } + break; + case NUMBERFORMAT_SCIENTIFIC: + // #i43959# for scientific numbers, count all integer symbols ("0" and "#") + // as integer digits: use nIntegerSymbols instead of nLeading + // (use of '#' to select multiples in exponent might be added later) + WriteScientificElement_Impl( nPrecision, nIntegerSymbols, bThousand, nExpDigits ); + bAnyContent = sal_True; + break; + case NUMBERFORMAT_FRACTION: + { + sal_Int32 nInteger = nLeading; + if ( pElemStr && pElemStr->GetChar(0) == '?' ) + { + // If the first digit character is a question mark, + // the fraction doesn't have an integer part, and no + // min-integer-digits attribute must be written. + nInteger = -1; + } + WriteFractionElement_Impl( nInteger, bThousand, nPrecision, nPrecision ); + bAnyContent = sal_True; + } + break; + } + + bNumWritten = sal_True; + } + break; + case NF_SYMBOLTYPE_DECSEP: + if ( pElemStr && nPrecision == 0 ) + { + // A decimal separator after the number, without following decimal digits, + // isn't modelled as part of the number element, so it's written as text + // (the distinction between a quoted and non-quoted, locale-dependent + // character is lost here). + + AddToTextElement_Impl( *pElemStr ); + } + break; + case NF_SYMBOLTYPE_DEL: + if ( pElemStr && *pElemStr == XubString('@') ) + { + WriteTextContentElement_Impl(); + bAnyContent = sal_True; + } + break; + + case NF_SYMBOLTYPE_CALENDAR: + if ( pElemStr ) + aCalendar = *pElemStr; + break; + + // date elements: + + case NF_KEY_D: + case NF_KEY_DD: + { + sal_Bool bLong = ( nElemType == NF_KEY_DD ); + WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) ); + bAnyContent = sal_True; + } + break; + case NF_KEY_DDD: + case NF_KEY_DDDD: + case NF_KEY_NN: + case NF_KEY_NNN: + case NF_KEY_NNNN: + case NF_KEY_AAA: + case NF_KEY_AAAA: + { + OUString aCalAttr = aCalendar; + if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA ) + { + // calendar attribute for AAA and AAAA is switched only for this element + if (!aCalAttr.getLength()) + aCalAttr = lcl_GetDefaultCalendar( pFormatter, nLang ); + } + + sal_Bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN || + nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA ); + WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) ); + bAnyContent = sal_True; + if ( nElemType == NF_KEY_NNNN ) + { + // write additional text element for separator + pLocaleData->setLocale( MsLangId::convertLanguageToLocale( nLang ) ); + AddToTextElement_Impl( pLocaleData->getLongDateDayOfWeekSep() ); + } + } + break; + case NF_KEY_M: + case NF_KEY_MM: + case NF_KEY_MMM: + case NF_KEY_MMMM: + case NF_KEY_MMMMM: //! first letter of month name, no attribute available + { + sal_Bool bLong = ( nElemType == NF_KEY_MM || nElemType == NF_KEY_MMMM ); + sal_Bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM || + nElemType == NF_KEY_MMMMM ); + WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText ); + bAnyContent = sal_True; + } + break; + case NF_KEY_YY: + case NF_KEY_YYYY: + case NF_KEY_EC: + case NF_KEY_EEC: + case NF_KEY_R: //! R acts as EE, no attribute available + { + //! distinguish EE and R + // calendar attribute for E and EE and R is set in first loop + sal_Bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC || + nElemType == NF_KEY_R ); + WriteYearElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) ); + bAnyContent = sal_True; + } + break; + case NF_KEY_G: + case NF_KEY_GG: + case NF_KEY_GGG: + case NF_KEY_RR: //! RR acts as GGGEE, no attribute available + { + //! distinguish GG and GGG and RR + sal_Bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR ); + WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) ); + bAnyContent = sal_True; + if ( nElemType == NF_KEY_RR ) + { + // calendar attribute for RR is set in first loop + WriteYearElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : sal_True ) ); + } + } + break; + case NF_KEY_Q: + case NF_KEY_QQ: + { + sal_Bool bLong = ( nElemType == NF_KEY_QQ ); + WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) ); + bAnyContent = sal_True; + } + break; + case NF_KEY_WW: + WriteWeekElement_Impl( aCalendar ); + bAnyContent = sal_True; + break; + + // time elements (bSystemDate is not used): + + case NF_KEY_H: + case NF_KEY_HH: + WriteHoursElement_Impl( nElemType == NF_KEY_HH ); + bAnyContent = sal_True; + break; + case NF_KEY_MI: + case NF_KEY_MMI: + WriteMinutesElement_Impl( nElemType == NF_KEY_MMI ); + bAnyContent = sal_True; + break; + case NF_KEY_S: + case NF_KEY_SS: + WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision ); + bAnyContent = sal_True; + break; + case NF_KEY_AMPM: + case NF_KEY_AP: + WriteAMPMElement_Impl(); // short/long? + bAnyContent = sal_True; + break; + } + nPrevType = nElemType; + ++nPos; + } + } + + if ( sTextContent.getLength() ) + bAnyContent = sal_True; // element written in FinishTextElement_Impl + + FinishTextElement_Impl(); // final text element - before maps + + if ( !bAnyContent ) + { + // for an empty format, write an empty text element + SvXMLElementExport aTElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT, + sal_True, sal_False ); + } + + // + // mapping (conditions) must be last elements + // + + if (bDefPart) + { + SvNumberformatLimitOps eOp1, eOp2; + double fLimit1, fLimit2; + rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 ); + + WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 ); + WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 ); + + if ( rFormat.HasTextFormat() ) + { + // 4th part is for text -> make an "all other numbers" condition for the 3rd part + // by reversing the 2nd condition + + SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO; + double fLimit3 = fLimit2; + switch ( eOp2 ) + { + case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break; + case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break; + case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break; + case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break; + case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break; + case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break; + default: + break; + } + + if ( fLimit1 == fLimit2 && + ( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) || + ( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) ) + { + // For <x and >x, add =x as last condition + // (just for readability, <=x would be valid, too) + + eOp3 = NUMBERFORMAT_OP_EQ; + } + + WriteMapElement_Impl( eOp3, fLimit3, nKey, 2 ); + } + } +} + +//------------------------------------------------------------------------- + +// +// export one format +// + +void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey ) +{ + sal_uInt16 nUsedParts = 0; + sal_uInt16 nPart; + for (nPart=0; nPart<XMLNUM_MAX_PARTS; nPart++) + if (rFormat.GetNumForType( nPart, 0, sal_False ) != 0) + nUsedParts = nPart+1; + + SvNumberformatLimitOps eOp1, eOp2; + double fLimit1, fLimit2; + rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 ); + + // if conditions are set, even empty formats must be written + + if ( eOp1 != NUMBERFORMAT_OP_NO && nUsedParts < 2 ) + nUsedParts = 2; + if ( eOp2 != NUMBERFORMAT_OP_NO && nUsedParts < 3 ) + nUsedParts = 3; + if ( rFormat.HasTextFormat() && nUsedParts < 4 ) + nUsedParts = 4; + + for (nPart=0; nPart<nUsedParts; nPart++) + { + sal_Bool bDefault = ( nPart+1 == nUsedParts ); // last = default + ExportPart_Impl( rFormat, nKey, nPart, bDefault ); + } +} + +//------------------------------------------------------------------------- + +// +// export method called by application +// + +void SvXMLNumFmtExport::Export( sal_Bool bIsAutoStyle ) +{ + if ( !pFormatter ) + return; // no formatter -> no entries + + sal_uInt32 nKey; + const SvNumberformat* pFormat = NULL; + sal_Bool bNext(pUsedList->GetFirstUsed(nKey)); + while(bNext) + { + pFormat = pFormatter->GetEntry(nKey); + if(pFormat) + ExportFormat_Impl( *pFormat, nKey ); + bNext = pUsedList->GetNextUsed(nKey); + } + if (!bIsAutoStyle) + { + SvUShorts aLanguages; + pFormatter->GetUsedLanguages( aLanguages ); + sal_uInt16 nLangCount = aLanguages.Count(); + for (sal_uInt16 nLangPos=0; nLangPos<nLangCount; nLangPos++) + { + LanguageType nLang = aLanguages[nLangPos]; + + sal_uInt32 nDefaultIndex = 0; + SvNumberFormatTable& rTable = pFormatter->GetEntryTable( + NUMBERFORMAT_DEFINED, nDefaultIndex, nLang ); + pFormat = rTable.First(); + while (pFormat) + { + nKey = rTable.GetCurKey(); + if (!pUsedList->IsUsed(nKey)) + { + DBG_ASSERT((pFormat->GetType() & NUMBERFORMAT_DEFINED) != 0, "a not user defined numberformat found"); + // user-defined and used formats are exported + ExportFormat_Impl( *pFormat, nKey ); + // if it is a user-defined Format it will be added else nothing will hapen + pUsedList->SetUsed(nKey); + } + + pFormat = rTable.Next(); + } + } + } + pUsedList->Export(); +} + +OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey ) +{ + if(pUsedList->IsUsed(nKey) || pUsedList->IsWasUsed(nKey)) + return lcl_CreateStyleName( nKey, 0, sal_True, sPrefix ); + else + { + DBG_ERROR("There is no written Data-Style"); + return rtl::OUString(); + } +} + +void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey ) +{ + DBG_ASSERT( pFormatter != NULL, "missing formatter" ); + if( !pFormatter ) + return; + + if (pFormatter->GetEntry(nKey)) + pUsedList->SetUsed( nKey ); + else { + DBG_ERROR("no existing Numberformat found with this key"); + } +} + +void SvXMLNumFmtExport::GetWasUsed(uno::Sequence<sal_Int32>& rWasUsed) +{ + if (pUsedList) + pUsedList->GetWasUsed(rWasUsed); +} + +void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed) +{ + if (pUsedList) + pUsedList->SetWasUsed(rWasUsed); +} + + + +const SvNumberformat* lcl_GetFormat( SvNumberFormatter* pFormatter, + sal_uInt32 nKey ) +{ + return ( pFormatter != NULL ) ? pFormatter->GetEntry( nKey ) : NULL; +} + +sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey ) +{ + sal_uInt32 nRet = nKey; + + const SvNumberformat* pFormat = lcl_GetFormat( pFormatter, nKey ); + if( pFormat != NULL ) + { + DBG_ASSERT( pFormatter != NULL, "format without formatter?" ); + + xub_StrLen nErrorPos; + short nType = pFormat->GetType(); + + sal_uInt32 nNewKey = pFormatter->GetFormatForLanguageIfBuiltIn( + nKey, LANGUAGE_SYSTEM ); + + if( nNewKey != nKey ) + { + nRet = nNewKey; + } + else + { + String aFormatString( pFormat->GetFormatstring() ); + pFormatter->PutandConvertEntry( + aFormatString, + nErrorPos, nType, nNewKey, + pFormat->GetLanguage(), LANGUAGE_SYSTEM ); + + // success? Then use new key. + if( nErrorPos == 0 ) + nRet = nNewKey; + } + } + + return nRet; +} |