diff options
author | thb <thb@openoffice.org> | 2010-01-18 01:10:42 +0100 |
---|---|---|
committer | thb <thb@openoffice.org> | 2010-01-18 01:10:42 +0100 |
commit | ea48c18b848fed4d6504c956adeb3f24f05938ca (patch) | |
tree | 1eccf884707ec16e4dd74f8e83c74563e1539b34 /svl/source/numbers | |
parent | 1737f4d2fdba50a590f76631cd7ca7e762d18c35 (diff) | |
parent | 8765a3bf9f2926a50d0f644e4263782269abe023 (diff) |
thbfixes10: merge with DEV300 m69
Diffstat (limited to 'svl/source/numbers')
-rw-r--r-- | svl/source/numbers/makefile.mk | 78 | ||||
-rw-r--r-- | svl/source/numbers/nbdll.cxx | 82 | ||||
-rw-r--r-- | svl/source/numbers/numfmuno.cxx | 1143 | ||||
-rw-r--r-- | svl/source/numbers/numfmuno.hxx | 324 | ||||
-rw-r--r-- | svl/source/numbers/numhead.cxx | 252 | ||||
-rw-r--r-- | svl/source/numbers/numhead.hxx | 109 | ||||
-rw-r--r-- | svl/source/numbers/numuno.cxx | 170 | ||||
-rw-r--r-- | svl/source/numbers/supservs.cxx | 232 | ||||
-rw-r--r-- | svl/source/numbers/supservs.hxx | 105 | ||||
-rw-r--r-- | svl/source/numbers/zforfind.cxx | 2819 | ||||
-rw-r--r-- | svl/source/numbers/zforfind.hxx | 291 | ||||
-rw-r--r-- | svl/source/numbers/zforlist.cxx | 4348 | ||||
-rw-r--r-- | svl/source/numbers/zformat.cxx | 4480 | ||||
-rw-r--r-- | svl/source/numbers/zforscan.cxx | 2812 | ||||
-rw-r--r-- | svl/source/numbers/zforscan.hxx | 278 |
15 files changed, 17523 insertions, 0 deletions
diff --git a/svl/source/numbers/makefile.mk b/svl/source/numbers/makefile.mk new file mode 100644 index 000000000000..db4b30070ce8 --- /dev/null +++ b/svl/source/numbers/makefile.mk @@ -0,0 +1,78 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.12.148.1 $ +# +# 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. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=svl +TARGET=numbers +LIBTARGET=NO + +PROJECTPCH= +PROJECTPCHSOURCE= + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/svl.pmk + +# --- Files -------------------------------------------------------- + +EXCEPTIONSFILES= \ + $(SLO)$/numuno.obj \ + $(SLO)$/numfmuno.obj \ + $(SLO)$/supservs.obj \ + $(SLO)$/zforlist.obj + +SLOFILES = \ + $(EXCEPTIONSFILES) \ + $(SLO)$/zforfind.obj \ + $(SLO)$/zformat.obj \ + $(SLO)$/zforscan.obj \ + $(SLO)$/numhead.obj + +LIB1TARGET= $(SLB)$/$(TARGET).uno.lib +LIB1OBJFILES= \ + $(SLO)$/numfmuno.obj \ + $(SLO)$/supservs.obj + +LIB2TARGET= $(SLB)$/$(TARGET).lib +LIB2OBJFILES= \ + $(SLO)$/zforfind.obj \ + $(SLO)$/zforlist.obj \ + $(SLO)$/zformat.obj \ + $(SLO)$/zforscan.obj \ + $(SLO)$/numuno.obj \ + $(SLO)$/numhead.obj + +# --- Targets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/svl/source/numbers/nbdll.cxx b/svl/source/numbers/nbdll.cxx new file mode 100644 index 000000000000..2bc5121e18fd --- /dev/null +++ b/svl/source/numbers/nbdll.cxx @@ -0,0 +1,82 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: nbdll.cxx,v $ + * $Revision: 1.4 $ + * + * 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_svl.hxx" + +#ifdef WIN +#include <svwin.h> + +#ifndef _SYSDEP_HXX +#include <sysdep.hxx> +#endif + +// Statische DLL-Verwaltungs-Variablen +static HINSTANCE hDLLInst = 0; // HANDLE der DLL + + +/*************************************************************************** +|* +|* LibMain() +|* +|* Beschreibung Initialisierungsfunktion der DLL +|* Ersterstellung TH 05.05.93 +|* Letzte Aenderung TH 05.05.93 +|* +***************************************************************************/ + +extern "C" int CALLBACK LibMain( HINSTANCE hDLL, WORD, WORD nHeap, LPSTR ) +{ +#ifndef WNT + if ( nHeap ) + UnlockData( 0 ); +#endif + + hDLLInst = hDLL; + + return TRUE; +} + +/*************************************************************************** +|* +|* WEP() +|* +|* Beschreibung DLL-Deinitialisierung +|* Ersterstellung TH 05.05.93 +|* Letzte Aenderung TH 05.05.93 +|* +***************************************************************************/ + +extern "C" int CALLBACK WEP( int ) +{ + return 1; +} + +#endif diff --git a/svl/source/numbers/numfmuno.cxx b/svl/source/numbers/numfmuno.cxx new file mode 100644 index 000000000000..23f627834955 --- /dev/null +++ b/svl/source/numbers/numfmuno.cxx @@ -0,0 +1,1143 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: numfmuno.cxx,v $ + * $Revision: 1.15 $ + * + * 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_svl.hxx" +#ifndef GCC +#endif + +#include <tools/color.hxx> +#include <tools/debug.hxx> +#include <i18npool/mslangid.hxx> +#include <vos/mutex.hxx> +#include <rtl/ustring.hxx> + +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include "numfmuno.hxx" +#include "numuno.hxx" +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <svl/itemprop.hxx> + +using namespace com::sun::star; + +//------------------------------------------------------------------------ + +#define SERVICENAME_NUMBERFORMATTER "com.sun.star.util.NumberFormatter" +#define SERVICENAME_NUMBERSETTINGS "com.sun.star.util.NumberFormatSettings" +#define SERVICENAME_NUMBERFORMATS "com.sun.star.util.NumberFormats" +#define SERVICENAME_NUMBERFORMAT "com.sun.star.util.NumberFormatProperties" + +//------------------------------------------------------------------------ + +#define PROPERTYNAME_FMTSTR "FormatString" +#define PROPERTYNAME_LOCALE "Locale" +#define PROPERTYNAME_TYPE "Type" +#define PROPERTYNAME_COMMENT "Comment" +#define PROPERTYNAME_CURREXT "CurrencyExtension" +#define PROPERTYNAME_CURRSYM "CurrencySymbol" +#define PROPERTYNAME_CURRABB "CurrencyAbbreviation" +#define PROPERTYNAME_DECIMALS "Decimals" +#define PROPERTYNAME_LEADING "LeadingZeros" +#define PROPERTYNAME_NEGRED "NegativeRed" +#define PROPERTYNAME_STDFORM "StandardFormat" +#define PROPERTYNAME_THOUS "ThousandsSeparator" +#define PROPERTYNAME_USERDEF "UserDefined" + +#define PROPERTYNAME_NOZERO "NoZero" +#define PROPERTYNAME_NULLDATE "NullDate" +#define PROPERTYNAME_STDDEC "StandardDecimals" +#define PROPERTYNAME_TWODIGIT "TwoDigitDateStart" + +//------------------------------------------------------------------------ + +// alles ohne Which-ID, Map nur fuer PropertySetInfo + +const SfxItemPropertyMapEntry* lcl_GetNumberFormatPropertyMap() +{ + static SfxItemPropertyMapEntry aNumberFormatPropertyMap_Impl[] = + { + {MAP_CHAR_LEN(PROPERTYNAME_FMTSTR), 0, &getCppuType((rtl::OUString*)0),beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_LOCALE), 0, &getCppuType((lang::Locale*)0),beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_TYPE), 0, &getCppuType((sal_Int16*)0), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_COMMENT), 0, &getCppuType((rtl::OUString*)0),beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_CURREXT), 0, &getCppuType((rtl::OUString*)0),beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_CURRSYM), 0, &getCppuType((rtl::OUString*)0),beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_DECIMALS), 0, &getCppuType((sal_Int16*)0), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_LEADING), 0, &getCppuType((sal_Int16*)0), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_NEGRED), 0, &getBooleanCppuType(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_STDFORM), 0, &getBooleanCppuType(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_THOUS), 0, &getBooleanCppuType(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_USERDEF), 0, &getBooleanCppuType(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_CURRABB), 0, &getCppuType((rtl::OUString*)0),beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY, 0}, + {0,0,0,0,0,0} + }; + return aNumberFormatPropertyMap_Impl; +} + +const SfxItemPropertyMapEntry* lcl_GetNumberSettingsPropertyMap() +{ + static SfxItemPropertyMapEntry aNumberSettingsPropertyMap_Impl[] = + { + {MAP_CHAR_LEN(PROPERTYNAME_NOZERO), 0, &getBooleanCppuType(), beans::PropertyAttribute::BOUND, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_NULLDATE), 0, &getCppuType((util::Date*)0), beans::PropertyAttribute::BOUND, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_STDDEC), 0, &getCppuType((sal_Int16*)0), beans::PropertyAttribute::BOUND, 0}, + {MAP_CHAR_LEN(PROPERTYNAME_TWODIGIT), 0, &getCppuType((sal_Int16*)0), beans::PropertyAttribute::BOUND, 0}, + {0,0,0,0,0,0} + }; + return aNumberSettingsPropertyMap_Impl; +} + +//---------------------------------------------------------------------------------------- + +LanguageType lcl_GetLanguage( const lang::Locale& rLocale ) +{ + // empty language -> LANGUAGE_SYSTEM + if ( rLocale.Language.getLength() == 0 ) + return LANGUAGE_SYSTEM; + + LanguageType eRet = MsLangId::convertLocaleToLanguage( rLocale ); + if ( eRet == LANGUAGE_NONE ) + eRet = LANGUAGE_SYSTEM; //! or throw an exception? + + return eRet; +} + +//---------------------------------------------------------------------------------------- + +SvNumberFormatterServiceObj::SvNumberFormatterServiceObj() + :m_aMutex() +{ +} + +SvNumberFormatterServiceObj::~SvNumberFormatterServiceObj() +{ +} + +com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL SvNumberFormatterServiceObj_CreateInstance( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& ) +{ + return ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface >( ( ::cppu::OWeakObject* ) new SvNumberFormatterServiceObj ); +} + +// XNumberFormatter + +void SAL_CALL SvNumberFormatterServiceObj::attachNumberFormatsSupplier( + const uno::Reference<util::XNumberFormatsSupplier>& _xSupplier ) + throw(uno::RuntimeException) +{ + ::rtl::Reference< SvNumberFormatsSupplierObj > xAutoReleaseOld; + + // SYNCHRONIZED -> + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + + SvNumberFormatsSupplierObj* pNew = SvNumberFormatsSupplierObj::getImplementation( _xSupplier ); + if (!pNew) + throw uno::RuntimeException(); // wrong object + + xAutoReleaseOld = xSupplier; + + xSupplier = pNew; + m_aMutex = xSupplier->getSharedMutex(); + } + // <- SYNCHRONIZED +} + +uno::Reference<util::XNumberFormatsSupplier> SAL_CALL + SvNumberFormatterServiceObj::getNumberFormatsSupplier() + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return xSupplier.get(); +} + +sal_Int32 SAL_CALL SvNumberFormatterServiceObj::detectNumberFormat( + sal_Int32 nKey, const rtl::OUString& aString ) + throw(util::NotNumericException, uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aTemp = aString; + sal_uInt32 nUKey = nKey; + double fValue = 0.0; + if ( pFormatter->IsNumberFormat(aTemp, nUKey, fValue) ) + nRet = nUKey; + else + throw util::NotNumericException(); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +double SAL_CALL SvNumberFormatterServiceObj::convertStringToNumber( + sal_Int32 nKey, const rtl::OUString& aString ) + throw(util::NotNumericException, uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + double fRet = 0.0; + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aTemp = aString; + sal_uInt32 nUKey = nKey; + double fValue = 0.0; + if ( pFormatter->IsNumberFormat(aTemp, nUKey, fValue) ) + fRet = fValue; + else + throw util::NotNumericException(); + } + else + throw uno::RuntimeException(); + + return fRet; +} + +rtl::OUString SAL_CALL SvNumberFormatterServiceObj::convertNumberToString( + sal_Int32 nKey, double fValue ) throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + String aRet; + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + Color* pColor = NULL; + pFormatter->GetOutputString(fValue, nKey, aRet, &pColor); + } + else + throw uno::RuntimeException(); + + return aRet; +} + +util::Color SAL_CALL SvNumberFormatterServiceObj::queryColorForNumber( sal_Int32 nKey, + double fValue, util::Color aDefaultColor ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + util::Color nRet = aDefaultColor; // color = INT32 + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aStr; + Color* pColor = NULL; + pFormatter->GetOutputString(fValue, nKey, aStr, &pColor); + if (pColor) + nRet = pColor->GetColor(); + // sonst Default behalten + } + else + throw uno::RuntimeException(); + + return nRet; +} + +rtl::OUString SAL_CALL SvNumberFormatterServiceObj::formatString( sal_Int32 nKey, + const rtl::OUString& aString ) throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + String aRet; + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aTemp = aString; + Color* pColor = NULL; + pFormatter->GetOutputString(aTemp, nKey, aRet, &pColor); + } + else + throw uno::RuntimeException(); + + return aRet; +} + +util::Color SAL_CALL SvNumberFormatterServiceObj::queryColorForString( sal_Int32 nKey, + const rtl::OUString& aString,util::Color aDefaultColor ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + util::Color nRet = aDefaultColor; // color = INT32 + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aTemp = aString; + String aStr; + Color* pColor = NULL; + pFormatter->GetOutputString(aTemp, nKey, aStr, &pColor); + if (pColor) + nRet = pColor->GetColor(); + // sonst Default behalten + } + else + throw uno::RuntimeException(); + + return nRet; +} + +rtl::OUString SAL_CALL SvNumberFormatterServiceObj::getInputString( sal_Int32 nKey, double fValue ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + String aRet; + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + pFormatter->GetInputLineString(fValue, nKey, aRet); + else + throw uno::RuntimeException(); + + return aRet; +} + +// XNumberFormatPreviewer + +rtl::OUString SAL_CALL SvNumberFormatterServiceObj::convertNumberToPreviewString( + const rtl::OUString& aFormat, double fValue, + const lang::Locale& nLocale, sal_Bool bAllowEnglish ) + throw(util::MalformedNumberFormatException, uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + String aRet; + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aOutString; + String aFormString = aFormat; + LanguageType eLang = lcl_GetLanguage( nLocale ); + Color* pColor = NULL; + + BOOL bOk; + if ( bAllowEnglish ) + bOk = pFormatter->GetPreviewStringGuess( + aFormString, fValue, aOutString, &pColor, eLang ); + else + bOk = pFormatter->GetPreviewString( + aFormString, fValue, aOutString, &pColor, eLang ); + + if (bOk) + aRet = aOutString; + else + throw util::MalformedNumberFormatException(); + } + else + throw uno::RuntimeException(); + + return aRet; +} + +util::Color SAL_CALL SvNumberFormatterServiceObj::queryPreviewColorForNumber( + const rtl::OUString& aFormat, double fValue, + const lang::Locale& nLocale, sal_Bool bAllowEnglish, + util::Color aDefaultColor ) + throw(util::MalformedNumberFormatException, uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + util::Color nRet = aDefaultColor; // color = INT32 + SvNumberFormatter* pFormatter = xSupplier.is() ? xSupplier->GetNumberFormatter() : NULL; + if (pFormatter) + { + String aOutString; + String aFormString = aFormat; + LanguageType eLang = lcl_GetLanguage( nLocale ); + Color* pColor = NULL; + + BOOL bOk; + if ( bAllowEnglish ) + bOk = pFormatter->GetPreviewStringGuess( + aFormString, fValue, aOutString, &pColor, eLang ); + else + bOk = pFormatter->GetPreviewString( + aFormString, fValue, aOutString, &pColor, eLang ); + + if (bOk) + { + if (pColor) + nRet = pColor->GetColor(); + // sonst Default behalten + } + else + throw util::MalformedNumberFormatException(); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +// XServiceInfo + +rtl::OUString SAL_CALL SvNumberFormatterServiceObj::getImplementationName() + throw(uno::RuntimeException) +{ + return rtl::OUString::createFromAscii("com.sun.star.uno.util.numbers.SvNumberFormatterServiceObject"); +} + +sal_Bool SAL_CALL SvNumberFormatterServiceObj::supportsService( const rtl::OUString& ServiceName ) + throw(uno::RuntimeException) +{ + return ( ServiceName.compareToAscii(SERVICENAME_NUMBERFORMATTER) == 0 ); +} + +uno::Sequence<rtl::OUString> SAL_CALL SvNumberFormatterServiceObj::getSupportedServiceNames() + throw(uno::RuntimeException) +{ + uno::Sequence<rtl::OUString> aRet(1); + rtl::OUString* pArray = aRet.getArray(); + pArray[0] = rtl::OUString::createFromAscii(SERVICENAME_NUMBERFORMATTER); + return aRet; +} + +//------------------------------------------------------------------------ + +SvNumberFormatsObj::SvNumberFormatsObj( SvNumberFormatsSupplierObj& _rParent, ::comphelper::SharedMutex& _rMutex ) + :rSupplier( _rParent ) + ,m_aMutex( _rMutex ) +{ + rSupplier.acquire(); +} + +SvNumberFormatsObj::~SvNumberFormatsObj() +{ + rSupplier.release(); +} + +// XNumberFormats + +uno::Reference<beans::XPropertySet> SAL_CALL SvNumberFormatsObj::getByKey( sal_Int32 nKey ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + const SvNumberformat* pFormat = pFormatter ? pFormatter->GetEntry(nKey) : NULL; + if (pFormat) + return new SvNumberFormatObj( rSupplier, nKey, m_aMutex ); + else + throw uno::RuntimeException(); +} + +uno::Sequence<sal_Int32> SAL_CALL SvNumberFormatsObj::queryKeys( sal_Int16 nType, + const lang::Locale& nLocale, sal_Bool bCreate ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if ( pFormatter ) + { + sal_uInt32 nIndex = 0; + LanguageType eLang = lcl_GetLanguage( nLocale ); + SvNumberFormatTable& rTable = bCreate ? + pFormatter->ChangeCL( nType, nIndex, eLang ) : + pFormatter->GetEntryTable( nType, nIndex, eLang ); + sal_uInt32 nCount = rTable.Count(); + uno::Sequence<sal_Int32> aSeq(nCount); + sal_Int32* pAry = aSeq.getArray(); + for (sal_uInt32 i=0; i<nCount; i++) + pAry[i] = rTable.GetObjectKey( i ); + + return aSeq; + } + else + throw uno::RuntimeException(); +} + +sal_Int32 SAL_CALL SvNumberFormatsObj::queryKey( const rtl::OUString& aFormat, + const lang::Locale& nLocale, sal_Bool bScan ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + String aFormStr = aFormat; + LanguageType eLang = lcl_GetLanguage( nLocale ); + if (bScan) + { + //! irgendwas muss hier noch passieren... + } + nRet = pFormatter->GetEntryKey( aFormat, eLang ); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +sal_Int32 SAL_CALL SvNumberFormatsObj::addNew( const rtl::OUString& aFormat, + const lang::Locale& nLocale ) + throw(util::MalformedNumberFormatException, uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + String aFormStr = aFormat; + LanguageType eLang = lcl_GetLanguage( nLocale ); + sal_uInt32 nKey = 0; + xub_StrLen nCheckPos = 0; + short nType = 0; + BOOL bOk = pFormatter->PutEntry( aFormStr, nCheckPos, nType, nKey, eLang ); + if (bOk) + nRet = nKey; + else if (nCheckPos) + { + throw util::MalformedNumberFormatException(); // ungueltiges Format + } + else + throw uno::RuntimeException(); // anderer Fehler (z.B. schon vorhanden) + } + else + throw uno::RuntimeException(); + + return nRet; +} + +sal_Int32 SAL_CALL SvNumberFormatsObj::addNewConverted( const rtl::OUString& aFormat, + const lang::Locale& nLocale, const lang::Locale& nNewLocale ) + throw(util::MalformedNumberFormatException, uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + String aFormStr = aFormat; + LanguageType eLang = lcl_GetLanguage( nLocale ); + LanguageType eNewLang = lcl_GetLanguage( nNewLocale ); + sal_uInt32 nKey = 0; + xub_StrLen nCheckPos = 0; + short nType = 0; + BOOL bOk = pFormatter->PutandConvertEntry( aFormStr, nCheckPos, nType, nKey, eLang, eNewLang ); + if (bOk || nKey > 0) + nRet = nKey; + else if (nCheckPos) + { + throw util::MalformedNumberFormatException(); // ungueltiges Format + } + else + throw uno::RuntimeException(); // anderer Fehler (z.B. schon vorhanden) + } + else + throw uno::RuntimeException(); + + return nRet; +} + +void SAL_CALL SvNumberFormatsObj::removeByKey( sal_Int32 nKey ) throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + + if (pFormatter) + { + pFormatter->DeleteEntry(nKey); + rSupplier.NumberFormatDeleted(nKey); // Benachrichtigung fuers Dokument + } +} + +rtl::OUString SAL_CALL SvNumberFormatsObj::generateFormat( sal_Int32 nBaseKey, + const lang::Locale& nLocale, sal_Bool bThousands, + sal_Bool bRed, sal_Int16 nDecimals, sal_Int16 nLeading ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + String aRet; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + LanguageType eLang = lcl_GetLanguage( nLocale ); + pFormatter->GenerateFormat( aRet, nBaseKey, eLang, bThousands, bRed, nDecimals, nLeading ); + } + else + throw uno::RuntimeException(); + + return aRet; +} + +// XNumberFormatTypes + +sal_Int32 SAL_CALL SvNumberFormatsObj::getStandardIndex( const lang::Locale& nLocale ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + LanguageType eLang = lcl_GetLanguage( nLocale ); + nRet = pFormatter->GetStandardIndex(eLang); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +sal_Int32 SAL_CALL SvNumberFormatsObj::getStandardFormat( sal_Int16 nType, const lang::Locale& nLocale ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + LanguageType eLang = lcl_GetLanguage( nLocale ); + // mask out "defined" bit, so type from an existing number format + // can directly be used for getStandardFormat + nType &= ~NUMBERFORMAT_DEFINED; + nRet = pFormatter->GetStandardFormat(nType, eLang); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +sal_Int32 SAL_CALL SvNumberFormatsObj::getFormatIndex( sal_Int16 nIndex, const lang::Locale& nLocale ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + LanguageType eLang = lcl_GetLanguage( nLocale ); + nRet = pFormatter->GetFormatIndex( (NfIndexTableOffset)nIndex, eLang ); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +sal_Bool SAL_CALL SvNumberFormatsObj::isTypeCompatible( sal_Int16 nOldType, sal_Int16 nNewType ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + BOOL bRet = FALSE; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + bRet = pFormatter->IsCompatible( nOldType, nNewType ); + else + throw uno::RuntimeException(); + + return bRet; +} + +sal_Int32 SAL_CALL SvNumberFormatsObj::getFormatForLocale( sal_Int32 nKey, const lang::Locale& nLocale ) + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + INT32 nRet = 0; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + LanguageType eLang = lcl_GetLanguage( nLocale ); + nRet = pFormatter->GetFormatForLanguageIfBuiltIn(nKey, eLang); + } + else + throw uno::RuntimeException(); + + return nRet; +} + +// XServiceInfo + +rtl::OUString SAL_CALL SvNumberFormatsObj::getImplementationName() + throw(uno::RuntimeException) +{ + return rtl::OUString::createFromAscii("SvNumberFormatsObj"); +} + +sal_Bool SAL_CALL SvNumberFormatsObj::supportsService( const rtl::OUString& ServiceName ) + throw(uno::RuntimeException) +{ + return ( ServiceName.compareToAscii(SERVICENAME_NUMBERFORMATS) == 0 ); +} + +uno::Sequence<rtl::OUString> SAL_CALL SvNumberFormatsObj::getSupportedServiceNames() + throw(uno::RuntimeException) +{ + uno::Sequence<rtl::OUString> aRet(1); + rtl::OUString* pArray = aRet.getArray(); + pArray[0] = rtl::OUString::createFromAscii(SERVICENAME_NUMBERFORMATS); + return aRet; +} + +//------------------------------------------------------------------------ + +SvNumberFormatObj::SvNumberFormatObj( SvNumberFormatsSupplierObj& rParent, ULONG nK, const ::comphelper::SharedMutex& _rMutex ) + :rSupplier( rParent ) + ,nKey( nK ) + ,m_aMutex( _rMutex ) +{ + rSupplier.acquire(); +} + +SvNumberFormatObj::~SvNumberFormatObj() +{ + rSupplier.release(); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL SvNumberFormatObj::getPropertySetInfo() + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( lcl_GetNumberFormatPropertyMap() ); + return aRef; +} + +void SAL_CALL SvNumberFormatObj::setPropertyValue( const rtl::OUString&, + const uno::Any& ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + throw beans::UnknownPropertyException(); // everything is read-only +} + +uno::Any SAL_CALL SvNumberFormatObj::getPropertyValue( const rtl::OUString& aPropertyName ) + throw(beans::UnknownPropertyException, lang::WrappedTargetException, + uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + uno::Any aRet; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + const SvNumberformat* pFormat = pFormatter ? pFormatter->GetEntry(nKey) : NULL; + if (pFormat) + { + BOOL bThousand, bRed; + USHORT nDecimals, nLeading; + + String aString = aPropertyName; + if (aString.EqualsAscii( PROPERTYNAME_FMTSTR )) + { + aRet <<= rtl::OUString( pFormat->GetFormatstring() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_LOCALE )) + { + lang::Locale aLocale( MsLangId::convertLanguageToLocale( + pFormat->GetLanguage())); + aRet <<= aLocale; + } + else if (aString.EqualsAscii( PROPERTYNAME_TYPE )) + { + aRet <<= (sal_Int16)( pFormat->GetType() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_COMMENT )) + { + aRet <<= rtl::OUString( pFormat->GetComment() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_STDFORM )) + { + //! SvNumberformat Member bStandard rausreichen? + BOOL bStandard = ( ( nKey % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 ); + aRet.setValue( &bStandard, getBooleanCppuType() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_USERDEF )) + { + BOOL bUserDef = ( ( pFormat->GetType() & NUMBERFORMAT_DEFINED ) != 0 ); + aRet.setValue( &bUserDef, getBooleanCppuType() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_DECIMALS )) + { + pFormat->GetFormatSpecialInfo( bThousand, bRed, nDecimals, nLeading ); + aRet <<= (sal_Int16)( nDecimals ); + } + else if (aString.EqualsAscii( PROPERTYNAME_LEADING )) + { + pFormat->GetFormatSpecialInfo( bThousand, bRed, nDecimals, nLeading ); + aRet <<= (sal_Int16)( nLeading ); + } + else if (aString.EqualsAscii( PROPERTYNAME_NEGRED )) + { + pFormat->GetFormatSpecialInfo( bThousand, bRed, nDecimals, nLeading ); + aRet.setValue( &bRed, getBooleanCppuType() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_THOUS )) + { + pFormat->GetFormatSpecialInfo( bThousand, bRed, nDecimals, nLeading ); + aRet.setValue( &bThousand, getBooleanCppuType() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_CURRSYM )) + { + String aSymbol, aExt; + pFormat->GetNewCurrencySymbol( aSymbol, aExt ); + aRet <<= rtl::OUString( aSymbol ); + } + else if (aString.EqualsAscii( PROPERTYNAME_CURREXT )) + { + String aSymbol, aExt; + pFormat->GetNewCurrencySymbol( aSymbol, aExt ); + aRet <<= rtl::OUString( aExt ); + } + else if (aString.EqualsAscii( PROPERTYNAME_CURRABB )) + { + String aSymbol, aExt; + BOOL bBank = FALSE; + pFormat->GetNewCurrencySymbol( aSymbol, aExt ); + const NfCurrencyEntry* pCurr = pFormatter->GetCurrencyEntry( bBank, + aSymbol, aExt, pFormat->GetLanguage() ); + if ( pCurr ) + aRet <<= rtl::OUString( pCurr->GetBankSymbol() ); + else + aRet <<= rtl::OUString(); + } + else + throw beans::UnknownPropertyException(); + } + else + throw uno::RuntimeException(); + + return aRet; +} + +void SAL_CALL SvNumberFormatObj::addPropertyChangeListener( const rtl::OUString&, + const uno::Reference<beans::XPropertyChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +void SAL_CALL SvNumberFormatObj::removePropertyChangeListener( const rtl::OUString&, + const uno::Reference<beans::XPropertyChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +void SAL_CALL SvNumberFormatObj::addVetoableChangeListener( const rtl::OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +void SAL_CALL SvNumberFormatObj::removeVetoableChangeListener( const rtl::OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +// XPropertyAccess + +uno::Sequence<beans::PropertyValue> SAL_CALL SvNumberFormatObj::getPropertyValues() + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + const SvNumberformat* pFormat = pFormatter ? pFormatter->GetEntry(nKey) : NULL; + if (pFormat) + { + String aSymbol, aExt, aAbb; + BOOL bBank = FALSE; + pFormat->GetNewCurrencySymbol( aSymbol, aExt ); + const NfCurrencyEntry* pCurr = pFormatter->GetCurrencyEntry( bBank, + aSymbol, aExt, pFormat->GetLanguage() ); + if ( pCurr ) + aAbb = pCurr->GetBankSymbol(); + + String aFmtStr = pFormat->GetFormatstring(); + String aComment = pFormat->GetComment(); + BOOL bStandard = ( ( nKey % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 ); + //! SvNumberformat Member bStandard rausreichen? + BOOL bUserDef = ( ( pFormat->GetType() & NUMBERFORMAT_DEFINED ) != 0 ); + BOOL bThousand, bRed; + USHORT nDecimals, nLeading; + pFormat->GetFormatSpecialInfo( bThousand, bRed, nDecimals, nLeading ); + lang::Locale aLocale( MsLangId::convertLanguageToLocale( + pFormat->GetLanguage())); + + uno::Sequence<beans::PropertyValue> aSeq(13); + beans::PropertyValue* pArray = aSeq.getArray(); + + pArray[0].Name = rtl::OUString::createFromAscii( PROPERTYNAME_FMTSTR ); + pArray[0].Value <<= rtl::OUString( aFmtStr ); + pArray[1].Name = rtl::OUString::createFromAscii( PROPERTYNAME_LOCALE ); + pArray[1].Value <<= aLocale; + pArray[2].Name = rtl::OUString::createFromAscii( PROPERTYNAME_TYPE ); + pArray[2].Value <<= (sal_Int16)( pFormat->GetType() ); + pArray[3].Name = rtl::OUString::createFromAscii( PROPERTYNAME_COMMENT ); + pArray[3].Value <<= rtl::OUString( aComment ); + pArray[4].Name = rtl::OUString::createFromAscii( PROPERTYNAME_STDFORM ); + pArray[4].Value.setValue( &bStandard, getBooleanCppuType() ); + pArray[5].Name = rtl::OUString::createFromAscii( PROPERTYNAME_USERDEF ); + pArray[5].Value.setValue( &bUserDef, getBooleanCppuType() ); + pArray[6].Name = rtl::OUString::createFromAscii( PROPERTYNAME_DECIMALS ); + pArray[6].Value <<= (sal_Int16)( nDecimals ); + pArray[7].Name = rtl::OUString::createFromAscii( PROPERTYNAME_LEADING ); + pArray[7].Value <<= (sal_Int16)( nLeading ); + pArray[8].Name = rtl::OUString::createFromAscii( PROPERTYNAME_NEGRED ); + pArray[8].Value.setValue( &bRed, getBooleanCppuType() ); + pArray[9].Name = rtl::OUString::createFromAscii( PROPERTYNAME_THOUS ); + pArray[9].Value.setValue( &bThousand, getBooleanCppuType() ); + pArray[10].Name = rtl::OUString::createFromAscii( PROPERTYNAME_CURRSYM ); + pArray[10].Value <<= rtl::OUString( aSymbol ); + pArray[11].Name = rtl::OUString::createFromAscii( PROPERTYNAME_CURREXT ); + pArray[11].Value <<= rtl::OUString( aExt ); + pArray[12].Name = rtl::OUString::createFromAscii( PROPERTYNAME_CURRABB ); + pArray[12].Value <<= rtl::OUString( aAbb ); + + return aSeq; + } + else + throw uno::RuntimeException(); +} + +void SAL_CALL SvNumberFormatObj::setPropertyValues( const uno::Sequence<beans::PropertyValue>& ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + throw beans::UnknownPropertyException(); // everything is read-only +} + +// XServiceInfo + +rtl::OUString SAL_CALL SvNumberFormatObj::getImplementationName() + throw(uno::RuntimeException) +{ + return rtl::OUString::createFromAscii("SvNumberFormatObj"); +} + +sal_Bool SAL_CALL SvNumberFormatObj::supportsService( const rtl::OUString& ServiceName ) + throw(uno::RuntimeException) +{ + return ( ServiceName.compareToAscii(SERVICENAME_NUMBERFORMAT) == 0 ); +} + +uno::Sequence<rtl::OUString> SAL_CALL SvNumberFormatObj::getSupportedServiceNames() + throw(uno::RuntimeException) +{ + uno::Sequence<rtl::OUString> aRet(1); + rtl::OUString* pArray = aRet.getArray(); + pArray[0] = rtl::OUString::createFromAscii(SERVICENAME_NUMBERFORMAT); + return aRet; +} + +//------------------------------------------------------------------------ + +SvNumberFormatSettingsObj::SvNumberFormatSettingsObj( SvNumberFormatsSupplierObj& rParent, const ::comphelper::SharedMutex& _rMutex ) + :rSupplier( rParent ) + ,m_aMutex( _rMutex ) +{ + rSupplier.acquire(); +} + +SvNumberFormatSettingsObj::~SvNumberFormatSettingsObj() +{ + rSupplier.release(); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL SvNumberFormatSettingsObj::getPropertySetInfo() + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( lcl_GetNumberSettingsPropertyMap() ); + return aRef; +} + +void SAL_CALL SvNumberFormatSettingsObj::setPropertyValue( const rtl::OUString& aPropertyName, + const uno::Any& aValue ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + String aString = aPropertyName; + if (aString.EqualsAscii( PROPERTYNAME_NOZERO )) + { + // operator >>= shouldn't be used for bool (?) + if ( aValue.getValueTypeClass() == uno::TypeClass_BOOLEAN ) + pFormatter->SetNoZero( *(sal_Bool*)aValue.getValue() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_NULLDATE )) + { + util::Date aDate; + if ( aValue >>= aDate ) + pFormatter->ChangeNullDate( aDate.Day, aDate.Month, aDate.Year ); + } + else if (aString.EqualsAscii( PROPERTYNAME_STDDEC )) + { + sal_Int16 nInt16 = sal_Int16(); + if ( aValue >>= nInt16 ) + pFormatter->ChangeStandardPrec( nInt16 ); + } + else if (aString.EqualsAscii( PROPERTYNAME_TWODIGIT )) + { + sal_Int16 nInt16 = sal_Int16(); + if ( aValue >>= nInt16 ) + pFormatter->SetYear2000( nInt16 ); + } + else + throw beans::UnknownPropertyException(); + + rSupplier.SettingsChanged(); + } + else + throw uno::RuntimeException(); +} + +uno::Any SAL_CALL SvNumberFormatSettingsObj::getPropertyValue( const rtl::OUString& aPropertyName ) + throw(beans::UnknownPropertyException, lang::WrappedTargetException, + uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + uno::Any aRet; + SvNumberFormatter* pFormatter = rSupplier.GetNumberFormatter(); + if (pFormatter) + { + String aString = aPropertyName; + if (aString.EqualsAscii( PROPERTYNAME_NOZERO )) + { + BOOL bNoZero = pFormatter->GetNoZero(); + aRet.setValue( &bNoZero, getBooleanCppuType() ); + } + else if (aString.EqualsAscii( PROPERTYNAME_NULLDATE )) + { + Date* pDate = pFormatter->GetNullDate(); + if (pDate) + { + util::Date aUnoDate( pDate->GetDay(), pDate->GetMonth(), pDate->GetYear() ); + aRet <<= aUnoDate; + } + } + else if (aString.EqualsAscii( PROPERTYNAME_STDDEC )) + aRet <<= (sal_Int16)( pFormatter->GetStandardPrec() ); + else if (aString.EqualsAscii( PROPERTYNAME_TWODIGIT )) + aRet <<= (sal_Int16)( pFormatter->GetYear2000() ); + else + throw beans::UnknownPropertyException(); + } + else + throw uno::RuntimeException(); + + return aRet; +} + +void SAL_CALL SvNumberFormatSettingsObj::addPropertyChangeListener( const rtl::OUString&, + const uno::Reference<beans::XPropertyChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +void SAL_CALL SvNumberFormatSettingsObj::removePropertyChangeListener( const rtl::OUString&, + const uno::Reference<beans::XPropertyChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +void SAL_CALL SvNumberFormatSettingsObj::addVetoableChangeListener( const rtl::OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +void SAL_CALL SvNumberFormatSettingsObj::removeVetoableChangeListener( const rtl::OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) + throw(beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + DBG_ERROR("not implemented"); +} + +// XServiceInfo + +rtl::OUString SAL_CALL SvNumberFormatSettingsObj::getImplementationName() + throw(uno::RuntimeException) +{ + return rtl::OUString::createFromAscii("SvNumberFormatSettingsObj"); +} + +sal_Bool SAL_CALL SvNumberFormatSettingsObj::supportsService( const rtl::OUString& ServiceName ) + throw(uno::RuntimeException) +{ + return ( ServiceName.compareToAscii(SERVICENAME_NUMBERSETTINGS) == 0 ); +} + +uno::Sequence<rtl::OUString> SAL_CALL SvNumberFormatSettingsObj::getSupportedServiceNames() + throw(uno::RuntimeException) +{ + uno::Sequence<rtl::OUString> aRet(1); + rtl::OUString* pArray = aRet.getArray(); + pArray[0] = rtl::OUString::createFromAscii(SERVICENAME_NUMBERSETTINGS); + return aRet; +} + + diff --git a/svl/source/numbers/numfmuno.hxx b/svl/source/numbers/numfmuno.hxx new file mode 100644 index 000000000000..4148069fb37e --- /dev/null +++ b/svl/source/numbers/numfmuno.hxx @@ -0,0 +1,324 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: numfmuno.hxx,v $ + * $Revision: 1.4 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _NUMFMUNO_HXX +#define _NUMFMUNO_HXX + +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/util/XNumberFormatPreviewer.hpp> +#include <com/sun/star/util/XNumberFormats.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <cppuhelper/implbase2.hxx> +#include <cppuhelper/implbase3.hxx> +#include <comphelper/sharedmutex.hxx> +#include <rtl/ref.hxx> + +class SvNumberformat; +class SvNumberFormatter; +class SvNumberFormatsSupplierObj; + + +// SvNumberFormatterServiceObj wird global als Service angemeldet + +class SvNumberFormatterServiceObj : public cppu::WeakImplHelper3< + com::sun::star::util::XNumberFormatter, + com::sun::star::util::XNumberFormatPreviewer, + com::sun::star::lang::XServiceInfo> +{ +private: + ::rtl::Reference< SvNumberFormatsSupplierObj > xSupplier; + mutable ::comphelper::SharedMutex m_aMutex; + +public: + SvNumberFormatterServiceObj(); + virtual ~SvNumberFormatterServiceObj(); + + // XNumberFormatter + virtual void SAL_CALL attachNumberFormatsSupplier( + const ::com::sun::star::uno::Reference< + ::com::sun::star::util::XNumberFormatsSupplier >& xSupplier ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier > + SAL_CALL getNumberFormatsSupplier() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL detectNumberFormat( sal_Int32 nKey, const ::rtl::OUString& aString ) + throw(::com::sun::star::util::NotNumericException, + ::com::sun::star::uno::RuntimeException); + virtual double SAL_CALL convertStringToNumber( sal_Int32 nKey, const ::rtl::OUString& aString ) + throw(::com::sun::star::util::NotNumericException, + ::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL convertNumberToString( sal_Int32 nKey, double fValue ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::util::Color SAL_CALL queryColorForNumber( sal_Int32 nKey, + double fValue, ::com::sun::star::util::Color aDefaultColor ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL formatString( sal_Int32 nKey, const ::rtl::OUString& aString ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::util::Color SAL_CALL queryColorForString( sal_Int32 nKey, + const ::rtl::OUString& aString, + ::com::sun::star::util::Color aDefaultColor ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getInputString( sal_Int32 nKey, double fValue ) + throw(::com::sun::star::uno::RuntimeException); + + // XNumberFormatPreviewer + virtual ::rtl::OUString SAL_CALL convertNumberToPreviewString( + const ::rtl::OUString& aFormat, double fValue, + const ::com::sun::star::lang::Locale& nLocale, sal_Bool bAllowEnglish ) + throw(::com::sun::star::util::MalformedNumberFormatException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::util::Color SAL_CALL queryPreviewColorForNumber( + const ::rtl::OUString& aFormat, double fValue, + const ::com::sun::star::lang::Locale& nLocale, sal_Bool bAllowEnglish, + ::com::sun::star::util::Color aDefaultColor ) + throw(::com::sun::star::util::MalformedNumberFormatException, + ::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() + throw(::com::sun::star::uno::RuntimeException); +}; + + +class SvNumberFormatsObj : public cppu::WeakImplHelper3< + com::sun::star::util::XNumberFormats, + com::sun::star::util::XNumberFormatTypes, + com::sun::star::lang::XServiceInfo> +{ +private: + SvNumberFormatsSupplierObj& rSupplier; + mutable ::comphelper::SharedMutex m_aMutex; + +public: + SvNumberFormatsObj(SvNumberFormatsSupplierObj& pParent, ::comphelper::SharedMutex& _rMutex); + virtual ~SvNumberFormatsObj(); + + + // XNumberFormats + virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > SAL_CALL + getByKey( sal_Int32 nKey ) throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< sal_Int32 > SAL_CALL queryKeys( sal_Int16 nType, + const ::com::sun::star::lang::Locale& nLocale, sal_Bool bCreate ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL queryKey( const ::rtl::OUString& aFormat, + const ::com::sun::star::lang::Locale& nLocale, sal_Bool bScan ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL addNew( const ::rtl::OUString& aFormat, + const ::com::sun::star::lang::Locale& nLocale ) + throw(::com::sun::star::util::MalformedNumberFormatException, + ::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL addNewConverted( const ::rtl::OUString& aFormat, + const ::com::sun::star::lang::Locale& nLocale, + const ::com::sun::star::lang::Locale& nNewLocale ) + throw(::com::sun::star::util::MalformedNumberFormatException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeByKey( sal_Int32 nKey ) throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL generateFormat( sal_Int32 nBaseKey, + const ::com::sun::star::lang::Locale& nLocale, sal_Bool bThousands, + sal_Bool bRed, sal_Int16 nDecimals, sal_Int16 nLeading ) + throw(::com::sun::star::uno::RuntimeException); + + // XNumberFormatTypes + virtual sal_Int32 SAL_CALL getStandardIndex( const ::com::sun::star::lang::Locale& nLocale ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL getStandardFormat( sal_Int16 nType, + const ::com::sun::star::lang::Locale& nLocale ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL getFormatIndex( sal_Int16 nIndex, + const ::com::sun::star::lang::Locale& nLocale ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL isTypeCompatible( sal_Int16 nOldType, sal_Int16 nNewType ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL getFormatForLocale( sal_Int32 nKey, + const ::com::sun::star::lang::Locale& nLocale ) + throw(::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() + throw(::com::sun::star::uno::RuntimeException); + +private: + SvNumberFormatsObj(); // never implemented +}; + + +class SvNumberFormatObj : public cppu::WeakImplHelper3< + com::sun::star::beans::XPropertySet, + com::sun::star::beans::XPropertyAccess, + com::sun::star::lang::XServiceInfo> +{ +private: + SvNumberFormatsSupplierObj& rSupplier; + ULONG nKey; + mutable ::comphelper::SharedMutex m_aMutex; + +public: + SvNumberFormatObj( SvNumberFormatsSupplierObj& rParent, ULONG nK, const ::comphelper::SharedMutex& _rMutex ); + virtual ~SvNumberFormatObj(); + + // XPropertySet + virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo > + SAL_CALL getPropertySetInfo( ) + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setPropertyValue( const ::rtl::OUString& aPropertyName, + const ::com::sun::star::uno::Any& aValue ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::beans::PropertyVetoException, + ::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Any SAL_CALL getPropertyValue( + const ::rtl::OUString& PropertyName ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addPropertyChangeListener( const ::rtl::OUString& aPropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertyChangeListener >& xListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removePropertyChangeListener( const ::rtl::OUString& aPropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertyChangeListener >& aListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addVetoableChangeListener( const ::rtl::OUString& PropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XVetoableChangeListener >& aListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeVetoableChangeListener( const ::rtl::OUString& PropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XVetoableChangeListener >& aListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + + // XPropertyAccess + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > SAL_CALL + getPropertyValues() throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setPropertyValues( const ::com::sun::star::uno::Sequence< + ::com::sun::star::beans::PropertyValue >& aProps ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::beans::PropertyVetoException, + ::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() + throw(::com::sun::star::uno::RuntimeException); +}; + + +class SvNumberFormatSettingsObj : public cppu::WeakImplHelper2< + com::sun::star::beans::XPropertySet, + com::sun::star::lang::XServiceInfo> +{ +private: + SvNumberFormatsSupplierObj& rSupplier; + mutable ::comphelper::SharedMutex m_aMutex; + +public: + SvNumberFormatSettingsObj( SvNumberFormatsSupplierObj& rParent, const ::comphelper::SharedMutex& _rMutex); + virtual ~SvNumberFormatSettingsObj(); + + + // XPropertySet + virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo > + SAL_CALL getPropertySetInfo( ) + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setPropertyValue( const ::rtl::OUString& aPropertyName, + const ::com::sun::star::uno::Any& aValue ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::beans::PropertyVetoException, + ::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Any SAL_CALL getPropertyValue( + const ::rtl::OUString& PropertyName ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addPropertyChangeListener( const ::rtl::OUString& aPropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertyChangeListener >& xListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removePropertyChangeListener( const ::rtl::OUString& aPropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertyChangeListener >& aListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addVetoableChangeListener( const ::rtl::OUString& PropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XVetoableChangeListener >& aListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeVetoableChangeListener( const ::rtl::OUString& PropertyName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XVetoableChangeListener >& aListener ) + throw(::com::sun::star::beans::UnknownPropertyException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() + throw(::com::sun::star::uno::RuntimeException); +}; + + + +#endif + diff --git a/svl/source/numbers/numhead.cxx b/svl/source/numbers/numhead.cxx new file mode 100644 index 000000000000..99ff33433de3 --- /dev/null +++ b/svl/source/numbers/numhead.cxx @@ -0,0 +1,252 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: numhead.cxx,v $ + * $Revision: 1.9 $ + * + * 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_svl.hxx" +#ifndef GCC +#endif +#include <tools/debug.hxx> + +#include "numhead.hxx" + +// ID's fuer Dateien: +#define SV_NUMID_SIZES 0x4200 + +// STATIC DATA ----------------------------------------------------------- + +//SEG_EOFGLOBALS() + +// ======================================================================= +/* wird fuer SvNumberformatter nicht gebraucht +//#pragma SEG_FUNCDEF(numhead_01) + +SvNumReadHeader::SvNumReadHeader(SvStream& rNewStream) : + rStream( rNewStream ) +{ + ULONG nDataSize; + rStream >> nDataSize; + nDataEnd = rStream.Tell() + nDataSize; +} + +//#pragma SEG_FUNCDEF(numhead_02) + +SvNumReadHeader::~SvNumReadHeader() +{ + ULONG nReadEnd = rStream.Tell(); + DBG_ASSERT( nReadEnd <= nDataEnd, "zuviele Bytes gelesen" ); + if ( nReadEnd != nDataEnd ) + rStream.Seek(nDataEnd); // Rest ueberspringen +} + +//#pragma SEG_FUNCDEF(numhead_03) + +ULONG SvNumReadHeader::BytesLeft() const +{ + ULONG nReadEnd = rStream.Tell(); + if (nReadEnd <= nDataEnd) + return nDataEnd-nReadEnd; + + DBG_ERROR("Fehler bei SvNumReadHeader::BytesLeft"); + return 0; +} + +// ----------------------------------------------------------------------- + +//#pragma SEG_FUNCDEF(numhead_04) + +SvNumWriteHeader::SvNumWriteHeader(SvStream& rNewStream, ULONG nDefault) : + rStream( rNewStream ) +{ + nDataSize = nDefault; + rStream << nDataSize; + nDataPos = rStream.Tell(); +} + +//#pragma SEG_FUNCDEF(numhead_05) + +SvNumWriteHeader::~SvNumWriteHeader() +{ + ULONG nPos = rStream.Tell(); + + if ( nPos - nDataPos != nDataSize ) // Default getroffen? + { + nDataSize = nPos - nDataPos; + rStream.Seek(nDataPos - sizeof(sal_uInt32)); + rStream << nDataSize; // Groesse am Anfang eintragen + rStream.Seek(nPos); + } +} +*/ + +// ======================================================================= + +//#pragma SEG_FUNCDEF(numhead_06) + +//! mit Skip() synchron +ImpSvNumMultipleReadHeader::ImpSvNumMultipleReadHeader(SvStream& rNewStream) : + rStream( rNewStream ) +{ + sal_uInt32 nDataSize; + rStream >> nDataSize; + ULONG nDataPos = rStream.Tell(); + nEntryEnd = nDataPos; + + rStream.SeekRel(nDataSize); + USHORT nID; + rStream >> nID; + if (nID != SV_NUMID_SIZES) + { + DBG_ERROR("SV_NUMID_SIZES nicht gefunden"); + } + sal_uInt32 nSizeTableLen; + rStream >> nSizeTableLen; + pBuf = new char[nSizeTableLen]; + rStream.Read( pBuf, nSizeTableLen ); + pMemStream = new SvMemoryStream( pBuf, nSizeTableLen, STREAM_READ ); + + nEndPos = rStream.Tell(); + rStream.Seek( nDataPos ); +} + +//#pragma SEG_FUNCDEF(numhead_07) + +ImpSvNumMultipleReadHeader::~ImpSvNumMultipleReadHeader() +{ + DBG_ASSERT( pMemStream->Tell() == pMemStream->GetSize(), + "Sizes nicht vollstaendig gelesen" ); + delete pMemStream; + delete [] pBuf; + + rStream.Seek(nEndPos); +} + +//! mit ctor synchron +// static +void ImpSvNumMultipleReadHeader::Skip( SvStream& rStream ) +{ + sal_uInt32 nDataSize; + rStream >> nDataSize; + rStream.SeekRel( nDataSize ); + USHORT nID; + rStream >> nID; + if ( nID != SV_NUMID_SIZES ) + { + DBG_ERROR("SV_NUMID_SIZES nicht gefunden"); + } + sal_uInt32 nSizeTableLen; + rStream >> nSizeTableLen; + rStream.SeekRel( nSizeTableLen ); +} + +//#pragma SEG_FUNCDEF(numhead_08) + +void ImpSvNumMultipleReadHeader::EndEntry() +{ + ULONG nPos = rStream.Tell(); + DBG_ASSERT( nPos <= nEntryEnd, "zuviel gelesen" ); + if ( nPos != nEntryEnd ) + rStream.Seek( nEntryEnd ); // Rest ueberspringen +} + +//#pragma SEG_FUNCDEF(numhead_0d) + +void ImpSvNumMultipleReadHeader::StartEntry() +{ + ULONG nPos = rStream.Tell(); + sal_uInt32 nEntrySize; + (*pMemStream) >> nEntrySize; + + nEntryEnd = nPos + nEntrySize; +} + +//#pragma SEG_FUNCDEF(numhead_09) + +ULONG ImpSvNumMultipleReadHeader::BytesLeft() const +{ + ULONG nReadEnd = rStream.Tell(); + if (nReadEnd <= nEntryEnd) + return nEntryEnd-nReadEnd; + + DBG_ERROR("Fehler bei ImpSvNumMultipleReadHeader::BytesLeft"); + return 0; +} + +// ----------------------------------------------------------------------- + +//#pragma SEG_FUNCDEF(numhead_0a) + +ImpSvNumMultipleWriteHeader::ImpSvNumMultipleWriteHeader(SvStream& rNewStream, + ULONG nDefault) : + rStream( rNewStream ), + aMemStream( 4096, 4096 ) +{ + nDataSize = nDefault; + rStream << nDataSize; + + nDataPos = rStream.Tell(); + nEntryStart = nDataPos; +} + +//#pragma SEG_FUNCDEF(numhead_0b) + +ImpSvNumMultipleWriteHeader::~ImpSvNumMultipleWriteHeader() +{ + ULONG nDataEnd = rStream.Tell(); + + rStream << (USHORT) SV_NUMID_SIZES; + rStream << static_cast<sal_uInt32>(aMemStream.Tell()); + rStream.Write( aMemStream.GetData(), aMemStream.Tell() ); + + if ( nDataEnd - nDataPos != nDataSize ) // Default getroffen? + { + nDataSize = nDataEnd - nDataPos; + ULONG nPos = rStream.Tell(); + rStream.Seek(nDataPos-sizeof(sal_uInt32)); + rStream << nDataSize; // Groesse am Anfang eintragen + rStream.Seek(nPos); + } +} + +//#pragma SEG_FUNCDEF(numhead_0c) + +void ImpSvNumMultipleWriteHeader::EndEntry() +{ + ULONG nPos = rStream.Tell(); + aMemStream << static_cast<sal_uInt32>(nPos - nEntryStart); +} + +//#pragma SEG_FUNCDEF(numhead_0e) + +void ImpSvNumMultipleWriteHeader::StartEntry() +{ + ULONG nPos = rStream.Tell(); + nEntryStart = nPos; +} + diff --git a/svl/source/numbers/numhead.hxx b/svl/source/numbers/numhead.hxx new file mode 100644 index 000000000000..de23b3cbccf4 --- /dev/null +++ b/svl/source/numbers/numhead.hxx @@ -0,0 +1,109 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: numhead.hxx,v $ + * $Revision: 1.4 $ + * + * 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. + * + ************************************************************************/ + +#ifndef NF_NUMHEAD_HXX +#define NF_NUMHEAD_HXX + +#include <tools/stream.hxx> + +// ----------------------------------------------------------------------- + + // "Automatischer" Record-Header mit Groessenangabe + +/* wird fuer SvNumberFormatter nicht gebraucht +class SvNumReadHeader +{ +private: + SvStream& rStream; + ULONG nDataEnd; + +public: + SvNumReadHeader(SvStream& rNewStream); + ~SvNumReadHeader(); + + ULONG BytesLeft() const; +}; + +class SvNumWriteHeader +{ +private: + SvStream& rStream; + ULONG nDataPos; + ULONG nDataSize; + +public: + SvNumWriteHeader(SvStream& rNewStream, ULONG nDefault = 0); + ~SvNumWriteHeader(); +}; + +*/ + + // Header mit Groessenangaben fuer mehrere Objekte + +class ImpSvNumMultipleReadHeader +{ +private: + SvStream& rStream; + char* pBuf; + SvMemoryStream* pMemStream; + ULONG nEndPos; + ULONG nEntryEnd; + +public: + ImpSvNumMultipleReadHeader(SvStream& rNewStream); + ~ImpSvNumMultipleReadHeader(); + + void StartEntry(); + void EndEntry(); + ULONG BytesLeft() const; + + static void Skip( SvStream& ); // komplett ueberspringen +}; + +class ImpSvNumMultipleWriteHeader +{ +private: + SvStream& rStream; + SvMemoryStream aMemStream; + ULONG nDataPos; + sal_uInt32 nDataSize; + ULONG nEntryStart; + +public: + ImpSvNumMultipleWriteHeader(SvStream& rNewStream, ULONG nDefault = 0); + ~ImpSvNumMultipleWriteHeader(); + + void StartEntry(); + void EndEntry(); +}; + +#endif + + diff --git a/svl/source/numbers/numuno.cxx b/svl/source/numbers/numuno.cxx new file mode 100644 index 000000000000..3cc90998e2dc --- /dev/null +++ b/svl/source/numbers/numuno.cxx @@ -0,0 +1,170 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: numuno.cxx,v $ + * $Revision: 1.9 $ + * + * 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_svl.hxx" +#ifndef GCC +#endif + +#define _ZFORLIST_DECLARE_TABLE + +#include <tools/color.hxx> +#include <tools/debug.hxx> +#include <vos/mutex.hxx> +#include <osl/mutex.hxx> +#include <rtl/uuid.h> + +#include "numuno.hxx" +#include "numfmuno.hxx" +#include <svl/zforlist.hxx> + +using namespace com::sun::star; + +//------------------------------------------------------------------------ + +class SvNumFmtSuppl_Impl +{ +public: + SvNumberFormatter* pFormatter; + mutable ::comphelper::SharedMutex aMutex; + + SvNumFmtSuppl_Impl(SvNumberFormatter* p) : + pFormatter(p) {} +}; + +//------------------------------------------------------------------------ + +// Default-ctor fuer getReflection +SvNumberFormatsSupplierObj::SvNumberFormatsSupplierObj() +{ + pImpl = new SvNumFmtSuppl_Impl(NULL); +} + +SvNumberFormatsSupplierObj::SvNumberFormatsSupplierObj(SvNumberFormatter* pForm) +{ + pImpl = new SvNumFmtSuppl_Impl(pForm); +} + +SvNumberFormatsSupplierObj::~SvNumberFormatsSupplierObj() +{ + delete pImpl; +} + +::comphelper::SharedMutex& SvNumberFormatsSupplierObj::getSharedMutex() const +{ + return pImpl->aMutex; +} + +SvNumberFormatter* SvNumberFormatsSupplierObj::GetNumberFormatter() const +{ + return pImpl->pFormatter; +} + +void SvNumberFormatsSupplierObj::SetNumberFormatter(SvNumberFormatter* pNew) +{ + // der alte Numberformatter ist ungueltig geworden, nicht mehr darauf zugreifen! + pImpl->pFormatter = pNew; +} + +void SvNumberFormatsSupplierObj::NumberFormatDeleted(sal_uInt32) +{ + // Basis-Implementierung tut nix... +} + +void SvNumberFormatsSupplierObj::SettingsChanged() +{ + // Basis-Implementierung tut nix... +} + +// XNumberFormatsSupplier + +uno::Reference<beans::XPropertySet> SAL_CALL SvNumberFormatsSupplierObj::getNumberFormatSettings() + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( pImpl->aMutex ); + + return new SvNumberFormatSettingsObj( *this, pImpl->aMutex ); +} + +uno::Reference<util::XNumberFormats> SAL_CALL SvNumberFormatsSupplierObj::getNumberFormats() + throw(uno::RuntimeException) +{ + ::osl::MutexGuard aGuard( pImpl->aMutex ); + + return new SvNumberFormatsObj( *this, pImpl->aMutex ); +} + +// XUnoTunnel + +sal_Int64 SAL_CALL SvNumberFormatsSupplierObj::getSomething( + const uno::Sequence<sal_Int8 >& rId ) throw(uno::RuntimeException) +{ + if ( rId.getLength() == 16 && + 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), + rId.getConstArray(), 16 ) ) + { + return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this)); + } + return 0; +} + +// static +const uno::Sequence<sal_Int8>& SvNumberFormatsSupplierObj::getUnoTunnelId() +{ + static uno::Sequence<sal_Int8> * pSeq = 0; + if( !pSeq ) + { + osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() ); + if( !pSeq ) + { + static uno::Sequence< sal_Int8 > aSeq( 16 ); + rtl_createUuid( (sal_uInt8*)aSeq.getArray(), 0, sal_True ); + pSeq = &aSeq; + } + } + return *pSeq; +} + +// static +SvNumberFormatsSupplierObj* SvNumberFormatsSupplierObj::getImplementation( + const uno::Reference<util::XNumberFormatsSupplier> xObj ) +{ + SvNumberFormatsSupplierObj* pRet = NULL; + uno::Reference<lang::XUnoTunnel> xUT( xObj, uno::UNO_QUERY ); + if (xUT.is()) + pRet = reinterpret_cast<SvNumberFormatsSupplierObj*>(sal::static_int_cast<sal_IntPtr>(xUT->getSomething( getUnoTunnelId() ))); + return pRet; +} + + +//------------------------------------------------------------------------ + + + diff --git a/svl/source/numbers/supservs.cxx b/svl/source/numbers/supservs.cxx new file mode 100644 index 000000000000..7e4d8560dae7 --- /dev/null +++ b/svl/source/numbers/supservs.cxx @@ -0,0 +1,232 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: supservs.cxx,v $ + * $Revision: 1.10 $ + * + * 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_svl.hxx" +#include "supservs.hxx" +#include <com/sun/star/lang/Locale.hpp> +#include <comphelper/sharedmutex.hxx> +#include <i18npool/mslangid.hxx> +#include <tools/debug.hxx> +#include <vos/mutex.hxx> +#include <tools/stream.hxx> +#include <strmadpt.hxx> +#include "instrm.hxx" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::vos; +using namespace ::utl; + +#define PERSISTENT_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.util.NumberFormatsSupplier"); + +//------------------------------------------------------------------------- +Reference< XInterface > SAL_CALL SvNumberFormatsSupplierServiceObject_CreateInstance(const Reference< XMultiServiceFactory >& _rxFactory) +{ + return static_cast< ::cppu::OWeakObject* >(new SvNumberFormatsSupplierServiceObject(_rxFactory)); +} + +//------------------------------------------------------------------------- +SvNumberFormatsSupplierServiceObject::SvNumberFormatsSupplierServiceObject(const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB) + :m_pOwnFormatter(NULL) + ,m_xORB(_rxORB) +{ +} + +//------------------------------------------------------------------------- +SvNumberFormatsSupplierServiceObject::~SvNumberFormatsSupplierServiceObject() +{ + if (m_pOwnFormatter) + { + delete m_pOwnFormatter; + m_pOwnFormatter = NULL; + } +} + +//------------------------------------------------------------------------- +Any SAL_CALL SvNumberFormatsSupplierServiceObject::queryAggregation( const Type& _rType ) throw (RuntimeException) +{ + Any aReturn = ::cppu::queryInterface(_rType, + static_cast< XInitialization* >(this), + static_cast< XPersistObject* >(this), + static_cast< XServiceInfo* >(this) + ); + + if (!aReturn.hasValue()) + aReturn = SvNumberFormatsSupplierObj::queryAggregation(_rType); + + return aReturn; +} + +//------------------------------------------------------------------------- +void SAL_CALL SvNumberFormatsSupplierServiceObject::initialize( const Sequence< Any >& _rArguments ) throw(Exception, RuntimeException) +{ + ::osl::MutexGuard aGuard( getSharedMutex() ); + + DBG_ASSERT(m_pOwnFormatter == NULL, + "SvNumberFormatsSupplierServiceObject::initialize : already initialized !"); + // maybe you already called a method which needed the formatter + // you should use XMultiServiceFactory::createInstanceWithArguments to avoid that + if (m_pOwnFormatter) + { // !!! this is only a emergency handling, normally this should not occur !!! + delete m_pOwnFormatter; + m_pOwnFormatter = NULL; + SetNumberFormatter(m_pOwnFormatter); + } + + Type aExpectedArgType = ::getCppuType(static_cast<Locale*>(NULL)); + LanguageType eNewFormatterLanguage = LANGUAGE_ENGLISH_US; + // the default + + const Any* pArgs = _rArguments.getConstArray(); + for (sal_Int32 i=0; i<_rArguments.getLength(); ++i, ++pArgs) + { + if (pArgs->getValueType().equals(aExpectedArgType)) + { + Locale aLocale; + *pArgs >>= aLocale; + eNewFormatterLanguage = MsLangId::convertLocaleToLanguage( aLocale); + } +#ifdef DBG_UTIL + else + { + DBG_ERROR("SvNumberFormatsSupplierServiceObject::initialize : unknown argument !"); + } +#endif + } + + m_pOwnFormatter = new SvNumberFormatter(m_xORB, eNewFormatterLanguage); + m_pOwnFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL ); + SetNumberFormatter(m_pOwnFormatter); +} + +//------------------------------------------------------------------------- +::rtl::OUString SAL_CALL SvNumberFormatsSupplierServiceObject::getImplementationName( ) throw(RuntimeException) +{ + return ::rtl::OUString::createFromAscii("com.sun.star.uno.util.numbers.SvNumberFormatsSupplierServiceObject"); +} + +//------------------------------------------------------------------------- +sal_Bool SAL_CALL SvNumberFormatsSupplierServiceObject::supportsService( const ::rtl::OUString& _rServiceName ) throw(RuntimeException) +{ + Sequence< ::rtl::OUString > aServices = getSupportedServiceNames(); + const ::rtl::OUString* pServices = aServices.getConstArray(); + for (sal_Int32 i=0; i<aServices.getLength(); ++i, ++pServices) + if (pServices->equals(_rServiceName)) + return sal_True; + + return sal_False; +} + +//------------------------------------------------------------------------- +Sequence< ::rtl::OUString > SAL_CALL SvNumberFormatsSupplierServiceObject::getSupportedServiceNames( ) throw(RuntimeException) +{ + Sequence< ::rtl::OUString > aSupported(1); + aSupported.getArray()[0] = PERSISTENT_SERVICE_NAME; + return aSupported; +} + +//------------------------------------------------------------------------- +::rtl::OUString SAL_CALL SvNumberFormatsSupplierServiceObject::getServiceName( ) throw(RuntimeException) +{ + return PERSISTENT_SERVICE_NAME; +} + +//------------------------------------------------------------------------- +void SAL_CALL SvNumberFormatsSupplierServiceObject::write( const Reference< XObjectOutputStream >& _rxOutStream ) throw(IOException, RuntimeException) +{ + ::osl::MutexGuard aGuard( getSharedMutex() ); + implEnsureFormatter(); + + Reference< XOutputStream > xStream(_rxOutStream.get()); + SvLockBytesRef aLockBytes = new SvOutputStreamOpenLockBytes(xStream); + SvStream aSvOutputSteam(aLockBytes); + + m_pOwnFormatter->Save(aSvOutputSteam); +} + +//------------------------------------------------------------------------- +void SAL_CALL SvNumberFormatsSupplierServiceObject::read( const Reference< XObjectInputStream >& _rxInStream ) throw(IOException, RuntimeException) +{ + ::osl::MutexGuard aGuard( getSharedMutex() ); + implEnsureFormatter(); + + Reference< XInputStream > xStream(_rxInStream.get()); + SvInputStream aSvInputSteam(xStream); + + m_pOwnFormatter->Load(aSvInputSteam); +} + +//------------------------------------------------------------------------- +Reference< XPropertySet > SAL_CALL SvNumberFormatsSupplierServiceObject::getNumberFormatSettings() throw(RuntimeException) +{ + ::osl::MutexGuard aGuard( getSharedMutex() ); + implEnsureFormatter(); + return SvNumberFormatsSupplierObj::getNumberFormatSettings(); +} + +//------------------------------------------------------------------------- +Reference< XNumberFormats > SAL_CALL SvNumberFormatsSupplierServiceObject::getNumberFormats() throw(RuntimeException) +{ + ::osl::MutexGuard aGuard( getSharedMutex() ); + implEnsureFormatter(); + return SvNumberFormatsSupplierObj::getNumberFormats(); +} + +//------------------------------------------------------------------------- +sal_Int64 SAL_CALL SvNumberFormatsSupplierServiceObject::getSomething( const Sequence< sal_Int8 >& aIdentifier ) throw (RuntimeException) +{ + sal_Int64 nReturn = SvNumberFormatsSupplierObj::getSomething( aIdentifier ); + if ( nReturn ) + // if somebody accesses internals then we should have the formatter + implEnsureFormatter(); + return nReturn; +} + +//------------------------------------------------------------------------- +void SvNumberFormatsSupplierServiceObject::implEnsureFormatter() +{ + if (!m_pOwnFormatter) + { + // get the office's UI locale + SvtSysLocale aSysLocale; + Locale aOfficeLocale = aSysLocale.GetLocaleData().getLocale(); + + // initi with this locale + Sequence< Any > aFakedInitProps( 1 ); + aFakedInitProps[0] <<= aOfficeLocale; + + initialize( aFakedInitProps ); + } +} + diff --git a/svl/source/numbers/supservs.hxx b/svl/source/numbers/supservs.hxx new file mode 100644 index 000000000000..7dbbfe27177d --- /dev/null +++ b/svl/source/numbers/supservs.hxx @@ -0,0 +1,105 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: supservs.hxx,v $ + * $Revision: 1.7 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _SVTOOLS_NUMBERS_SUPPLIERSERVICE_HXX_ +#define _SVTOOLS_NUMBERS_SUPPLIERSERVICE_HXX_ + +#include "numuno.hxx" +#include <svl/zforlist.hxx> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/io/XPersistObject.hpp> + +//========================================================================= +//= SvNumberFormatsSupplierServiceObject - a number formats supplier which +//= - can be instantiated as an service +//= - supports the ::com::sun::star::io::XPersistObject interface +//= - works with it's own SvNumberFormatter instance +//= - can be initialized (::com::sun::star::lang::XInitialization) +//= with a specific language (i.e. ::com::sun::star::lang::Locale) +//========================================================================= +class SvNumberFormatsSupplierServiceObject + :protected SvNumberFormatsSupplierObj + ,public ::com::sun::star::lang::XInitialization + ,public ::com::sun::star::io::XPersistObject + ,public ::com::sun::star::lang::XServiceInfo +{ // don't want the Set-/GetNumberFormatter to be accessable from outside + + friend ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > + SAL_CALL SvNumberFormatsSupplierServiceObject_CreateInstance( + const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >&); + +protected: + SvNumberFormatter* m_pOwnFormatter; + ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > + m_xORB; + +public: + SvNumberFormatsSupplierServiceObject(const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB); + ~SvNumberFormatsSupplierServiceObject(); + + // XInterface + virtual void SAL_CALL acquire() throw() { SvNumberFormatsSupplierObj::acquire(); } + virtual void SAL_CALL release() throw() { SvNumberFormatsSupplierObj::release(); } + virtual ::com::sun::star::uno::Any SAL_CALL queryInterface( const ::com::sun::star::uno::Type& _rType ) throw(::com::sun::star::uno::RuntimeException) + { return SvNumberFormatsSupplierObj::queryInterface(_rType); } + + // XAggregation + virtual ::com::sun::star::uno::Any SAL_CALL queryAggregation( const ::com::sun::star::uno::Type& _rType ) throw(::com::sun::star::uno::RuntimeException); + + // XInitialization + virtual void SAL_CALL initialize( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aArguments ) throw(::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw(::com::sun::star::uno::RuntimeException); + + // XPersistObject + virtual ::rtl::OUString SAL_CALL getServiceName( ) throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL write( const ::com::sun::star::uno::Reference< ::com::sun::star::io::XObjectOutputStream >& OutStream ) throw(::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL read( const ::com::sun::star::uno::Reference< ::com::sun::star::io::XObjectInputStream >& InStream ) throw(::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException); + + // XNumberFormatsSupplier + virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > SAL_CALL + getNumberFormatSettings() throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormats > SAL_CALL + getNumberFormats() throw(::com::sun::star::uno::RuntimeException); + + // XUnoTunnler + virtual sal_Int64 SAL_CALL getSomething( const ::com::sun::star::uno::Sequence< sal_Int8 >& aIdentifier ) throw (::com::sun::star::uno::RuntimeException); + +protected: + void implEnsureFormatter(); +}; + + +#endif // _SVTOOLS_NUMBERS_SUPPLIERSERVICE_HXX_ + diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx new file mode 100644 index 000000000000..372ae2b15abf --- /dev/null +++ b/svl/source/numbers/zforfind.cxx @@ -0,0 +1,2819 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zforfind.cxx,v $ + * $Revision: 1.51.96.1 $ + * + * 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_svl.hxx" + +#include <ctype.h> +#include <stdlib.h> +#include <float.h> +#include <errno.h> +#include <tools/date.hxx> +#include <tools/debug.hxx> +#include <rtl/math.hxx> +#include <unotools/charclass.hxx> +#include <unotools/calendarwrapper.hxx> +#include <unotools/localedatawrapper.hxx> +#include <com/sun/star/i18n/CalendarFieldIndex.hpp> +#include <unotools/digitgroupingiterator.hxx> + +#include <svl/zforlist.hxx> // NUMBERFORMAT_XXX +#include "zforscan.hxx" +#include <svl/zformat.hxx> + +#define _ZFORFIND_CXX +#include "zforfind.hxx" +#undef _ZFORFIND_CXX + + +#ifndef DBG_UTIL +#define NF_TEST_CALENDAR 0 +#else +#define NF_TEST_CALENDAR 0 +#endif +#if NF_TEST_CALENDAR +#include <comphelper/processfactory.hxx> +#include <com/sun/star/i18n/XExtendedCalendar.hpp> +#endif + + +const BYTE ImpSvNumberInputScan::nMatchedEndString = 0x01; +const BYTE ImpSvNumberInputScan::nMatchedMidString = 0x02; +const BYTE ImpSvNumberInputScan::nMatchedStartString = 0x04; +const BYTE ImpSvNumberInputScan::nMatchedVirgin = 0x08; +const BYTE ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10; + +/* It is not clear how we want timezones to be handled. Convert them to local + * time isn't wanted, as it isn't done in any other place and timezone + * information isn't stored anywhere. Ignoring them and pretending local time + * may be wrong too and might not be what the user expects. Keep the input as + * string so that no information is lost. + * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it + * would work, together with the nTimezonePos handling in GetTimeRef(). */ +#define NF_RECOGNIZE_ISO8601_TIMEZONES 0 + +//--------------------------------------------------------------------------- +// Konstruktor + +ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter* pFormatterP ) + : + pUpperMonthText( NULL ), + pUpperAbbrevMonthText( NULL ), + pUpperDayText( NULL ), + pUpperAbbrevDayText( NULL ) +{ + pFormatter = pFormatterP; + pNullDate = new Date(30,12,1899); + nYear2000 = SvNumberFormatter::GetYear2000Default(); + Reset(); + ChangeIntl(); +} + + +//--------------------------------------------------------------------------- +// Destruktor + +ImpSvNumberInputScan::~ImpSvNumberInputScan() +{ + Reset(); + delete pNullDate; + delete [] pUpperMonthText; + delete [] pUpperAbbrevMonthText; + delete [] pUpperDayText; + delete [] pUpperAbbrevDayText; +} + + +//--------------------------------------------------------------------------- +// Reset + +void ImpSvNumberInputScan::Reset() +{ +#if 0 +// ER 16.06.97 18:56 Vorbelegung erfolgt jetzt in NumberStringDivision, +// wozu immer alles loeschen wenn einiges wieder benutzt oder gar nicht +// gebraucht wird.. + for (USHORT i = 0; i < SV_MAX_ANZ_INPUT_STRINGS; i++) + { + sStrArray[i].Erase(); + nNums[i] = SV_MAX_ANZ_INPUT_STRINGS-1; + IsNum[i] = FALSE; + } +#endif + nMonth = 0; + nMonthPos = 0; + nTimePos = 0; + nSign = 0; + nESign = 0; + nDecPos = 0; + nNegCheck = 0; + nAnzStrings = 0; + nAnzNums = 0; + nThousand = 0; + eScannedType = NUMBERFORMAT_UNDEFINED; + nAmPm = 0; + nPosThousandString = 0; + nLogical = 0; + nStringScanNumFor = 0; + nStringScanSign = 0; + nMatchedAllStrings = nMatchedVirgin; + nMayBeIso8601 = 0; + nTimezonePos = 0; +} + + +//--------------------------------------------------------------------------- +// +// static +inline BOOL ImpSvNumberInputScan::MyIsdigit( sal_Unicode c ) +{ + // If the input string wouldn't be converted using TransformInput() we'd + // to use something similar to the following and to adapt many places. +#if 0 + // use faster isdigit() if possible + if ( c < 128 ) + return isdigit( (unsigned char) c ) != 0; + if ( c < 256 ) + return FALSE; + String aTmp( c ); + return pFormatter->GetCharClass()->isDigit( aTmp, 0 ); +#else + return c < 128 && isdigit( (unsigned char) c ); +#endif +} + + +//--------------------------------------------------------------------------- +// +void ImpSvNumberInputScan::TransformInput( String& rStr ) +{ + xub_StrLen nPos, nLen; + for ( nPos = 0, nLen = rStr.Len(); nPos < nLen; ++nPos ) + { + if ( 256 <= rStr.GetChar( nPos ) && + pFormatter->GetCharClass()->isDigit( rStr, nPos ) ) + break; + } + if ( nPos < nLen ) + rStr = pFormatter->GetNatNum()->getNativeNumberString( rStr, + pFormatter->GetLocale(), 0 ); +} + + +//--------------------------------------------------------------------------- +// StringToDouble +// +// Only simple unsigned floating point values without any error detection, +// decimal separator has to be '.' + +double ImpSvNumberInputScan::StringToDouble( const String& rStr, BOOL bForceFraction ) +{ + double fNum = 0.0; + double fFrac = 0.0; + int nExp = 0; + xub_StrLen nPos = 0; + xub_StrLen nLen = rStr.Len(); + BOOL bPreSep = !bForceFraction; + + while (nPos < nLen) + { + if (rStr.GetChar(nPos) == '.') + bPreSep = FALSE; + else if (bPreSep) + fNum = fNum * 10.0 + (double) (rStr.GetChar(nPos) - '0'); + else + { + fFrac = fFrac * 10.0 + (double) (rStr.GetChar(nPos) - '0'); + --nExp; + } + nPos++; + } + if ( fFrac ) + return fNum + ::rtl::math::pow10Exp( fFrac, nExp ); + return fNum; +} + + +//--------------------------------------------------------------------------- +// NextNumberStringSymbol +// +// Zerlegt die Eingabe in Zahlen und Strings fuer die weitere +// Verarbeitung (Turing-Maschine). +//--------------------------------------------------------------------------- +// Ausgangs Zustand = GetChar +//---------------+-------------------+-----------------------+--------------- +// Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand +//---------------+-------------------+-----------------------+--------------- +// GetChar | Ziffer | Symbol=Zeichen | GetValue +// | Sonst | Symbol=Zeichen | GetString +//---------------|-------------------+-----------------------+--------------- +// GetValue | Ziffer | Symbol=Symbol+Zeichen | GetValue +// | Sonst | Dec(CharPos) | Stop +//---------------+-------------------+-----------------------+--------------- +// GetString | Ziffer | Dec(CharPos) | Stop +// | Sonst | Symbol=Symbol+Zeichen | GetString +//---------------+-------------------+-----------------------+--------------- + +enum ScanState // States der Turing-Maschine +{ + SsStop = 0, + SsStart = 1, + SsGetValue = 2, + SsGetString = 3 +}; + +BOOL ImpSvNumberInputScan::NextNumberStringSymbol( + const sal_Unicode*& pStr, + String& rSymbol ) +{ + BOOL isNumber = FALSE; + sal_Unicode cToken; + ScanState eState = SsStart; + register const sal_Unicode* pHere = pStr; + register xub_StrLen nChars = 0; + + while ( ((cToken = *pHere) != 0) && eState != SsStop) + { + pHere++; + switch (eState) + { + case SsStart: + if ( MyIsdigit( cToken ) ) + { + eState = SsGetValue; + isNumber = TRUE; + } + else + eState = SsGetString; + nChars++; + break; + case SsGetValue: + if ( MyIsdigit( cToken ) ) + nChars++; + else + { + eState = SsStop; + pHere--; + } + break; + case SsGetString: + if ( !MyIsdigit( cToken ) ) + nChars++; + else + { + eState = SsStop; + pHere--; + } + break; + default: + break; + } // switch + } // while + + if ( nChars ) + rSymbol.Assign( pStr, nChars ); + else + rSymbol.Erase(); + + pStr = pHere; + + return isNumber; +} + + +//--------------------------------------------------------------------------- +// SkipThousands + +// FIXME: should be grouping; it is only used though in case nAnzStrings is +// near SV_MAX_ANZ_INPUT_STRINGS, in NumberStringDivision(). + +BOOL ImpSvNumberInputScan::SkipThousands( + const sal_Unicode*& pStr, + String& rSymbol ) +{ + BOOL res = FALSE; + sal_Unicode cToken; + const String& rThSep = pFormatter->GetNumThousandSep(); + register const sal_Unicode* pHere = pStr; + ScanState eState = SsStart; + xub_StrLen nCounter = 0; // counts 3 digits + + while ( ((cToken = *pHere) != 0) && eState != SsStop) + { + pHere++; + switch (eState) + { + case SsStart: + if ( StringPtrContains( rThSep, pHere-1, 0 ) ) + { + nCounter = 0; + eState = SsGetValue; + pHere += rThSep.Len()-1; + } + else + { + eState = SsStop; + pHere--; + } + break; + case SsGetValue: + if ( MyIsdigit( cToken ) ) + { + rSymbol += cToken; + nCounter++; + if (nCounter == 3) + { + eState = SsStart; + res = TRUE; // .000 combination found + } + } + else + { + eState = SsStop; + pHere--; + } + break; + default: + break; + } // switch + } // while + + if (eState == SsGetValue) // break witth less than 3 digits + { + if ( nCounter ) + rSymbol.Erase( rSymbol.Len() - nCounter, nCounter ); + pHere -= nCounter + rThSep.Len(); // put back ThSep also + } + pStr = pHere; + + return res; +} + + +//--------------------------------------------------------------------------- +// NumberStringDivision + +void ImpSvNumberInputScan::NumberStringDivision( const String& rString ) +{ + const sal_Unicode* pStr = rString.GetBuffer(); + const sal_Unicode* const pEnd = pStr + rString.Len(); + while ( pStr < pEnd && nAnzStrings < SV_MAX_ANZ_INPUT_STRINGS ) + { + if ( NextNumberStringSymbol( pStr, sStrArray[nAnzStrings] ) ) + { // Zahl + IsNum[nAnzStrings] = TRUE; + nNums[nAnzNums] = nAnzStrings; + nAnzNums++; + if (nAnzStrings >= SV_MAX_ANZ_INPUT_STRINGS - 7 && + nPosThousandString == 0) // nur einmal + if ( SkipThousands( pStr, sStrArray[nAnzStrings] ) ) + nPosThousandString = nAnzStrings; + } + else + { + IsNum[nAnzStrings] = FALSE; + } + nAnzStrings++; + } +} + + +//--------------------------------------------------------------------------- +// Whether rString contains rWhat at nPos + +BOOL ImpSvNumberInputScan::StringContainsImpl( const String& rWhat, + const String& rString, xub_StrLen nPos ) +{ + if ( nPos + rWhat.Len() <= rString.Len() ) + return StringPtrContainsImpl( rWhat, rString.GetBuffer(), nPos ); + return FALSE; +} + + +//--------------------------------------------------------------------------- +// Whether pString contains rWhat at nPos + +BOOL ImpSvNumberInputScan::StringPtrContainsImpl( const String& rWhat, + const sal_Unicode* pString, xub_StrLen nPos ) +{ + if ( rWhat.Len() == 0 ) + return FALSE; + register const sal_Unicode* pWhat = rWhat.GetBuffer(); + register const sal_Unicode* const pEnd = pWhat + rWhat.Len(); + register const sal_Unicode* pStr = pString + nPos; + while ( pWhat < pEnd ) + { + if ( *pWhat != *pStr ) + return FALSE; + pWhat++; + pStr++; + } + return TRUE; +} + + +//--------------------------------------------------------------------------- +// SkipChar +// +// ueberspringt genau das angegebene Zeichen + +inline BOOL ImpSvNumberInputScan::SkipChar( sal_Unicode c, const String& rString, + xub_StrLen& nPos ) +{ + if ((nPos < rString.Len()) && (rString.GetChar(nPos) == c)) + { + nPos++; + return TRUE; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// SkipBlanks +// +// Ueberspringt Leerzeichen + +inline void ImpSvNumberInputScan::SkipBlanks( const String& rString, + xub_StrLen& nPos ) +{ + if ( nPos < rString.Len() ) + { + register const sal_Unicode* p = rString.GetBuffer() + nPos; + while ( *p == ' ' ) + { + nPos++; + p++; + } + } +} + + +//--------------------------------------------------------------------------- +// SkipString +// +// jump over rWhat in rString at nPos + +inline BOOL ImpSvNumberInputScan::SkipString( const String& rWhat, + const String& rString, xub_StrLen& nPos ) +{ + if ( StringContains( rWhat, rString, nPos ) ) + { + nPos = nPos + rWhat.Len(); + return TRUE; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// GetThousandSep +// +// recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping + +inline BOOL ImpSvNumberInputScan::GetThousandSep( + const String& rString, + xub_StrLen& nPos, + USHORT nStringPos ) +{ + const String& rSep = pFormatter->GetNumThousandSep(); + // Is it an ordinary space instead of a non-breaking space? + bool bSpaceBreak = rSep.GetChar(0) == 0xa0 && rString.GetChar(0) == 0x20 && + rSep.Len() == 1 && rString.Len() == 1; + if (!( (rString == rSep || bSpaceBreak) // nothing else + && nStringPos < nAnzStrings - 1 // safety first! + && IsNum[nStringPos+1] )) // number follows + return FALSE; // no? => out + + utl::DigitGroupingIterator aGrouping( + pFormatter->GetLocaleData()->getDigitGrouping()); + // Match ,### in {3} or ,## in {3,2} + /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or + * ,##,### and to match ,### in {3,2} only if it's the last. However, + * currently there is no track kept where group separators occur. In {3,2} + * #,###,### and #,##,## would be valid input, which maybe isn't even bad + * for #,###,###. Other combinations such as #,###,## maybe not. */ + xub_StrLen nLen = sStrArray[nStringPos+1].Len(); + if (nLen == aGrouping.get() // with 3 (or so) digits + || nLen == aGrouping.advance().get() // or with 2 (or 3 or so) digits + || nPosThousandString == nStringPos+1 // or concatenated + ) + { + nPos = nPos + rSep.Len(); + return TRUE; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// GetLogical +// +// Conversion of text to logial value +// "TRUE" => 1: +// "FALSE"=> -1: +// else => 0: + +short ImpSvNumberInputScan::GetLogical( const String& rString ) +{ + short res; + + const ImpSvNumberformatScan* pFS = pFormatter->GetFormatScanner(); + if ( rString == pFS->GetTrueString() ) + res = 1; + else if ( rString == pFS->GetFalseString() ) + res = -1; + else + res = 0; + + return res; +} + + +//--------------------------------------------------------------------------- +// GetMonth +// +// Converts a string containing a month name (JAN, January) at nPos into the +// month number (negative if abbreviated), returns 0 if nothing found + +short ImpSvNumberInputScan::GetMonth( const String& rString, xub_StrLen& nPos ) +{ + // #102136# The correct English form of month September abbreviated is + // SEPT, but almost every data contains SEP instead. + static const String aSeptCorrect( RTL_CONSTASCII_USTRINGPARAM( "SEPT" ) ); + static const String aSepShortened( RTL_CONSTASCII_USTRINGPARAM( "SEP" ) ); + + short res = 0; // no month found + + if (rString.Len() > nPos) // only if needed + { + if ( !bTextInitialized ) + InitText(); + sal_Int16 nMonths = pFormatter->GetCalendar()->getNumberOfMonthsInYear(); + for ( sal_Int16 i = 0; i < nMonths; i++ ) + { + if ( StringContains( pUpperMonthText[i], rString, nPos ) ) + { // full names first + nPos = nPos + pUpperMonthText[i].Len(); + res = i+1; + break; // for + } + else if ( StringContains( pUpperAbbrevMonthText[i], rString, nPos ) ) + { // abbreviated + nPos = nPos + pUpperAbbrevMonthText[i].Len(); + res = sal::static_int_cast< short >(-(i+1)); // negative + break; // for + } + else if ( i == 8 && pUpperAbbrevMonthText[i] == aSeptCorrect && + StringContains( aSepShortened, rString, nPos ) ) + { // #102136# SEPT/SEP + nPos = nPos + aSepShortened.Len(); + res = sal::static_int_cast< short >(-(i+1)); // negative + break; // for + } + } + } + + return res; +} + + +//--------------------------------------------------------------------------- +// GetDayOfWeek +// +// Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the +// DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found + +int ImpSvNumberInputScan::GetDayOfWeek( const String& rString, xub_StrLen& nPos ) +{ + int res = 0; // no day found + + if (rString.Len() > nPos) // only if needed + { + if ( !bTextInitialized ) + InitText(); + sal_Int16 nDays = pFormatter->GetCalendar()->getNumberOfDaysInWeek(); + for ( sal_Int16 i = 0; i < nDays; i++ ) + { + if ( StringContains( pUpperDayText[i], rString, nPos ) ) + { // full names first + nPos = nPos + pUpperDayText[i].Len(); + res = i + 1; + break; // for + } + if ( StringContains( pUpperAbbrevDayText[i], rString, nPos ) ) + { // abbreviated + nPos = nPos + pUpperAbbrevDayText[i].Len(); + res = -(i + 1); // negative + break; // for + } + } + } + + return res; +} + + +//--------------------------------------------------------------------------- +// GetCurrency +// +// Lesen eines Waehrungssysmbols +// '$' => TRUE +// sonst => FALSE + +BOOL ImpSvNumberInputScan::GetCurrency( const String& rString, xub_StrLen& nPos, + const SvNumberformat* pFormat ) +{ + if ( rString.Len() > nPos ) + { + if ( !aUpperCurrSymbol.Len() ) + { // if no format specified the currency of the initialized formatter + LanguageType eLang = (pFormat ? pFormat->GetLanguage() : + pFormatter->GetLanguage()); + aUpperCurrSymbol = pFormatter->GetCharClass()->upper( + SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() ); + } + if ( StringContains( aUpperCurrSymbol, rString, nPos ) ) + { + nPos = nPos + aUpperCurrSymbol.Len(); + return TRUE; + } + if ( pFormat ) + { + String aSymbol, aExtension; + if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) ) + { + if ( aSymbol.Len() <= rString.Len() - nPos ) + { + pFormatter->GetCharClass()->toUpper( aSymbol ); + if ( StringContains( aSymbol, rString, nPos ) ) + { + nPos = nPos + aSymbol.Len(); + return TRUE; + } + } + } + } + } + + return FALSE; +} + + +//--------------------------------------------------------------------------- +// GetTimeAmPm +// +// Lesen des Zeitsymbols (AM od. PM) f. kurze Zeitangabe +// +// Rueckgabe: +// "AM" od. "PM" => TRUE +// sonst => FALSE +// +// nAmPos: +// "AM" => 1 +// "PM" => -1 +// sonst => 0 + +BOOL ImpSvNumberInputScan::GetTimeAmPm( const String& rString, xub_StrLen& nPos ) +{ + + if ( rString.Len() > nPos ) + { + const CharClass* pChr = pFormatter->GetCharClass(); + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + if ( StringContains( pChr->upper( pLoc->getTimeAM() ), rString, nPos ) ) + { + nAmPm = 1; + nPos = nPos + pLoc->getTimeAM().Len(); + return TRUE; + } + else if ( StringContains( pChr->upper( pLoc->getTimePM() ), rString, nPos ) ) + { + nAmPm = -1; + nPos = nPos + pLoc->getTimePM().Len(); + return TRUE; + } + } + + return FALSE; +} + + +//--------------------------------------------------------------------------- +// GetDecSep +// +// Lesen eines Dezimaltrenners (',') +// ',' => TRUE +// sonst => FALSE + +inline BOOL ImpSvNumberInputScan::GetDecSep( const String& rString, xub_StrLen& nPos ) +{ + if ( rString.Len() > nPos ) + { + const String& rSep = pFormatter->GetNumDecimalSep(); + if ( rString.Equals( rSep, nPos, rSep.Len() ) ) + { + nPos = nPos + rSep.Len(); + return TRUE; + } + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// read a hundredth seconds separator + +inline BOOL ImpSvNumberInputScan::GetTime100SecSep( const String& rString, xub_StrLen& nPos ) +{ + if ( rString.Len() > nPos ) + { + const String& rSep = pFormatter->GetLocaleData()->getTime100SecSep(); + if ( rString.Equals( rSep, nPos, rSep.Len() ) ) + { + nPos = nPos + rSep.Len(); + return TRUE; + } + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// GetSign +// +// Lesen eines Vorzeichens, auch Klammer !?! +// '+' => 1 +// '-' => -1 +// '(' => -1, nNegCheck = 1 +// sonst => 0 + +int ImpSvNumberInputScan::GetSign( const String& rString, xub_StrLen& nPos ) +{ + if (rString.Len() > nPos) + switch (rString.GetChar(nPos)) + { + case '+': + nPos++; + return 1; + case '(': // '(' aehnlich wie '-' ?!? + nNegCheck = 1; + //! fallthru + case '-': + nPos++; + return -1; + default: + break; + } + + return 0; +} + + +//--------------------------------------------------------------------------- +// GetESign +// +// Lesen eines Vorzeichens, gedacht fuer Exponent ?!? +// '+' => 1 +// '-' => -1 +// sonst => 0 + +short ImpSvNumberInputScan::GetESign( const String& rString, xub_StrLen& nPos ) +{ + if (rString.Len() > nPos) + switch (rString.GetChar(nPos)) + { + case '+': + nPos++; + return 1; + case '-': + nPos++; + return -1; + default: + break; + } + + return 0; +} + + +//--------------------------------------------------------------------------- +// GetNextNumber +// +// i counts string portions, j counts numbers thereof. +// It should had been called SkipNumber instead. + +inline BOOL ImpSvNumberInputScan::GetNextNumber( USHORT& i, USHORT& j ) +{ + if ( i < nAnzStrings && IsNum[i] ) + { + j++; + i++; + return TRUE; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// GetTimeRef + +void ImpSvNumberInputScan::GetTimeRef( + double& fOutNumber, + USHORT nIndex, // j-value of the first numeric time part of input, default 0 + USHORT nAnz ) // count of numeric time parts +{ + USHORT nHour; + USHORT nMinute = 0; + USHORT nSecond = 0; + double fSecond100 = 0.0; + USHORT nStartIndex = nIndex; + + if (nTimezonePos) + { + // find first timezone number index and adjust count + for (USHORT j=0; j<nAnzNums; ++j) + { + if (nNums[j] == nTimezonePos) + { + // nAnz is not total count, but count of time relevant strings. + if (nStartIndex < j && j - nStartIndex < nAnz) + nAnz = j - nStartIndex; + break; // for + } + } + } + + if (nDecPos == 2 && (nAnz == 3 || nAnz == 2)) // 20:45.5 or 45.5 + nHour = 0; + else if (nIndex - nStartIndex < nAnz) + nHour = (USHORT) sStrArray[nNums[nIndex++]].ToInt32(); + else + { + nHour = 0; + DBG_ERRORFILE( "ImpSvNumberInputScan::GetTimeRef: bad number index"); + } + if (nDecPos == 2 && nAnz == 2) // 45.5 + nMinute = 0; + else if (nIndex - nStartIndex < nAnz) + nMinute = (USHORT) sStrArray[nNums[nIndex++]].ToInt32(); + if (nIndex - nStartIndex < nAnz) + nSecond = (USHORT) sStrArray[nNums[nIndex++]].ToInt32(); + if (nIndex - nStartIndex < nAnz) + fSecond100 = StringToDouble( sStrArray[nNums[nIndex]], TRUE ); + if (nAmPm == -1 && nHour != 12) // PM + nHour += 12; + else if (nAmPm == 1 && nHour == 12) // 12 AM + nHour = 0; + + fOutNumber = ((double)nHour*3600 + + (double)nMinute*60 + + (double)nSecond + + fSecond100)/86400.0; +} + + +//--------------------------------------------------------------------------- +// ImplGetDay + +USHORT ImpSvNumberInputScan::ImplGetDay( USHORT nIndex ) +{ + USHORT nRes = 0; + + if (sStrArray[nNums[nIndex]].Len() <= 2) + { + USHORT nNum = (USHORT) sStrArray[nNums[nIndex]].ToInt32(); + if (nNum <= 31) + nRes = nNum; + } + + return nRes; +} + + +//--------------------------------------------------------------------------- +// ImplGetMonth + +USHORT ImpSvNumberInputScan::ImplGetMonth( USHORT nIndex ) +{ + // preset invalid month number + USHORT nRes = pFormatter->GetCalendar()->getNumberOfMonthsInYear(); + + if (sStrArray[nNums[nIndex]].Len() <= 2) + { + USHORT nNum = (USHORT) sStrArray[nNums[nIndex]].ToInt32(); + if ( 0 < nNum && nNum <= nRes ) + nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH + } + + return nRes; +} + + +//--------------------------------------------------------------------------- +// ImplGetYear +// +// 30 -> 1930, 29 -> 2029, oder 56 -> 1756, 55 -> 1855, ... + +USHORT ImpSvNumberInputScan::ImplGetYear( USHORT nIndex ) +{ + USHORT nYear = 0; + + if (sStrArray[nNums[nIndex]].Len() <= 4) + { + nYear = (USHORT) sStrArray[nNums[nIndex]].ToInt32(); + nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 ); + } + + return nYear; +} + +//--------------------------------------------------------------------------- + +bool ImpSvNumberInputScan::MayBeIso8601() +{ + if (nMayBeIso8601 == 0) + { + if (nAnzNums >= 3 && nNums[0] < nAnzStrings && + sStrArray[nNums[0]].ToInt32() > 31) + nMayBeIso8601 = 1; + else + nMayBeIso8601 = 2; + } + return nMayBeIso8601 == 1; +} + +//--------------------------------------------------------------------------- +// GetDateRef + +BOOL ImpSvNumberInputScan::GetDateRef( double& fDays, USHORT& nCounter, + const SvNumberformat* pFormat ) +{ + using namespace ::com::sun::star::i18n; + NfEvalDateFormat eEDF; + int nFormatOrder; + if ( pFormat && ((pFormat->GetType() & NUMBERFORMAT_DATE) == NUMBERFORMAT_DATE) ) + { + eEDF = pFormatter->GetEvalDateFormat(); + switch ( eEDF ) + { + case NF_EVALDATEFORMAT_INTL : + case NF_EVALDATEFORMAT_FORMAT : + nFormatOrder = 1; // only one loop + break; + default: + nFormatOrder = 2; + if ( nMatchedAllStrings ) + eEDF = NF_EVALDATEFORMAT_FORMAT_INTL; + // we have a complete match, use it + } + } + else + { + eEDF = NF_EVALDATEFORMAT_INTL; + nFormatOrder = 1; + } + BOOL res = TRUE; + + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + CalendarWrapper* pCal = pFormatter->GetCalendar(); + for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ ) + { + pCal->setGregorianDateTime( Date() ); // today + String aOrgCalendar; // empty => not changed yet + DateFormat DateFmt; + BOOL bFormatTurn; + switch ( eEDF ) + { + case NF_EVALDATEFORMAT_INTL : + bFormatTurn = FALSE; + DateFmt = pLoc->getDateFormat(); + break; + case NF_EVALDATEFORMAT_FORMAT : + bFormatTurn = TRUE; + DateFmt = pFormat->GetDateOrder(); + break; + case NF_EVALDATEFORMAT_INTL_FORMAT : + if ( nTryOrder == 1 ) + { + bFormatTurn = FALSE; + DateFmt = pLoc->getDateFormat(); + } + else + { + bFormatTurn = TRUE; + DateFmt = pFormat->GetDateOrder(); + } + break; + case NF_EVALDATEFORMAT_FORMAT_INTL : + if ( nTryOrder == 2 ) + { + bFormatTurn = FALSE; + DateFmt = pLoc->getDateFormat(); + } + else + { + bFormatTurn = TRUE; + DateFmt = pFormat->GetDateOrder(); + } + break; + default: + DBG_ERROR( "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" ); + DateFmt = YMD; + bFormatTurn = FALSE; + } + if ( bFormatTurn ) + { +#if 0 +/* TODO: +We are currently not able to fully support a switch to another calendar during +input for the following reasons: +1. We do have a problem if both (locale's default and format's) calendars + define the same YMD order and use the same date separator, there is no way + to distinguish between them if the input results in valid calendar input for + both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should + it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's + calendar be preferred? This could be confusing if a Calc cell was formatted + different to the locale's default and has no content yet, then the user has + no clue about the format or calendar being set. +2. In Calc cell edit mode a date is always displayed and edited using the + default edit format of the default calendar (normally being Gregorian). If + input was ambiguous due to issue #1 we'd need a mechanism to tell that a + date was edited and not newly entered. Not feasible. Otherwise we'd need a + mechanism to use a specific edit format with a specific calendar according + to the format set. +3. For some calendars like Japanese Gengou we'd need era input, which isn't + implemented at all. Though this is a rare and special case, forcing a + calendar dependent edit format as suggested in item #2 might require era + input, if it shouldn't result in a fallback to Gregorian calendar. +4. Last and least: the GetMonth() method currently only matches month names of + the default calendar. Alternating month names of the actual format's + calendar would have to be implemented. No problem. + +*/ + if ( pFormat->IsOtherCalendar( nStringScanNumFor ) ) + pFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + else + pFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime, + nStringScanNumFor ); +#endif + } + + res = TRUE; + nCounter = 0; + // For incomplete dates, always assume first day of month if not specified. + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); + + switch (nAnzNums) // count of numbers in string + { + case 0: // none + if (nMonthPos) // only month (Jan) + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + else + res = FALSE; + break; + + case 1: // only one number + nCounter = 1; + switch (nMonthPos) // where is the month + { + case 0: // not found => only day entered + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + break; + case 1: // month at the beginning (Jan 01) + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + switch (DateFmt) + { + case MDY: + case YMD: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + break; + case DMY: + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + break; + default: + res = FALSE; + break; + } + break; + case 3: // month at the end (10 Jan) + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + switch (DateFmt) + { + case DMY: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + break; + case YMD: + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + break; + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } // switch (nMonthPos) + break; + + case 2: // 2 numbers + nCounter = 2; + switch (nMonthPos) // where is the month + { + case 0: // not found + { + bool bHadExact; + sal_uInt32 nExactDateOrder = (bFormatTurn ? pFormat->GetExactDateOrder() : 0); + if ( 0xff < nExactDateOrder && nExactDateOrder <= 0xffff ) + { // formatted as date and exactly 2 parts + bHadExact = true; + switch ( (nExactDateOrder >> 8) & 0xff ) + { + case 'Y': + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + break; + case 'M': + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); + break; + case 'D': + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + break; + default: + bHadExact = false; + } + switch ( nExactDateOrder & 0xff ) + { + case 'Y': + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + break; + case 'M': + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); + break; + case 'D': + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + break; + default: + bHadExact = false; + } + } + else + bHadExact = false; + if ( !bHadExact || !pCal->isValid() ) + { + if ( !bHadExact && nExactDateOrder ) + pCal->setGregorianDateTime( Date() ); // reset today + switch (DateFmt) + { + case MDY: + // M D + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); + if ( !pCal->isValid() ) // 2nd try + { // M Y + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + } + break; + case DMY: + // D M + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); + if ( !pCal->isValid() ) // 2nd try + { // M Y + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + } + break; + case YMD: + // M D + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); + if ( !pCal->isValid() ) // 2nd try + { // Y M + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + } + break; + default: + res = FALSE; + break; + } + } + } + break; + case 1: // month at the beginning (Jan 01 01) + { + // The input is valid as MDY in almost any + // constellation, there is no date order (M)YD except if + // set in a format applied. + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + sal_uInt32 nExactDateOrder = (bFormatTurn ? pFormat->GetExactDateOrder() : 0); + if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D')) + { + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + } + else + { + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + } + } + break; + case 2: // month in the middle (10 Jan 94) + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + switch (DateFmt) + { + case MDY: // yes, "10-Jan-94" is valid + case DMY: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + break; + case YMD: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + break; + default: + res = FALSE; + break; + } + break; + default: // else, e.g. month at the end (94 10 Jan) + res = FALSE; + break; + } // switch (nMonthPos) + break; + + default: // more than two numbers (31.12.94 8:23) (31.12. 8:23) + switch (nMonthPos) // where is the month + { + case 0: // not found + { + nCounter = 3; + if ( nTimePos > 1 ) + { // find first time number index (should only be 3 or 2 anyway) + for ( USHORT j = 0; j < nAnzNums; j++ ) + { + if ( nNums[j] == nTimePos - 2 ) + { + nCounter = j; + break; // for + } + } + } + // ISO 8601 yyyy-mm-dd forced recognition + DateFormat eDF = (MayBeIso8601() ? YMD : DateFmt); + switch (eDF) + { + case MDY: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); + if ( nCounter > 2 ) + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) ); + break; + case DMY: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); + if ( nCounter > 2 ) + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) ); + break; + case YMD: + if ( nCounter > 2 ) + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) ); + pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + break; + default: + res = FALSE; + break; + } + } + break; + case 1: // month at the beginning (Jan 01 01 8:23) + nCounter = 2; + switch (DateFmt) + { + case MDY: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + break; + default: + res = FALSE; + break; + } + break; + case 2: // month in the middle (10 Jan 94 8:23) + nCounter = 2; + pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); + switch (DateFmt) + { + case DMY: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); + break; + case YMD: + pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); + pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); + break; + default: + res = FALSE; + break; + } + break; + default: // else, e.g. month at the end (94 10 Jan 8:23) + nCounter = 2; + res = FALSE; + break; + } // switch (nMonthPos) + break; + } // switch (nAnzNums) + + if ( res && pCal->isValid() ) + { + double fDiff = DateTime(*pNullDate) - pCal->getEpochStart(); + fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() ); + fDays -= fDiff; + nTryOrder = nFormatOrder; // break for + } + else + res = FALSE; + + if ( aOrgCalendar.Len() ) + pCal->loadCalendar( aOrgCalendar, pLoc->getLocale() ); // restore calendar + +#if NF_TEST_CALENDAR +{ + using namespace ::com::sun::star; + struct entry { const char* lan; const char* cou; const char* cal; }; + const entry cals[] = { + { "en", "US", "gregorian" }, + { "ar", "TN", "hijri" }, + { "he", "IL", "jewish" }, + { "ja", "JP", "gengou" }, + { "ko", "KR", "hanja_yoil" }, + { "th", "TH", "buddhist" }, + { "zh", "TW", "ROC" }, + {0,0,0} + }; + lang::Locale aLocale; + sal_Bool bValid; + sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond; + sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet; + sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis; + sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis; + uno::Reference< lang::XMultiServiceFactory > xSMgr = + ::comphelper::getProcessServiceFactory(); + uno::Reference< ::com::sun::star::i18n::XExtendedCalendar > xCal( + xSMgr->createInstance( ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.i18n.LocaleCalendar" ) ) ), + uno::UNO_QUERY ); + for ( const entry* p = cals; p->lan; ++p ) + { + aLocale.Language = ::rtl::OUString::createFromAscii( p->lan ); + aLocale.Country = ::rtl::OUString::createFromAscii( p->cou ); + xCal->loadCalendar( ::rtl::OUString::createFromAscii( p->cal ), + aLocale ); + double nDateTime = 0.0; // 1-Jan-1970 00:00:00 + nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET ); + nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS ); + nZoneInMillis = static_cast<sal_Int32>(nZO) * 60000 + + (nZO < 0 ? -1 : 1) * static_cast<sal_uInt16>(nZOmillis); + nDST1 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET ); + nDST1millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS ); + nDST1InMillis = static_cast<sal_Int32>(nDST1) * 60000 + + (nDST1 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST1millis); + nDateTime -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0; + xCal->setDateTime( nDateTime ); + nDST2 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET ); + nDST2millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS ); + nDST2InMillis = static_cast<sal_Int32>(nDST2) * 60000 + + (nDST2 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST2millis); + if ( nDST1InMillis != nDST2InMillis ) + { + nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0; + xCal->setDateTime( nDateTime ); + } + nDaySet = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH ); + nMonthSet = xCal->getValue( i18n::CalendarFieldIndex::MONTH ); + nYearSet = xCal->getValue( i18n::CalendarFieldIndex::YEAR ); + nHourSet = xCal->getValue( i18n::CalendarFieldIndex::HOUR ); + nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE ); + nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND ); + nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET ); + nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS ); + nDST = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET ); + nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS ); + xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet ); + xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet ); + xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet ); + xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet ); + xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet ); + xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet ); + bValid = xCal->isValid(); + nDay = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH ); + nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH ); + nYear = xCal->getValue( i18n::CalendarFieldIndex::YEAR ); + nHour = xCal->getValue( i18n::CalendarFieldIndex::HOUR ); + nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE ); + nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND ); + bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear == + nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond + == nSecondSet; + } +} +#endif // NF_TEST_CALENDAR + + } + + return res; +} + + +//--------------------------------------------------------------------------- +// ScanStartString +// +// ersten String analysieren +// Alles weg => TRUE +// sonst => FALSE + +BOOL ImpSvNumberInputScan::ScanStartString( const String& rString, + const SvNumberformat* pFormat ) +{ + xub_StrLen nPos = 0; + int nDayOfWeek; + + // First of all, eat leading blanks + SkipBlanks(rString, nPos); + + // Yes, nMatchedAllStrings should know about the sign position + nSign = GetSign(rString, nPos); + if ( nSign ) // sign? + SkipBlanks(rString, nPos); + + // #102371# match against format string only if start string is not a sign character + if ( nMatchedAllStrings && !(nSign && rString.Len() == 1) ) + { // Match against format in any case, so later on for a "x1-2-3" input + // we may distinguish between a xy-m-d (or similar) date and a x0-0-0 + // format. No sign detection here! + if ( ScanStringNumFor( rString, nPos, pFormat, 0, TRUE ) ) + nMatchedAllStrings |= nMatchedStartString; + else + nMatchedAllStrings = 0; + } + + if ( GetDecSep(rString, nPos) ) // decimal separator in start string + { + nDecPos = 1; + SkipBlanks(rString, nPos); + } + else if ( GetCurrency(rString, nPos, pFormat) ) // currency (DM 1)? + { + eScannedType = NUMBERFORMAT_CURRENCY; // !!! it IS currency !!! + SkipBlanks(rString, nPos); + if (nSign == 0) // no sign yet + { + nSign = GetSign(rString, nPos); + if ( nSign ) // DM -1 + SkipBlanks(rString, nPos); + } + } + else + { + nMonth = GetMonth(rString, nPos); + if ( nMonth ) // month (Jan 1)? + { + eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date !!! + nMonthPos = 1; // month at the beginning + if ( nMonth < 0 ) + SkipChar( '.', rString, nPos ); // abbreviated + SkipBlanks(rString, nPos); + } + else + { + nDayOfWeek = GetDayOfWeek( rString, nPos ); + if ( nDayOfWeek ) + { // day of week is just parsed away + eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date !!! + if ( nPos < rString.Len() ) + { + if ( nDayOfWeek < 0 ) + { // abbreviated + if ( rString.GetChar( nPos ) == '.' ) + ++nPos; + } + else + { // full long name + SkipBlanks(rString, nPos); + SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos ); + } + SkipBlanks(rString, nPos); + nMonth = GetMonth(rString, nPos); + if ( nMonth ) // month (Jan 1)? + { + nMonthPos = 1; // month a the beginning + if ( nMonth < 0 ) + SkipChar( '.', rString, nPos ); // abbreviated + SkipBlanks(rString, nPos); + } + } + } + } + } + + if (nPos < rString.Len()) // not everything consumed + { + // Does input StartString equal StartString of format? + // This time with sign detection! + if ( !ScanStringNumFor( rString, nPos, pFormat, 0 ) ) + return MatchedReturn(); + } + + return TRUE; +} + + +//--------------------------------------------------------------------------- +// ScanMidString +// +// String in der Mitte analysieren +// Alles weg => TRUE +// sonst => FALSE + +BOOL ImpSvNumberInputScan::ScanMidString( const String& rString, + USHORT nStringPos, const SvNumberformat* pFormat ) +{ + xub_StrLen nPos = 0; + short eOldScannedType = eScannedType; + + if ( nMatchedAllStrings ) + { // Match against format in any case, so later on for a "1-2-3-4" input + // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0 + // format. + if ( ScanStringNumFor( rString, 0, pFormat, nStringPos ) ) + nMatchedAllStrings |= nMatchedMidString; + else + nMatchedAllStrings = 0; + } + + SkipBlanks(rString, nPos); + if (GetDecSep(rString, nPos)) // decimal separator? + { + if (nDecPos == 1 || nDecPos == 3) // .12.4 or 1.E2.1 + return MatchedReturn(); + else if (nDecPos == 2) // . dup: 12.4. + { + if (bDecSepInDateSeps) // . also date separator + { + if ( eScannedType != NUMBERFORMAT_UNDEFINED && + eScannedType != NUMBERFORMAT_DATE && + eScannedType != NUMBERFORMAT_DATETIME) // already another type + return MatchedReturn(); + if (eScannedType == NUMBERFORMAT_UNDEFINED) + eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date + SkipBlanks(rString, nPos); + } + else + return MatchedReturn(); + } + else + { + nDecPos = 2; // . in mid string + SkipBlanks(rString, nPos); + } + } + else if ( ((eScannedType & NUMBERFORMAT_TIME) == NUMBERFORMAT_TIME) + && GetTime100SecSep( rString, nPos ) ) + { // hundredth seconds separator + if ( nDecPos ) + return MatchedReturn(); + nDecPos = 2; // . in mid string + SkipBlanks(rString, nPos); + } + + if (SkipChar('/', rString, nPos)) // fraction? + { + if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type + && eScannedType != NUMBERFORMAT_DATE) // except date + return MatchedReturn(); // => jan/31/1994 + else if ( eScannedType != NUMBERFORMAT_DATE // analyzed date until now + && ( eSetType == NUMBERFORMAT_FRACTION // and preset was fraction + || (nAnzNums == 3 // or 3 numbers + && nStringPos > 2) ) ) // and what ??? + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_FRACTION; // !!! it IS a fraction + } + else + nPos--; // put '/' back + } + + if (GetThousandSep(rString, nPos, nStringPos)) // 1,000 + { + if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type + && eScannedType != NUMBERFORMAT_CURRENCY) // except currency + return MatchedReturn(); + nThousand++; + } + + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + const String& rDate = pFormatter->GetDateSep(); + const String& rTime = pLoc->getTimeSep(); + sal_Unicode cTime = rTime.GetChar(0); + SkipBlanks(rString, nPos); + if ( SkipString(rDate, rString, nPos) // 10., 10-, 10/ + || ((cTime != '.') && SkipChar('.', rString, nPos)) // TRICKY: + || ((cTime != '/') && SkipChar('/', rString, nPos)) // short boolean + || ((cTime != '-') && SkipChar('-', rString, nPos)) ) // evaluation! + { + if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type + && eScannedType != NUMBERFORMAT_DATE) // except date + return MatchedReturn(); + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date + short nTmpMonth = GetMonth(rString, nPos); // 10. Jan 94 + if (nMonth && nTmpMonth) // month dup + return MatchedReturn(); + if (nTmpMonth) + { + nMonth = nTmpMonth; + nMonthPos = 2; // month in the middle + if ( nMonth < 0 && SkipChar( '.', rString, nPos ) ) + ; // short month may be abbreviated Jan. + else if ( SkipChar( '-', rString, nPos ) ) + ; // #79632# recognize 17-Jan-2001 to be a date + // #99065# short and long month name + else + SkipString( pLoc->getLongDateMonthSep(), rString, nPos ); + SkipBlanks(rString, nPos); + } + } + + short nTempMonth = GetMonth(rString, nPos); // month in the middle (10 Jan 94) + if (nTempMonth) + { + if (nMonth != 0) // month dup + return MatchedReturn(); + if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type + && eScannedType != NUMBERFORMAT_DATE) // except date + return MatchedReturn(); + eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date + nMonth = nTempMonth; + nMonthPos = 2; // month in the middle + if ( nMonth < 0 ) + SkipChar( '.', rString, nPos ); // abbreviated + SkipString( pLoc->getLongDateMonthSep(), rString, nPos ); + SkipBlanks(rString, nPos); + } + + if ( SkipChar('E', rString, nPos) // 10E, 10e, 10,Ee + || SkipChar('e', rString, nPos) ) + { + if (eScannedType != NUMBERFORMAT_UNDEFINED) // already another type + return MatchedReturn(); + else + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_SCIENTIFIC; // !!! it IS scientific + if ( nThousand+2 == nAnzNums // special case 1.E2 + && nDecPos == 2 ) + nDecPos = 3; // 1,100.E2 1,100,100.E3 + } + nESign = GetESign(rString, nPos); // signed exponent? + SkipBlanks(rString, nPos); + } + + if ( SkipString(rTime, rString, nPos) ) // time separator? + { + if (nDecPos) // already . => maybe error + { + if (bDecSepInDateSeps) // . also date sep + { + if ( eScannedType != NUMBERFORMAT_DATE && // already another type than date + eScannedType != NUMBERFORMAT_DATETIME) // or date time + return MatchedReturn(); + if (eScannedType == NUMBERFORMAT_DATE) + nDecPos = 0; // reset for time transition + } + else + return MatchedReturn(); + } + if ( ( eScannedType == NUMBERFORMAT_DATE // already date type + || eScannedType == NUMBERFORMAT_DATETIME) // or date time + && nAnzNums > 3) // and more than 3 numbers? (31.Dez.94 8:23) + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_DATETIME; // !!! it IS date with time + } + else if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type + && eScannedType != NUMBERFORMAT_TIME) // except time + return MatchedReturn(); + else + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_TIME; // !!! it IS a time + } + if ( !nTimePos ) + nTimePos = nStringPos + 1; + } + + if (nPos < rString.Len()) + { + switch (eScannedType) + { + case NUMBERFORMAT_DATE: + if (nMonthPos == 1 && pLoc->getLongDateFormat() == MDY) + { + // #68232# recognize long date separators like ", " in "September 5, 1999" + if (SkipString( pLoc->getLongDateDaySep(), rString, nPos )) + SkipBlanks( rString, nPos ); + } + else if (nStringPos == 5 && nPos == 0 && rString.Len() == 1 && + rString.GetChar(0) == 'T' && MayBeIso8601()) + { + // ISO 8601 combined date and time, yyyy-mm-ddThh:mm + ++nPos; + } + break; +#if NF_RECOGNIZE_ISO8601_TIMEZONES + case NUMBERFORMAT_DATETIME: + if (nPos == 0 && rString.Len() == 1 && nStringPos >= 9 && + MayBeIso8601()) + { + // ISO 8601 timezone offset + switch (rString.GetChar(0)) + { + case '+': + case '-': + if (nStringPos == nAnzStrings-2 || + nStringPos == nAnzStrings-4) + { + ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy] + // nTimezonePos needed for GetTimeRef() + if (!nTimezonePos) + nTimezonePos = nStringPos + 1; + } + break; + case ':': + if (nTimezonePos && nStringPos >= 11 && + nStringPos == nAnzStrings-2) + ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx:yy + break; + } + } + break; +#endif + } + } + + if (nPos < rString.Len()) // not everything consumed? + { + if ( nMatchedAllStrings & ~nMatchedVirgin ) + eScannedType = eOldScannedType; + else + return FALSE; + } + + return TRUE; +} + + +//--------------------------------------------------------------------------- +// ScanEndString +// +// Schlussteil analysieren +// Alles weg => TRUE +// sonst => FALSE + +BOOL ImpSvNumberInputScan::ScanEndString( const String& rString, + const SvNumberformat* pFormat ) +{ + xub_StrLen nPos = 0; + + if ( nMatchedAllStrings ) + { // Match against format in any case, so later on for a "1-2-3-4" input + // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0 + // format. + if ( ScanStringNumFor( rString, 0, pFormat, 0xFFFF ) ) + nMatchedAllStrings |= nMatchedEndString; + else + nMatchedAllStrings = 0; + } + + SkipBlanks(rString, nPos); + if (GetDecSep(rString, nPos)) // decimal separator? + { + if (nDecPos == 1 || nDecPos == 3) // .12.4 or 12.E4. + return MatchedReturn(); + else if (nDecPos == 2) // . dup: 12.4. + { + if (bDecSepInDateSeps) // . also date sep + { + if ( eScannedType != NUMBERFORMAT_UNDEFINED && + eScannedType != NUMBERFORMAT_DATE && + eScannedType != NUMBERFORMAT_DATETIME) // already another type + return MatchedReturn(); + if (eScannedType == NUMBERFORMAT_UNDEFINED) + eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date + SkipBlanks(rString, nPos); + } + else + return MatchedReturn(); + } + else + { + nDecPos = 3; // . in end string + SkipBlanks(rString, nPos); + } + } + + if ( nSign == 0 // conflict - not signed + && eScannedType != NUMBERFORMAT_DATE) // and not date +//!? catch time too? + { // not signed yet + nSign = GetSign(rString, nPos); // 1- DM + if (nNegCheck) // '(' as sign + return MatchedReturn(); + } + + SkipBlanks(rString, nPos); + if (nNegCheck && SkipChar(')', rString, nPos)) // skip ')' if appropriate + { + nNegCheck = 0; + SkipBlanks(rString, nPos); + } + + if ( GetCurrency(rString, nPos, pFormat) ) // currency symbol? + { + if (eScannedType != NUMBERFORMAT_UNDEFINED) // currency dup + return MatchedReturn(); + else + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_CURRENCY; + } // behind currency a '-' is allowed + if (nSign == 0) // not signed yet + { + nSign = GetSign(rString, nPos); // DM - + SkipBlanks(rString, nPos); + if (nNegCheck) // 3 DM ( + return MatchedReturn(); + } + if ( nNegCheck && eScannedType == NUMBERFORMAT_CURRENCY + && SkipChar(')', rString, nPos) ) + { + nNegCheck = 0; // ')' skipped + SkipBlanks(rString, nPos); // only if currency + } + } + + if ( SkipChar('%', rString, nPos) ) // 1 % + { + if (eScannedType != NUMBERFORMAT_UNDEFINED) // already another type + return MatchedReturn(); + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_PERCENT; + } + + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + const String& rDate = pFormatter->GetDateSep(); + const String& rTime = pLoc->getTimeSep(); + if ( SkipString(rTime, rString, nPos) ) // 10: + { + if (nDecPos) // already , => error + return MatchedReturn(); + if (eScannedType == NUMBERFORMAT_DATE && nAnzNums > 2) // 31.Dez.94 8: + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_DATETIME; + } + else if (eScannedType != NUMBERFORMAT_UNDEFINED && + eScannedType != NUMBERFORMAT_TIME) // already another type + return MatchedReturn(); + else + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_TIME; + } + if ( !nTimePos ) + nTimePos = nAnzStrings; + } + + sal_Unicode cTime = rTime.GetChar(0); + if ( SkipString(rDate, rString, nPos) // 10., 10-, 10/ + || ((cTime != '.') && SkipChar('.', rString, nPos)) // TRICKY: + || ((cTime != '/') && SkipChar('/', rString, nPos)) // short boolean + || ((cTime != '-') && SkipChar('-', rString, nPos)) ) // evaluation! + { + if (eScannedType != NUMBERFORMAT_UNDEFINED && + eScannedType != NUMBERFORMAT_DATE) // already another type + return MatchedReturn(); + else + { + SkipBlanks(rString, nPos); + eScannedType = NUMBERFORMAT_DATE; + } + short nTmpMonth = GetMonth(rString, nPos); // 10. Jan + if (nMonth && nTmpMonth) // month dup + return MatchedReturn(); + if (nTmpMonth) + { + nMonth = nTmpMonth; + nMonthPos = 3; // month at end + if ( nMonth < 0 ) + SkipChar( '.', rString, nPos ); // abbreviated + SkipBlanks(rString, nPos); + } + } + + short nTempMonth = GetMonth(rString, nPos); // 10 Jan + if (nTempMonth) + { + if (nMonth) // month dup + return MatchedReturn(); + if (eScannedType != NUMBERFORMAT_UNDEFINED && + eScannedType != NUMBERFORMAT_DATE) // already another type + return MatchedReturn(); + eScannedType = NUMBERFORMAT_DATE; + nMonth = nTempMonth; + nMonthPos = 3; // month at end + if ( nMonth < 0 ) + SkipChar( '.', rString, nPos ); // abbreviated + SkipBlanks(rString, nPos); + } + + xub_StrLen nOrigPos = nPos; + if (GetTimeAmPm(rString, nPos)) + { + if (eScannedType != NUMBERFORMAT_UNDEFINED && + eScannedType != NUMBERFORMAT_TIME && + eScannedType != NUMBERFORMAT_DATETIME) // already another type + return MatchedReturn(); + else + { + // If not already scanned as time, 6.78am does not result in 6 + // seconds and 78 hundredths in the morning. Keep as suffix. + if (eScannedType != NUMBERFORMAT_TIME && nDecPos == 2 && nAnzNums == 2) + nPos = nOrigPos; // rewind am/pm + else + { + SkipBlanks(rString, nPos); + if ( eScannedType != NUMBERFORMAT_DATETIME ) + eScannedType = NUMBERFORMAT_TIME; + } + } + } + + if ( nNegCheck && SkipChar(')', rString, nPos) ) + { + if (eScannedType == NUMBERFORMAT_CURRENCY) // only if currency + { + nNegCheck = 0; // skip ')' + SkipBlanks(rString, nPos); + } + else + return MatchedReturn(); + } + + if ( nPos < rString.Len() && + (eScannedType == NUMBERFORMAT_DATE + || eScannedType == NUMBERFORMAT_DATETIME) ) + { // day of week is just parsed away + xub_StrLen nOldPos = nPos; + const String& rSep = pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(); + if ( StringContains( rSep, rString, nPos ) ) + { + nPos = nPos + rSep.Len(); + SkipBlanks(rString, nPos); + } + int nDayOfWeek = GetDayOfWeek( rString, nPos ); + if ( nDayOfWeek ) + { + if ( nPos < rString.Len() ) + { + if ( nDayOfWeek < 0 ) + { // short + if ( rString.GetChar( nPos ) == '.' ) + ++nPos; + } + SkipBlanks(rString, nPos); + } + } + else + nPos = nOldPos; + } + +#if NF_RECOGNIZE_ISO8601_TIMEZONES + if (nPos == 0 && eScannedType == NUMBERFORMAT_DATETIME && + rString.Len() == 1 && rString.GetChar(0) == 'Z' && MayBeIso8601()) + { + // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ + ++nPos; + } +#endif + + if (nPos < rString.Len()) // everything consumed? + { + // does input EndString equal EndString in Format? + if ( !ScanStringNumFor( rString, nPos, pFormat, 0xFFFF ) ) + return FALSE; + } + + return TRUE; +} + + +BOOL ImpSvNumberInputScan::ScanStringNumFor( + const String& rString, // String to scan + xub_StrLen nPos, // Position until which was consumed + const SvNumberformat* pFormat, // The format to match + USHORT nString, // Substring of format, 0xFFFF => last + BOOL bDontDetectNegation // Suppress sign detection + ) +{ + if ( !pFormat ) + return FALSE; + const ::utl::TransliterationWrapper* pTransliteration = pFormatter->GetTransliteration(); + const String* pStr; + String aString( rString ); + BOOL bFound = FALSE; + BOOL bFirst = TRUE; + BOOL bContinue = TRUE; + USHORT nSub; + do + { + // Don't try "lower" subformats ff the very first match was the second + // or third subformat. + nSub = nStringScanNumFor; + do + { // Step through subformats, first positive, then negative, then + // other, but not the last (text) subformat. + pStr = pFormat->GetNumForString( nSub, nString, TRUE ); + if ( pStr && pTransliteration->isEqual( aString, *pStr ) ) + { + bFound = TRUE; + bContinue = FALSE; + } + else if ( nSub < 2 ) + ++nSub; + else + bContinue = FALSE; + } while ( bContinue ); + if ( !bFound && bFirst && nPos ) + { // try remaining substring + bFirst = FALSE; + aString.Erase( 0, nPos ); + bContinue = TRUE; + } + } while ( bContinue ); + + if ( !bFound ) + { + if ( !bDontDetectNegation && (nString == 0) && !bFirst && (nSign < 0) + && pFormat->IsNegativeRealNegative() ) + { // simply negated twice? --1 + aString.EraseAllChars( ' ' ); + if ( (aString.Len() == 1) && (aString.GetChar(0) == '-') ) + { + bFound = TRUE; + nStringScanSign = -1; + nSub = 0; //! not 1 + } + } + if ( !bFound ) + return FALSE; + } + else if ( !bDontDetectNegation && (nSub == 1) && + pFormat->IsNegativeRealNegative() ) + { // negative + if ( nStringScanSign < 0 ) + { + if ( (nSign < 0) && (nStringScanNumFor != 1) ) + nStringScanSign = 1; // triple negated --1 yyy + } + else if ( nStringScanSign == 0 ) + { + if ( nSign < 0 ) + { // nSign and nStringScanSign will be combined later, + // flip sign if doubly negated + if ( (nString == 0) && !bFirst + && SvNumberformat::HasStringNegativeSign( aString ) ) + nStringScanSign = -1; // direct double negation + else if ( pFormat->IsNegativeWithoutSign() ) + nStringScanSign = -1; // indirect double negation + } + else + nStringScanSign = -1; + } + else // > 0 + nStringScanSign = -1; + } + nStringScanNumFor = nSub; + return TRUE; +} + + +//--------------------------------------------------------------------------- +// IsNumberFormatMain +// +// Recognizes types of number, exponential, fraction, percent, currency, date, time. +// Else text => return FALSE + +BOOL ImpSvNumberInputScan::IsNumberFormatMain( + const String& rString, // string to be analyzed + double& , // OUT: result as number, if possible + const SvNumberformat* pFormat ) // maybe number format set to match against +{ + Reset(); + NumberStringDivision( rString ); // breakdown into strings and numbers + if (nAnzStrings >= SV_MAX_ANZ_INPUT_STRINGS) // too many elements + return FALSE; // Njet, Nope, ... + + if (nAnzNums == 0) // no number in input + { + if ( nAnzStrings > 0 ) + { + // Here we may change the original, we don't need it anymore. + // This saves copies and ToUpper() in GetLogical() and is faster. + String& rStrArray = sStrArray[0]; + rStrArray.EraseTrailingChars( ' ' ); + rStrArray.EraseLeadingChars( ' ' ); + nLogical = GetLogical( rStrArray ); + if ( nLogical ) + { + eScannedType = NUMBERFORMAT_LOGICAL; // !!! it's a BOOLEAN + nMatchedAllStrings &= ~nMatchedVirgin; + return TRUE; + } + else + return FALSE; // simple text + } + else + return FALSE; // simple text + } + + USHORT i = 0; // mark any symbol + USHORT j = 0; // mark only numbers + + switch ( nAnzNums ) + { + case 1 : // Exactly 1 number in input + { // nAnzStrings >= 1 + if (GetNextNumber(i,j)) // i=1,0 + { // Number at start + if (eSetType == NUMBERFORMAT_FRACTION) // Fraction 1 = 1/1 + { + if (i >= nAnzStrings || // no end string nor decimal separator + sStrArray[i] == pFormatter->GetNumDecimalSep()) + { + eScannedType = NUMBERFORMAT_FRACTION; + nMatchedAllStrings &= ~nMatchedVirgin; + return TRUE; + } + } + } + else + { // Analyze start string + if (!ScanStartString( sStrArray[i], pFormat )) // i=0 + return FALSE; // already an error + i++; // next symbol, i=1 + } + GetNextNumber(i,j); // i=1,2 + if (eSetType == NUMBERFORMAT_FRACTION) // Fraction -1 = -1/1 + { + if (nSign && !nNegCheck && // Sign +, - + eScannedType == NUMBERFORMAT_UNDEFINED && // not date or currency + nDecPos == 0 && // no previous decimal separator + (i >= nAnzStrings || // no end string nor decimal separator + sStrArray[i] == pFormatter->GetNumDecimalSep()) + ) + { + eScannedType = NUMBERFORMAT_FRACTION; + nMatchedAllStrings &= ~nMatchedVirgin; + return TRUE; + } + } + if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) + return FALSE; + } + break; + case 2 : // Exactly 2 numbers in input + { // nAnzStrings >= 3 + if (!GetNextNumber(i,j)) // i=1,0 + { // Analyze start string + if (!ScanStartString( sStrArray[i], pFormat )) + return FALSE; // already an error + i++; // i=1 + } + GetNextNumber(i,j); // i=1,2 + if ( !ScanMidString( sStrArray[i], i, pFormat ) ) + return FALSE; + i++; // next symbol, i=2,3 + GetNextNumber(i,j); // i=3,4 + if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) + return FALSE; + if (eSetType == NUMBERFORMAT_FRACTION) // -1,200. as fraction + { + if (!nNegCheck && // no sign '(' + eScannedType == NUMBERFORMAT_UNDEFINED && + (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end + ) + { + eScannedType = NUMBERFORMAT_FRACTION; + nMatchedAllStrings &= ~nMatchedVirgin; + return TRUE; + } + } + } + break; + case 3 : // Exactly 3 numbers in input + { // nAnzStrings >= 5 + if (!GetNextNumber(i,j)) // i=1,0 + { // Analyze start string + if (!ScanStartString( sStrArray[i], pFormat )) + return FALSE; // already an error + i++; // i=1 + if (nDecPos == 1) // decimal separator at start => error + return FALSE; + } + GetNextNumber(i,j); // i=1,2 + if ( !ScanMidString( sStrArray[i], i, pFormat ) ) + return FALSE; + i++; // i=2,3 + if (eScannedType == NUMBERFORMAT_SCIENTIFIC) // E only at end + return FALSE; + GetNextNumber(i,j); // i=3,4 + if ( !ScanMidString( sStrArray[i], i, pFormat ) ) + return FALSE; + i++; // i=4,5 + GetNextNumber(i,j); // i=5,6 + if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) + return FALSE; + if (eSetType == NUMBERFORMAT_FRACTION) // -1,200,100. as fraction + { + if (!nNegCheck && // no sign '(' + eScannedType == NUMBERFORMAT_UNDEFINED && + (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end + ) + { + eScannedType = NUMBERFORMAT_FRACTION; + nMatchedAllStrings &= ~nMatchedVirgin; + return TRUE; + } + } + if ( eScannedType == NUMBERFORMAT_FRACTION && nDecPos ) + return FALSE; // #36857# not a real fraction + } + break; + default: // More than 3 numbers in input + { // nAnzStrings >= 7 + if (!GetNextNumber(i,j)) // i=1,0 + { // Analyze startstring + if (!ScanStartString( sStrArray[i], pFormat )) + return FALSE; // already an error + i++; // i=1 + if (nDecPos == 1) // decimal separator at start => error + return FALSE; + } + GetNextNumber(i,j); // i=1,2 + if ( !ScanMidString( sStrArray[i], i, pFormat ) ) + return FALSE; + i++; // i=2,3 + USHORT nThOld = 10; // just not 0 or 1 + while (nThOld != nThousand && j < nAnzNums-1) + // Execute at least one time + // but leave one number. + { // Loop over group separators + nThOld = nThousand; + if (eScannedType == NUMBERFORMAT_SCIENTIFIC) // E only at end + return FALSE; + GetNextNumber(i,j); + if ( i < nAnzStrings && !ScanMidString( sStrArray[i], i, pFormat ) ) + return FALSE; + i++; + } + if (eScannedType == NUMBERFORMAT_DATE || // long date or + eScannedType == NUMBERFORMAT_TIME || // long time or + eScannedType == NUMBERFORMAT_UNDEFINED) // long number + { + for (USHORT k = j; k < nAnzNums-1; k++) + { + if (eScannedType == NUMBERFORMAT_SCIENTIFIC) // E only at endd + return FALSE; + GetNextNumber(i,j); + if ( i < nAnzStrings && !ScanMidString( sStrArray[i], i, pFormat ) ) + return FALSE; + i++; + } + } + GetNextNumber(i,j); + if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) + return FALSE; + if (eSetType == NUMBERFORMAT_FRACTION) // -1,200,100. as fraction + { + if (!nNegCheck && // no sign '(' + eScannedType == NUMBERFORMAT_UNDEFINED && + (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end + ) + { + eScannedType = NUMBERFORMAT_FRACTION; + nMatchedAllStrings &= ~nMatchedVirgin; + return TRUE; + } + } + if ( eScannedType == NUMBERFORMAT_FRACTION && nDecPos ) + return FALSE; // #36857# not a real fraction + } + } + + if (eScannedType == NUMBERFORMAT_UNDEFINED) + { + nMatchedAllStrings &= ~nMatchedVirgin; + // did match including nMatchedUsedAsReturn + BOOL bDidMatch = (nMatchedAllStrings != 0); + if ( nMatchedAllStrings ) + { + BOOL bMatch = (pFormat ? pFormat->IsNumForStringElementCountEqual( + nStringScanNumFor, nAnzStrings, nAnzNums ) : FALSE); + if ( !bMatch ) + nMatchedAllStrings = 0; + } + if ( nMatchedAllStrings ) + eScannedType = eSetType; + else if ( bDidMatch ) + return FALSE; + else + eScannedType = NUMBERFORMAT_NUMBER; + // everything else should have been recognized by now + } + else if ( eScannedType == NUMBERFORMAT_DATE ) + { // the very relaxed date input checks may interfere with a preset format + nMatchedAllStrings &= ~nMatchedVirgin; + BOOL bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0); + if ( nMatchedAllStrings ) + { + BOOL bMatch = (pFormat ? pFormat->IsNumForStringElementCountEqual( + nStringScanNumFor, nAnzStrings, nAnzNums ) : FALSE); + if ( !bMatch ) + nMatchedAllStrings = 0; + } + if ( nMatchedAllStrings ) + eScannedType = eSetType; + else if ( bWasReturn ) + return FALSE; + } + else + nMatchedAllStrings = 0; // reset flag to no substrings matched + + return TRUE; +} + + +//--------------------------------------------------------------------------- +// return TRUE or FALSE depending on the nMatched... state and remember usage +BOOL ImpSvNumberInputScan::MatchedReturn() +{ + if ( nMatchedAllStrings & ~nMatchedVirgin ) + { + nMatchedAllStrings |= nMatchedUsedAsReturn; + return TRUE; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// Initialize uppercase months and weekdays + +void ImpSvNumberInputScan::InitText() +{ + sal_Int32 j, nElems; + const CharClass* pChrCls = pFormatter->GetCharClass(); + const CalendarWrapper* pCal = pFormatter->GetCalendar(); + delete [] pUpperMonthText; + delete [] pUpperAbbrevMonthText; + ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > xElems + = pCal->getMonths(); + nElems = xElems.getLength(); + pUpperMonthText = new String[nElems]; + pUpperAbbrevMonthText = new String[nElems]; + for ( j=0; j<nElems; j++ ) + { + pUpperMonthText[j] = pChrCls->upper( xElems[j].FullName ); + pUpperAbbrevMonthText[j] = pChrCls->upper( xElems[j].AbbrevName ); + } + delete [] pUpperDayText; + delete [] pUpperAbbrevDayText; + xElems = pCal->getDays(); + nElems = xElems.getLength(); + pUpperDayText = new String[nElems]; + pUpperAbbrevDayText = new String[nElems]; + for ( j=0; j<nElems; j++ ) + { + pUpperDayText[j] = pChrCls->upper( xElems[j].FullName ); + pUpperAbbrevDayText[j] = pChrCls->upper( xElems[j].AbbrevName ); + } + bTextInitialized = TRUE; +} + + +//=========================================================================== +// P U B L I C + +//--------------------------------------------------------------------------- +// ChangeIntl +// +// MUST be called if International/Locale is changed + +void ImpSvNumberInputScan::ChangeIntl() +{ + sal_Unicode cDecSep = pFormatter->GetNumDecimalSep().GetChar(0); + bDecSepInDateSeps = ( cDecSep == '-' || + cDecSep == '/' || + cDecSep == '.' || + cDecSep == pFormatter->GetDateSep().GetChar(0) ); + bTextInitialized = FALSE; + aUpperCurrSymbol.Erase(); +} + + +//--------------------------------------------------------------------------- +// ChangeNullDate + +void ImpSvNumberInputScan::ChangeNullDate( + const USHORT Day, + const USHORT Month, + const USHORT Year ) +{ + if ( pNullDate ) + *pNullDate = Date(Day, Month, Year); + else + pNullDate = new Date(Day, Month, Year); +} + + +//--------------------------------------------------------------------------- +// IsNumberFormat +// +// => does rString represent a number (also date, time et al) + +BOOL ImpSvNumberInputScan::IsNumberFormat( + const String& rString, // string to be analyzed + short& F_Type, // IN: old type, OUT: new type + double& fOutNumber, // OUT: number if convertable + const SvNumberformat* pFormat ) // maybe a number format to match against +{ + String sResString; + String aString; + BOOL res; // return value + eSetType = F_Type; // old type set + + if ( !rString.Len() ) + res = FALSE; + else if (rString.Len() > 308) // arbitrary + res = FALSE; + else + { + // NoMoreUpperNeeded, all comparisons on UpperCase + aString = pFormatter->GetCharClass()->upper( rString ); + // convert native number to ASCII if necessary + TransformInput( aString ); + res = IsNumberFormatMain( aString, fOutNumber, pFormat ); + } + + if (res) + { + if ( nNegCheck // ')' not found for '(' + || (nSign && (eScannedType == NUMBERFORMAT_DATE + || eScannedType == NUMBERFORMAT_DATETIME)) + ) // signed date/datetime + res = FALSE; + else + { // check count of partial number strings + switch (eScannedType) + { + case NUMBERFORMAT_PERCENT: + case NUMBERFORMAT_CURRENCY: + case NUMBERFORMAT_NUMBER: + if (nDecPos == 1) // .05 + { + // matched MidStrings function like group separators + if ( nMatchedAllStrings ) + nThousand = nAnzNums - 1; + else if ( nAnzNums != 1 ) + res = FALSE; + } + else if (nDecPos == 2) // 1.05 + { + // matched MidStrings function like group separators + if ( nMatchedAllStrings ) + nThousand = nAnzNums - 1; + else if ( nAnzNums != nThousand+2 ) + res = FALSE; + } + else // 1,100 or 1,100. + { + // matched MidStrings function like group separators + if ( nMatchedAllStrings ) + nThousand = nAnzNums - 1; + else if ( nAnzNums != nThousand+1 ) + res = FALSE; + } + break; + + case NUMBERFORMAT_SCIENTIFIC: // 1.0e-2 + if (nDecPos == 1) // .05 + { + if (nAnzNums != 2) + res = FALSE; + } + else if (nDecPos == 2) // 1.05 + { + if (nAnzNums != nThousand+3) + res = FALSE; + } + else // 1,100 or 1,100. + { + if (nAnzNums != nThousand+2) + res = FALSE; + } + break; + + case NUMBERFORMAT_DATE: + if (nMonth) + { // month name and numbers + if (nAnzNums > 2) + res = FALSE; + } + else + { + if (nAnzNums > 3) + res = FALSE; + } + break; + + case NUMBERFORMAT_TIME: + if (nDecPos) + { // hundredth seconds included + if (nAnzNums > 4) + res = FALSE; + } + else + { + if (nAnzNums > 3) + res = FALSE; + } + break; + + case NUMBERFORMAT_DATETIME: + if (nMonth) + { // month name and numbers + if (nDecPos) + { // hundredth seconds included + if (nAnzNums > 6) + res = FALSE; + } + else + { + if (nAnzNums > 5) + res = FALSE; + } + } + else + { + if (nDecPos) + { // hundredth seconds included + if (nAnzNums > 7) + res = FALSE; + } + else + { + if (nAnzNums > 6) + res = FALSE; + } + } + break; + + default: + break; + } // switch + } // else + } // if (res) + + if (res) + { // we finally have a number + switch (eScannedType) + { + case NUMBERFORMAT_LOGICAL: + if (nLogical == 1) + fOutNumber = 1.0; // True + else if (nLogical == -1) + fOutNumber = 0.0; // False + else + res = FALSE; // Oops + break; + + case NUMBERFORMAT_PERCENT: + case NUMBERFORMAT_CURRENCY: + case NUMBERFORMAT_NUMBER: + case NUMBERFORMAT_SCIENTIFIC: + case NUMBERFORMAT_DEFINED: // if no category detected handle as number + { + if ( nDecPos == 1 ) // . at start + sResString.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "0." ) ); + else + sResString.Erase(); + USHORT k; + for ( k = 0; k <= nThousand; k++) + sResString += sStrArray[nNums[k]]; // integer part + if ( nDecPos == 2 && k < nAnzNums ) // . somewhere + { + sResString += '.'; + USHORT nStop = (eScannedType == NUMBERFORMAT_SCIENTIFIC ? + nAnzNums-1 : nAnzNums); + for ( ; k < nStop; k++) + sResString += sStrArray[nNums[k]]; // fractional part + } + + if (eScannedType != NUMBERFORMAT_SCIENTIFIC) + fOutNumber = StringToDouble(sResString); + else + { // append exponent + sResString += 'E'; + if ( nESign == -1 ) + sResString += '-'; + sResString += sStrArray[nNums[nAnzNums-1]]; + rtl_math_ConversionStatus eStatus; + fOutNumber = ::rtl::math::stringToDouble( + sResString, '.', ',', &eStatus, NULL ); + if ( eStatus == rtl_math_ConversionStatus_OutOfRange ) + { + F_Type = NUMBERFORMAT_TEXT; // overflow/underflow -> Text + if (nESign == -1) + fOutNumber = 0.0; + else + fOutNumber = DBL_MAX; +/*!*/ return TRUE; + } + } + + if ( nStringScanSign ) + { + if ( nSign ) + nSign *= nStringScanSign; + else + nSign = nStringScanSign; + } + if ( nSign < 0 ) + fOutNumber = -fOutNumber; + + if (eScannedType == NUMBERFORMAT_PERCENT) + fOutNumber/= 100.0; + } + break; + + case NUMBERFORMAT_FRACTION: + if (nAnzNums == 1) + fOutNumber = StringToDouble(sStrArray[nNums[0]]); + else if (nAnzNums == 2) + { + if (nThousand == 1) + { + sResString = sStrArray[nNums[0]]; + sResString += sStrArray[nNums[1]]; // integer part + fOutNumber = StringToDouble(sResString); + } + else + { + double fZaehler = StringToDouble(sStrArray[nNums[0]]); + double fNenner = StringToDouble(sStrArray[nNums[1]]); + if (fNenner != 0.0) + fOutNumber = fZaehler/fNenner; + else + res = FALSE; + } + } + else // nAnzNums > 2 + { + USHORT k = 1; + sResString = sStrArray[nNums[0]]; + if (nThousand > 0) + for (k = 1; k <= nThousand; k++) + sResString += sStrArray[nNums[k]]; + fOutNumber = StringToDouble(sResString); + + if (k == nAnzNums-2) + { + double fZaehler = StringToDouble(sStrArray[nNums[k]]); + double fNenner = StringToDouble(sStrArray[nNums[k+1]]); + if (fNenner != 0.0) + fOutNumber += fZaehler/fNenner; + else + res = FALSE; + } + } + + if ( nStringScanSign ) + { + if ( nSign ) + nSign *= nStringScanSign; + else + nSign = nStringScanSign; + } + if ( nSign < 0 ) + fOutNumber = -fOutNumber; + break; + + case NUMBERFORMAT_TIME: + GetTimeRef(fOutNumber, 0, nAnzNums); + if ( nSign < 0 ) + fOutNumber = -fOutNumber; + break; + + case NUMBERFORMAT_DATE: + { + USHORT nCounter = 0; // dummy here + res = GetDateRef( fOutNumber, nCounter, pFormat ); + } + break; + + case NUMBERFORMAT_DATETIME: + { + USHORT nCounter = 0; // needed here + res = GetDateRef( fOutNumber, nCounter, pFormat ); + if ( res ) + { + double fTime; + GetTimeRef( fTime, nCounter, nAnzNums - nCounter ); + fOutNumber += fTime; + } + } + break; + + default: + DBG_ERRORFILE( "Some number recognized but what's it?" ); + fOutNumber = 0.0; + break; + } + } + + if (res) // overflow/underflow -> Text + { + if (fOutNumber < -DBL_MAX) // -1.7E308 + { + F_Type = NUMBERFORMAT_TEXT; + fOutNumber = -DBL_MAX; + return TRUE; + } + else if (fOutNumber > DBL_MAX) // 1.7E308 + { + F_Type = NUMBERFORMAT_TEXT; + fOutNumber = DBL_MAX; + return TRUE; + } + } + + if (res == FALSE) + { + eScannedType = NUMBERFORMAT_TEXT; + fOutNumber = 0.0; + } + + F_Type = eScannedType; + return res; +} + + + diff --git a/svl/source/numbers/zforfind.hxx b/svl/source/numbers/zforfind.hxx new file mode 100644 index 000000000000..049925f2034f --- /dev/null +++ b/svl/source/numbers/zforfind.hxx @@ -0,0 +1,291 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zforfind.hxx,v $ + * $Revision: 1.13 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _ZFORFIND_HXX +#define _ZFORFIND_HXX + +#include <tools/string.hxx> + +class Date; +class SvNumberformat; +class SvNumberFormatter; + +#define SV_MAX_ANZ_INPUT_STRINGS 20 // max count of substrings in input scanner + +class ImpSvNumberInputScan +{ +public: + ImpSvNumberInputScan( SvNumberFormatter* pFormatter ); + ~ImpSvNumberInputScan(); + +/*!*/ void ChangeIntl(); // MUST be called if language changes + + /// set reference date for offset calculation + void ChangeNullDate( + const USHORT nDay, + const USHORT nMonth, + const USHORT nYear ); + + /// convert input string to number + BOOL IsNumberFormat( + const String& rString, /// input string + short& F_Type, /// format type (in + out) + double& fOutNumber, /// value determined (out) + const SvNumberformat* pFormat = NULL /// optional a number format to which compare against + ); + + /// after IsNumberFormat: get decimal position + short GetDecPos() const { return nDecPos; } + /// after IsNumberFormat: get count of numeric substrings in input string + USHORT GetAnzNums() const { return nAnzNums; } + + /// set threshold of two-digit year input + void SetYear2000( USHORT nVal ) { nYear2000 = nVal; } + /// get threshold of two-digit year input + USHORT GetYear2000() const { return nYear2000; } + +private: + SvNumberFormatter* pFormatter; + String* pUpperMonthText; // Array of month names, uppercase + String* pUpperAbbrevMonthText; // Array of month names, abbreviated, uppercase + String* pUpperDayText; // Array of day of week names, uppercase + String* pUpperAbbrevDayText; // Array of day of week names, abbreviated, uppercase + String aUpperCurrSymbol; // Currency symbol, uppercase + BOOL bTextInitialized; // Whether days and months are initialized + Date* pNullDate; // 30Dec1899 + // Variables for provisional results: + String sStrArray[SV_MAX_ANZ_INPUT_STRINGS]; // Array of scanned substrings + BOOL IsNum[SV_MAX_ANZ_INPUT_STRINGS]; // Whether a substring is numeric + USHORT nNums[SV_MAX_ANZ_INPUT_STRINGS]; // Sequence of offsets to numeric strings + USHORT nAnzStrings; // Total count of scanned substrings + USHORT nAnzNums; // Count of numeric substrings + BOOL bDecSepInDateSeps; // True <=> DecSep in {.,-,/,DateSep} + BYTE nMatchedAllStrings; // Scan...String() matched all substrings, + // bit mask of nMatched... constants + + static const BYTE nMatchedEndString; // 0x01 + static const BYTE nMatchedMidString; // 0x02 + static const BYTE nMatchedStartString; // 0x04 + static const BYTE nMatchedVirgin; // 0x08 + static const BYTE nMatchedUsedAsReturn; // 0x10 + + int nSign; // Sign of number + short nMonth; // Month (1..x) if date + // negative => short format + short nMonthPos; // 1 = front, 2 = middle + // 3 = end + USHORT nTimePos; // Index of first time separator (+1) + short nDecPos; // Index of substring containing "," (+1) + short nNegCheck; // '( )' for negative + short nESign; // Sign of exponent + short nAmPm; // +1 AM, -1 PM, 0 if none + short nLogical; // -1 => False, 1 => True + USHORT nThousand; // Count of group (AKA thousand) separators + USHORT nPosThousandString; // Position of concatenaded 000,000,000 string + short eScannedType; // Scanned type + short eSetType; // Preset Type + + USHORT nStringScanNumFor; // Fixed strings recognized in + // pFormat->NumFor[nNumForStringScan] + short nStringScanSign; // Sign resulting of FixString + USHORT nYear2000; // Two-digit threshold + // Year as 20xx + // default 18 + // number <= nYear2000 => 20xx + // number > nYear2000 => 19xx + USHORT nTimezonePos; // Index of timezone separator (+1) + BYTE nMayBeIso8601; // 0:=dontknowyet, 1:=yes, 2:=no + +#ifdef _ZFORFIND_CXX // methods private to implementation + void Reset(); // Reset all variables before start of analysis + + void InitText(); // Init of months and days of week + + // Convert string to double. + // Only simple unsigned floating point values without any error detection, + // decimal separator has to be '.' + // If bForceFraction==TRUE the string is taken to be the fractional part + // of 0.1234 without the leading 0. (thus being just "1234"). + double StringToDouble( + const String& rStr, + BOOL bForceFraction = FALSE ); + + BOOL NextNumberStringSymbol( // Next number/string symbol + const sal_Unicode*& pStr, + String& rSymbol ); + + BOOL SkipThousands( // Concatenate ,000,23 blocks + const sal_Unicode*& pStr, // in input to 000123 + String& rSymbol ); + + void NumberStringDivision( // Divide numbers/strings into + const String& rString ); // arrays and variables above. + // Leading blanks and blanks + // after numbers are thrown away + + + // optimized substring versions + + static inline BOOL StringContains( // Whether rString contains rWhat at nPos + const String& rWhat, + const String& rString, + xub_StrLen nPos ) + { // mostly used with one character + if ( rWhat.GetChar(0) != rString.GetChar(nPos) ) + return FALSE; + return StringContainsImpl( rWhat, rString, nPos ); + } + static inline BOOL StringPtrContains( // Whether pString contains rWhat at nPos + const String& rWhat, + const sal_Unicode* pString, + xub_StrLen nPos ) // nPos MUST be a valid offset from pString + { // mostly used with one character + if ( rWhat.GetChar(0) != *(pString+nPos) ) + return FALSE; + return StringPtrContainsImpl( rWhat, pString, nPos ); + } + static BOOL StringContainsImpl( //! DO NOT use directly + const String& rWhat, + const String& rString, + xub_StrLen nPos ); + static BOOL StringPtrContainsImpl( //! DO NOT use directly + const String& rWhat, + const sal_Unicode* pString, + xub_StrLen nPos ); + + + static inline BOOL SkipChar( // Skip a special character + sal_Unicode c, + const String& rString, + xub_StrLen& nPos ); + static inline void SkipBlanks( // Skip blank + const String& rString, + xub_StrLen& nPos ); + static inline BOOL SkipString( // Jump over rWhat in rString at nPos + const String& rWhat, + const String& rString, + xub_StrLen& nPos ); + + inline BOOL GetThousandSep( // Recognizes exactly ,111 as group separator + const String& rString, + xub_StrLen& nPos, + USHORT nStringPos ); + short GetLogical( // Get boolean value + const String& rString ); + short GetMonth( // Get month and advance string position + const String& rString, + xub_StrLen& nPos ); + int GetDayOfWeek( // Get day of week and advance string position + const String& rString, + xub_StrLen& nPos ); + BOOL GetCurrency( // Get currency symbol and advance string position + const String& rString, + xub_StrLen& nPos, + const SvNumberformat* pFormat = NULL ); // optional number format to match against + BOOL GetTimeAmPm( // Get symbol AM or PM and advance string position + const String& rString, + xub_StrLen& nPos ); + inline BOOL GetDecSep( // Get decimal separator and advance string position + const String& rString, + xub_StrLen& nPos ); + inline BOOL GetTime100SecSep( // Get hundredth seconds separator and advance string position + const String& rString, + xub_StrLen& nPos ); + int GetSign( // Get sign and advance string position + const String& rString, // Including special case '(' + xub_StrLen& nPos ); + short GetESign( // Get sign of exponent and advance string position + const String& rString, + xub_StrLen& nPos ); + + inline BOOL GetNextNumber( // Get next number as array offset + USHORT& i, + USHORT& j ); + + void GetTimeRef( // Converts time -> double (only decimals) + double& fOutNumber, // result as double + USHORT nIndex, // Index of hour in input + USHORT nAnz ); // Count of time substrings in input + USHORT ImplGetDay ( USHORT nIndex ); // Day input, 0 if no match + USHORT ImplGetMonth( USHORT nIndex ); // Month input, zero based return, NumberOfMonths if no match + USHORT ImplGetYear ( USHORT nIndex ); // Year input, 0 if no match + BOOL GetDateRef( // Conversion of date to number + double& fDays, // OUT: days diff to null date + USHORT& nCounter, // Count of date substrings + const SvNumberformat* pFormat = NULL ); // optional number format to match against + + BOOL ScanStartString( // Analyze start of string + const String& rString, + const SvNumberformat* pFormat = NULL ); + BOOL ScanMidString( // Analyze middle substring + const String& rString, + USHORT nStringPos, + const SvNumberformat* pFormat = NULL ); + BOOL ScanEndString( // Analyze end of string + const String& rString, + const SvNumberformat* pFormat = NULL ); + + // Whether input may be a ISO 8601 date format, yyyy-mm-dd... + // checks if at least 3 numbers and first number>31 + bool MayBeIso8601(); + + // Compare rString to substring of array indexed by nString + // nString == 0xFFFF => last substring + BOOL ScanStringNumFor( + const String& rString, + xub_StrLen nPos, + const SvNumberformat* pFormat, + USHORT nString, + BOOL bDontDetectNegation = FALSE ); + + // if nMatchedAllStrings set nMatchedUsedAsReturn and return TRUE, + // else do nothing and return FALSE + BOOL MatchedReturn(); + + //! Be sure that the string to be analyzed is already converted to upper + //! case and if it contained native humber digits that they are already + //! converted to ASCII. + BOOL IsNumberFormatMain( // Main anlyzing function + const String& rString, + double& fOutNumber, // return value if string is numeric + const SvNumberformat* pFormat = NULL // optional number format to match against + ); + + static inline BOOL MyIsdigit( sal_Unicode c ); + + // native number transliteration if necessary + void TransformInput( String& rString ); + +#endif // _ZFORFIND_CXX +}; + + + +#endif // _ZFORFIND_HXX diff --git a/svl/source/numbers/zforlist.cxx b/svl/source/numbers/zforlist.cxx new file mode 100644 index 000000000000..f03ef3f31140 --- /dev/null +++ b/svl/source/numbers/zforlist.cxx @@ -0,0 +1,4348 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zforlist.cxx,v $ + * $Revision: 1.72.60.2 $ + * + * 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_svl.hxx" +#ifndef GCC +#endif + +// #include <math.h> +#include <tools/debug.hxx> +#include <unotools/charclass.hxx> +#include <i18npool/mslangid.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/numberformatcodewrapper.hxx> +#include <unotools/calendarwrapper.hxx> +#include <com/sun/star/i18n/KNumberFormatUsage.hpp> +#include <com/sun/star/i18n/KNumberFormatType.hpp> +#include <comphelper/processfactory.hxx> +#include <unotools/misccfg.hxx> + +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> + +#define _ZFORLIST_CXX +#include <osl/mutex.hxx> +#include <svl/zforlist.hxx> +#undef _ZFORLIST_CXX + +#include "zforscan.hxx" +#include "zforfind.hxx" +#include <svl/zformat.hxx> +#include "numhead.hxx" + +#include <unotools/syslocaleoptions.hxx> +#include <unotools/digitgroupingiterator.hxx> +#include <rtl/logfile.hxx> +#include <rtl/instance.hxx> + +#include <math.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; + + +// Constants for type offsets per Country/Language (CL) +#define ZF_STANDARD 0 +#define ZF_STANDARD_PERCENT 10 +#define ZF_STANDARD_CURRENCY 20 +#define ZF_STANDARD_DATE 30 +#define ZF_STANDARD_TIME 40 +#define ZF_STANDARD_DATETIME 50 +#define ZF_STANDARD_SCIENTIFIC 60 +#define ZF_STANDARD_FRACTION 70 +#define ZF_STANDARD_NEWEXTENDED 75 +#define ZF_STANDARD_NEWEXTENDEDMAX SV_MAX_ANZ_STANDARD_FORMATE-2 // 98 +#define ZF_STANDARD_LOGICAL SV_MAX_ANZ_STANDARD_FORMATE-1 // 99 +#define ZF_STANDARD_TEXT SV_MAX_ANZ_STANDARD_FORMATE // 100 + +/* Locale that is set if an unknown locale (from another system) is loaded of + * legacy documents. Can not be SYSTEM because else, for example, a German "DM" + * (old currency) is recognized as a date (#53155#). */ +#define UNKNOWN_SUBSTITUTE LANGUAGE_ENGLISH_US + +static BOOL bIndexTableInitialized = FALSE; +static sal_uInt32 __FAR_DATA theIndexTable[NF_INDEX_TABLE_ENTRIES]; + + +// ==================================================================== + +/** + instead of every number formatter being a listener we have a registry which + also handles one instance of the SysLocale options + */ + +class SvNumberFormatterRegistry_Impl : public utl::ConfigurationListener +{ + List aFormatters; + SvtSysLocaleOptions aSysLocaleOptions; + LanguageType eSysLanguage; + +public: + SvNumberFormatterRegistry_Impl(); + virtual ~SvNumberFormatterRegistry_Impl(); + + void Insert( SvNumberFormatter* pThis ) + { aFormatters.Insert( pThis, LIST_APPEND ); } + SvNumberFormatter* Remove( SvNumberFormatter* pThis ) + { return (SvNumberFormatter*)aFormatters.Remove( pThis ); } + sal_uInt32 Count() + { return aFormatters.Count(); } + + virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 ); +}; + + +SvNumberFormatterRegistry_Impl::SvNumberFormatterRegistry_Impl() +{ + eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM ); + aSysLocaleOptions.AddListener( this ); +} + + +SvNumberFormatterRegistry_Impl::~SvNumberFormatterRegistry_Impl() +{ + aSysLocaleOptions.RemoveListener( this ); +} + + +void SvNumberFormatterRegistry_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 nHint ) +{ + if ( nHint & SYSLOCALEOPTIONS_HINT_LOCALE ) + { + ::osl::MutexGuard aGuard( SvNumberFormatter::GetMutex() ); + for ( SvNumberFormatter* p = (SvNumberFormatter*)aFormatters.First(); + p; p = (SvNumberFormatter*)aFormatters.Next() ) + { + p->ReplaceSystemCL( eSysLanguage ); + } + eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM ); + } + if ( nHint & SYSLOCALEOPTIONS_HINT_CURRENCY ) + { + ::osl::MutexGuard aGuard( SvNumberFormatter::GetMutex() ); + for ( SvNumberFormatter* p = (SvNumberFormatter*)aFormatters.First(); + p; p = (SvNumberFormatter*)aFormatters.Next() ) + { + p->ResetDefaultSystemCurrency(); + } + } +} + + +// ==================================================================== + +SvNumberFormatterRegistry_Impl* SvNumberFormatter::pFormatterRegistry = NULL; +BOOL SvNumberFormatter::bCurrencyTableInitialized = FALSE; +namespace +{ + struct theCurrencyTable : + public rtl::Static< NfCurrencyTable, theCurrencyTable > {}; + + struct theLegacyOnlyCurrencyTable : + public rtl::Static< NfCurrencyTable, theLegacyOnlyCurrencyTable > {}; +} +USHORT SvNumberFormatter::nSystemCurrencyPosition = 0; +SV_IMPL_PTRARR( NfCurrencyTable, NfCurrencyEntry* ); +SV_IMPL_PTRARR( NfWSStringsDtor, String* ); + +// ob das BankSymbol immer am Ende ist (1 $;-1 $) oder sprachabhaengig +#define NF_BANKSYMBOL_FIX_POSITION 1 + + +/***********************Funktionen SvNumberFormatter**************************/ + +SvNumberFormatter::SvNumberFormatter( + const Reference< XMultiServiceFactory >& xSMgr, + LanguageType eLang ) + : + xServiceManager( xSMgr ) +{ + ImpConstruct( eLang ); +} + + +SvNumberFormatter::SvNumberFormatter( LanguageType eLang ) +{ + ImpConstruct( eLang ); +} + + +SvNumberFormatter::~SvNumberFormatter() +{ + { + ::osl::MutexGuard aGuard( GetMutex() ); + pFormatterRegistry->Remove( this ); + if ( !pFormatterRegistry->Count() ) + { + delete pFormatterRegistry; + pFormatterRegistry = NULL; + } + } + + SvNumberformat* pEntry = aFTable.First(); + while (pEntry) + { + delete pEntry; + pEntry = aFTable.Next(); + } + delete pFormatTable; + delete pCharClass; + delete pStringScanner; + delete pFormatScanner; + ClearMergeTable(); + delete pMergeTable; +} + + +void SvNumberFormatter::ImpConstruct( LanguageType eLang ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aTimeLog, "svl", "er93726", "SvNumberFormatter::ImpConstruct" ); + + if ( eLang == LANGUAGE_DONTKNOW ) + eLang = UNKNOWN_SUBSTITUTE; + IniLnge = eLang; + ActLnge = eLang; + eEvalDateFormat = NF_EVALDATEFORMAT_INTL; + nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; + + aLocale = MsLangId::convertLanguageToLocale( eLang ); + pCharClass = new CharClass( xServiceManager, aLocale ); + xLocaleData.init( xServiceManager, aLocale, eLang ); + xCalendar.init( xServiceManager, aLocale ); + xTransliteration.init( xServiceManager, eLang, + ::com::sun::star::i18n::TransliterationModules_IGNORE_CASE ); + xNatNum.init( xServiceManager ); + + // cached locale data items + const LocaleDataWrapper* pLoc = GetLocaleData(); + aDecimalSep = pLoc->getNumDecimalSep(); + aThousandSep = pLoc->getNumThousandSep(); + aDateSep = pLoc->getDateSep(); + + pStringScanner = new ImpSvNumberInputScan( this ); + pFormatScanner = new ImpSvNumberformatScan( this ); + pFormatTable = NULL; + MaxCLOffset = 0; + ImpGenerateFormats( 0, FALSE ); // 0 .. 999 for initialized language formats + pMergeTable = NULL; + bNoZero = FALSE; + + ::osl::MutexGuard aGuard( GetMutex() ); + GetFormatterRegistry().Insert( this ); +} + + +void SvNumberFormatter::ChangeIntl(LanguageType eLnge) +{ + if (ActLnge != eLnge) + { + ActLnge = eLnge; + + aLocale = MsLangId::convertLanguageToLocale( eLnge ); + pCharClass->setLocale( aLocale ); + xLocaleData.changeLocale( aLocale, eLnge ); + xCalendar.changeLocale( aLocale ); + xTransliteration.changeLocale( eLnge ); + + // cached locale data items, initialize BEFORE calling ChangeIntl below + const LocaleDataWrapper* pLoc = GetLocaleData(); + aDecimalSep = pLoc->getNumDecimalSep(); + aThousandSep = pLoc->getNumThousandSep(); + aDateSep = pLoc->getDateSep(); + + pFormatScanner->ChangeIntl(); + pStringScanner->ChangeIntl(); + } +} + + +// static +::osl::Mutex& SvNumberFormatter::GetMutex() +{ + static ::osl::Mutex* pMutex = NULL; + if( !pMutex ) + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + if( !pMutex ) + { + // #i77768# Due to a static reference in the toolkit lib + // we need a mutex that lives longer than the svl library. + // Otherwise the dtor would use a destructed mutex!! + pMutex = new ::osl::Mutex; + } + } + return *pMutex; +} + + +// static +SvNumberFormatterRegistry_Impl& SvNumberFormatter::GetFormatterRegistry() +{ + ::osl::MutexGuard aGuard( GetMutex() ); + if ( !pFormatterRegistry ) + pFormatterRegistry = new SvNumberFormatterRegistry_Impl; + return *pFormatterRegistry; +} + + +Color* SvNumberFormatter::GetUserDefColor(USHORT nIndex) +{ + if( aColorLink.IsSet() ) + return (Color*) ( aColorLink.Call( (void*) &nIndex )); + else + return NULL; +} + +void SvNumberFormatter::ChangeNullDate(USHORT nDay, + USHORT nMonth, + USHORT nYear) +{ + pFormatScanner->ChangeNullDate(nDay, nMonth, nYear); + pStringScanner->ChangeNullDate(nDay, nMonth, nYear); +} + +Date* SvNumberFormatter::GetNullDate() +{ + return pFormatScanner->GetNullDate(); +} + +void SvNumberFormatter::ChangeStandardPrec(short nPrec) +{ + pFormatScanner->ChangeStandardPrec(nPrec); +} + +short SvNumberFormatter::GetStandardPrec() +{ + return pFormatScanner->GetStandardPrec(); +} + +void SvNumberFormatter::ImpChangeSysCL( LanguageType eLnge, BOOL bLoadingSO5 ) +{ + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = UNKNOWN_SUBSTITUTE; + if (eLnge != IniLnge) + { + IniLnge = eLnge; + ChangeIntl(eLnge); + SvNumberformat* pEntry = aFTable.First(); + while (pEntry) // delete old formats + { + pEntry = (SvNumberformat*) aFTable.Remove(aFTable.GetCurKey()); + delete pEntry; + pEntry = (SvNumberformat*) aFTable.First(); + } + ImpGenerateFormats( 0, bLoadingSO5 ); // new standard formats + } + else if ( bLoadingSO5 ) + { // delete additional standard formats + sal_uInt32 nKey; + aFTable.Seek( SV_MAX_ANZ_STANDARD_FORMATE + 1 ); + while ( (nKey = aFTable.GetCurKey()) > SV_MAX_ANZ_STANDARD_FORMATE && + nKey < SV_COUNTRY_LANGUAGE_OFFSET ) + { + SvNumberformat* pEntry = (SvNumberformat*) aFTable.Remove( nKey ); + delete pEntry; + } + } +} + + +void SvNumberFormatter::ReplaceSystemCL( LanguageType eOldLanguage ) +{ + sal_uInt32 nCLOffset = ImpGetCLOffset( LANGUAGE_SYSTEM ); + if ( nCLOffset > MaxCLOffset ) + return ; // no SYSTEM entries to replace + + const sal_uInt32 nMaxBuiltin = nCLOffset + SV_MAX_ANZ_STANDARD_FORMATE; + const sal_uInt32 nNextCL = nCLOffset + SV_COUNTRY_LANGUAGE_OFFSET; + sal_uInt32 nKey; + + // remove old builtin formats + aFTable.Seek( nCLOffset ); + while ( (nKey = aFTable.GetCurKey()) >= nCLOffset && nKey <= nMaxBuiltin && aFTable.Count() ) + { + SvNumberformat* pEntry = (SvNumberformat*) aFTable.Remove( nKey ); + delete pEntry; + } + + // move additional and user defined to temporary table + Table aOldTable; + while ( (nKey = aFTable.GetCurKey()) >= nCLOffset && nKey < nNextCL && aFTable.Count() ) + { + SvNumberformat* pEntry = (SvNumberformat*) aFTable.Remove( nKey ); + aOldTable.Insert( nKey, pEntry ); + } + + // generate new old builtin formats + // reset ActLnge otherwise ChangeIntl() wouldn't switch if already LANGUAGE_SYSTEM + ActLnge = LANGUAGE_DONTKNOW; + ChangeIntl( LANGUAGE_SYSTEM ); + ImpGenerateFormats( nCLOffset, TRUE ); + + // convert additional and user defined from old system to new system + SvNumberformat* pStdFormat = (SvNumberformat*) aFTable.Get( nCLOffset + ZF_STANDARD ); + sal_uInt32 nLastKey = nMaxBuiltin; + pFormatScanner->SetConvertMode( eOldLanguage, LANGUAGE_SYSTEM, TRUE ); + aOldTable.First(); + while ( aOldTable.Count() ) + { + nKey = aOldTable.GetCurKey(); + if ( nLastKey < nKey ) + nLastKey = nKey; + SvNumberformat* pOldEntry = (SvNumberformat*) aOldTable.Remove( nKey ); + String aString( pOldEntry->GetFormatstring() ); + xub_StrLen nCheckPos = STRING_NOTFOUND; + + // Same as PutEntry() but assures key position even if format code is + // a duplicate. Also won't mix up any LastInsertKey. + ChangeIntl( eOldLanguage ); + LanguageType eLge = eOldLanguage; // ConvertMode changes this + BOOL bCheck = FALSE; + SvNumberformat* pNewEntry = new SvNumberformat( aString, pFormatScanner, + pStringScanner, nCheckPos, eLge ); + if ( nCheckPos != 0 ) + delete pNewEntry; + else + { + short eCheckType = pNewEntry->GetType(); + if ( eCheckType != NUMBERFORMAT_UNDEFINED ) + pNewEntry->SetType( eCheckType | NUMBERFORMAT_DEFINED ); + else + pNewEntry->SetType( NUMBERFORMAT_DEFINED ); + + if ( !aFTable.Insert( nKey, pNewEntry ) ) + delete pNewEntry; + else + bCheck = TRUE; + } + DBG_ASSERT( bCheck, "SvNumberFormatter::ReplaceSystemCL: couldn't convert" ); + + delete pOldEntry; + } + pFormatScanner->SetConvertMode(FALSE); + pStdFormat->SetLastInsertKey( USHORT(nLastKey - nCLOffset) ); + + // append new system additional formats + NumberFormatCodeWrapper aNumberFormatCode( xServiceManager, GetLocale() ); + ImpGenerateAdditionalFormats( nCLOffset, aNumberFormatCode, TRUE ); +} + + +BOOL SvNumberFormatter::IsTextFormat(sal_uInt32 F_Index) const +{ + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(F_Index); + if (!pFormat) + return FALSE; + else + return pFormat->IsTextFormat(); +} + +BOOL SvNumberFormatter::HasTextFormat(sal_uInt32 F_Index) const +{ + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(F_Index); + if (!pFormat) + return FALSE; + else + return pFormat->HasTextFormat(); +} + +BOOL SvNumberFormatter::PutEntry(String& rString, + xub_StrLen& nCheckPos, + short& nType, + sal_uInt32& nKey, // Formatnummer + LanguageType eLnge) +{ + nKey = 0; + if (rString.Len() == 0) // keinen Leerstring + { + nCheckPos = 1; // -> Fehler + return FALSE; + } + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + + ChangeIntl(eLnge); // ggfs. austauschen + LanguageType eLge = eLnge; // Umgehung const fuer ConvertMode + BOOL bCheck = FALSE; + SvNumberformat* p_Entry = new SvNumberformat(rString, + pFormatScanner, + pStringScanner, + nCheckPos, + eLge); + if (nCheckPos == 0) // Format ok + { // Typvergleich: + short eCheckType = p_Entry->GetType(); + if ( eCheckType != NUMBERFORMAT_UNDEFINED) + { + p_Entry->SetType(eCheckType | NUMBERFORMAT_DEFINED); + nType = eCheckType; + } + else + { + p_Entry->SetType(NUMBERFORMAT_DEFINED); + nType = NUMBERFORMAT_DEFINED; + } + sal_uInt32 CLOffset = ImpGenerateCL(eLge); // ggfs. neu Standard- + // formate anlegen + nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLge); + if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // schon vorhanden + delete p_Entry; + else + { + SvNumberformat* pStdFormat = + (SvNumberformat*) aFTable.Get(CLOffset + ZF_STANDARD); + sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey(); + if (nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET) + { + DBG_ERROR("SvNumberFormatter:: Zu viele Formate pro CL"); + delete p_Entry; + } + else if (!aFTable.Insert(nPos+1,p_Entry)) + delete p_Entry; + else + { + bCheck = TRUE; + nKey = nPos+1; + pStdFormat->SetLastInsertKey((USHORT) (nKey-CLOffset)); + } + } + } + else + delete p_Entry; + return bCheck; +} + +BOOL SvNumberFormatter::PutandConvertEntry(String& rString, + xub_StrLen& nCheckPos, + short& nType, + sal_uInt32& nKey, + LanguageType eLnge, + LanguageType eNewLnge) +{ + BOOL bRes; + if (eNewLnge == LANGUAGE_DONTKNOW) + eNewLnge = IniLnge; + + pFormatScanner->SetConvertMode(eLnge, eNewLnge); + bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge); + pFormatScanner->SetConvertMode(FALSE); + return bRes; +} + + +BOOL SvNumberFormatter::PutandConvertEntrySystem(String& rString, + xub_StrLen& nCheckPos, + short& nType, + sal_uInt32& nKey, + LanguageType eLnge, + LanguageType eNewLnge) +{ + BOOL bRes; + if (eNewLnge == LANGUAGE_DONTKNOW) + eNewLnge = IniLnge; + + pFormatScanner->SetConvertMode(eLnge, eNewLnge, TRUE); + bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge); + pFormatScanner->SetConvertMode(FALSE); + return bRes; +} + + +sal_uInt32 SvNumberFormatter::GetIndexPuttingAndConverting( String & rString, + LanguageType eLnge, LanguageType eSysLnge, short & rType, + BOOL & rNewInserted, xub_StrLen & rCheckPos ) +{ + sal_uInt32 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND; + rNewInserted = FALSE; + rCheckPos = 0; + + // #62389# empty format string (of Writer) => General standard format + if (!rString.Len()) + ; // nothing + else if (eLnge == LANGUAGE_SYSTEM && eSysLnge != SvtSysLocale().GetLanguage()) + { + sal_uInt32 nOrig = GetEntryKey( rString, eSysLnge ); + if (nOrig == NUMBERFORMAT_ENTRY_NOT_FOUND) + nKey = nOrig; // none avaliable, maybe user-defined + else + nKey = GetFormatForLanguageIfBuiltIn( nOrig, SvtSysLocale().GetLanguage() ); + + if (nKey == nOrig) + { + // Not a builtin format, convert. + // The format code string may get modified and adapted to the real + // language and wouldn't match eSysLnge anymore, do that on a copy. + String aTmp( rString); + rNewInserted = PutandConvertEntrySystem( aTmp, rCheckPos, rType, + nKey, eLnge, SvtSysLocale().GetLanguage()); + if (rCheckPos > 0) + { + DBG_ERRORFILE("SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for current locale"); + nKey = NUMBERFORMAT_ENTRY_NOT_FOUND; + } + } + } + else + { + nKey = GetEntryKey( rString, eLnge); + if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + rNewInserted = PutEntry( rString, rCheckPos, rType, nKey, eLnge); + if (rCheckPos > 0) + { + DBG_ERRORFILE("SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for specified locale"); + nKey = NUMBERFORMAT_ENTRY_NOT_FOUND; + } + } + } + if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) + nKey = GetStandardIndex( eLnge); + rType = GetType( nKey); + // Convert any (!) old "automatic" currency format to new fixed currency + // default format. + if ((rType & NUMBERFORMAT_CURRENCY) != 0) + { + const SvNumberformat* pFormat = GetEntry( nKey); + if (!pFormat->HasNewCurrency()) + { + if (rNewInserted) + { + DeleteEntry( nKey); // don't leave trails of rubbish + rNewInserted = FALSE; + } + nKey = GetStandardFormat( NUMBERFORMAT_CURRENCY, eLnge); + } + } + return nKey; +} + + +void SvNumberFormatter::DeleteEntry(sal_uInt32 nKey) +{ + SvNumberformat* pEntry = aFTable.Remove(nKey); + delete pEntry; +} + +void SvNumberFormatter::PrepareSave() +{ + SvNumberformat* pFormat = aFTable.First(); + while (pFormat) + { + pFormat->SetUsed(FALSE); + pFormat = aFTable.Next(); + } +} + +void SvNumberFormatter::SetFormatUsed(sal_uInt32 nFIndex) +{ + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex); + if (pFormat) + pFormat->SetUsed(TRUE); +} + +BOOL SvNumberFormatter::Load( SvStream& rStream ) +{ + LanguageType eSysLang = SvtSysLocale().GetLanguage(); + SvNumberFormatter* pConverter = NULL; + + ImpSvNumMultipleReadHeader aHdr( rStream ); + USHORT nVersion; + rStream >> nVersion; + SvNumberformat* pEntry; + sal_uInt32 nPos; + LanguageType eSaveSysLang, eLoadSysLang; + USHORT nSysOnStore, eLge, eDummy; // Dummy fuer kompatibles Format + rStream >> nSysOnStore >> eLge; // Systemeinstellung aus + // Dokument + eSaveSysLang = (nVersion < SV_NUMBERFORMATTER_VERSION_SYSTORE ? + LANGUAGE_SYSTEM : (LanguageType) nSysOnStore); + LanguageType eLnge = (LanguageType) eLge; + ImpChangeSysCL( eLnge, TRUE ); + + rStream >> nPos; + while (nPos != NUMBERFORMAT_ENTRY_NOT_FOUND) + { + rStream >> eDummy >> eLge; + eLnge = (LanguageType) eLge; + ImpGenerateCL( eLnge, TRUE ); // ggfs. neue Standardformate anlegen + + sal_uInt32 nOffset = nPos % SV_COUNTRY_LANGUAGE_OFFSET; // relativIndex + BOOL bUserDefined = (nOffset > SV_MAX_ANZ_STANDARD_FORMATE); + //! HACK! ER 29.07.97 15:15 + // SaveLang wurde bei SYSTEM nicht gespeichert sondern war auch SYSTEM, + // erst ab 364i Unterscheidung moeglich + BOOL bConversionHack; + if ( eLnge == LANGUAGE_SYSTEM ) + { + if ( nVersion < SV_NUMBERFORMATTER_VERSION_SYSTORE ) + { + bConversionHack = bUserDefined; + eLoadSysLang = eSaveSysLang; + } + else + { + bConversionHack = FALSE; + eLoadSysLang = eSysLang; + } + } + else + { + bConversionHack = FALSE; + eLoadSysLang = eSaveSysLang; + } + + pEntry = new SvNumberformat(*pFormatScanner, eLnge); + if ( bConversionHack ) + { // SYSTEM + // nVersion < SV_NUMBERFORMATTER_VERSION_SYSTORE + // nVersion < SV_NUMBERFORMATTER_VERSION_KEYWORDS + if ( !pConverter ) + pConverter = new SvNumberFormatter( xServiceManager, eSysLang ); + NfHackConversion eHackConversion = pEntry->Load( + rStream, aHdr, pConverter, *pStringScanner ); + switch ( eHackConversion ) + { + case NF_CONVERT_GERMAN_ENGLISH : + pEntry->ConvertLanguage( *pConverter, + LANGUAGE_ENGLISH_US, eSysLang, TRUE ); + break; + case NF_CONVERT_ENGLISH_GERMAN : + switch ( eSysLang ) + { + case LANGUAGE_GERMAN: + case LANGUAGE_GERMAN_SWISS: + case LANGUAGE_GERMAN_AUSTRIAN: + case LANGUAGE_GERMAN_LUXEMBOURG: + case LANGUAGE_GERMAN_LIECHTENSTEIN: + // alles beim alten + break; + default: + pEntry->ConvertLanguage( *pConverter, + LANGUAGE_GERMAN, eSysLang, TRUE ); + } + break; + case NF_CONVERT_NONE : + break; // -Wall not handled. + } + + } + else + { + pEntry->Load( rStream, aHdr, NULL, *pStringScanner ); + if ( !bUserDefined ) + bUserDefined = (pEntry->GetNewStandardDefined() > SV_NUMBERFORMATTER_VERSION); + if ( bUserDefined ) + { + if ( eSaveSysLang != eLoadSysLang ) + { // SYSTEM verschieden + if ( !pConverter ) + pConverter = new SvNumberFormatter( xServiceManager, eSysLang ); + if ( nVersion < SV_NUMBERFORMATTER_VERSION_KEYWORDS ) + { + switch ( eSaveSysLang ) + { + case LANGUAGE_GERMAN: + case LANGUAGE_GERMAN_SWISS: + case LANGUAGE_GERMAN_AUSTRIAN: + case LANGUAGE_GERMAN_LUXEMBOURG: + case LANGUAGE_GERMAN_LIECHTENSTEIN: + // alles beim alten + pEntry->ConvertLanguage( *pConverter, + eSaveSysLang, eLoadSysLang, TRUE ); + break; + default: + // alte english nach neuem anderen + pEntry->ConvertLanguage( *pConverter, + LANGUAGE_ENGLISH_US, eLoadSysLang, TRUE ); + } + } + else + pEntry->ConvertLanguage( *pConverter, + eSaveSysLang, eLoadSysLang, TRUE ); + } + else + { // nicht SYSTEM oder gleiches SYSTEM + if ( nVersion < SV_NUMBERFORMATTER_VERSION_KEYWORDS ) + { + LanguageType eLoadLang; + BOOL bSystem; + if ( eLnge == LANGUAGE_SYSTEM ) + { + eLoadLang = eSysLang; + bSystem = TRUE; + } + else + { + eLoadLang = eLnge; + bSystem = FALSE; + } + switch ( eLoadLang ) + { + case LANGUAGE_GERMAN: + case LANGUAGE_GERMAN_SWISS: + case LANGUAGE_GERMAN_AUSTRIAN: + case LANGUAGE_GERMAN_LUXEMBOURG: + case LANGUAGE_GERMAN_LIECHTENSTEIN: + // alles beim alten + break; + default: + // alte english nach neuem anderen + if ( !pConverter ) + pConverter = new SvNumberFormatter( xServiceManager, eSysLang ); + pEntry->ConvertLanguage( *pConverter, + LANGUAGE_ENGLISH_US, eLoadLang, bSystem ); + } + } + } + } + } + if ( nOffset == 0 ) // StandardFormat + { + SvNumberformat* pEnt = aFTable.Get(nPos); + if (pEnt) + pEnt->SetLastInsertKey(pEntry->GetLastInsertKey()); + } + if (!aFTable.Insert(nPos, pEntry)) + delete pEntry; + rStream >> nPos; + } + + // ab SV_NUMBERFORMATTER_VERSION_YEAR2000 + if ( nVersion >= SV_NUMBERFORMATTER_VERSION_YEAR2000 ) + { + aHdr.StartEntry(); + if ( aHdr.BytesLeft() >= sizeof(UINT16) ) + { + UINT16 nY2k; + rStream >> nY2k; + if ( nVersion < SV_NUMBERFORMATTER_VERSION_TWODIGITYEAR && nY2k < 100 ) + nY2k += 1901; // war vor src513e: 29, jetzt: 1930 + SetYear2000( nY2k ); + } + aHdr.EndEntry(); + } + + if ( pConverter ) + delete pConverter; + + // generate additional i18n standard formats for all used locales + LanguageType eOldLanguage = ActLnge; + NumberFormatCodeWrapper aNumberFormatCode( xServiceManager, GetLocale() ); + SvUShorts aList; + GetUsedLanguages( aList ); + USHORT nCount = aList.Count(); + for ( USHORT j=0; j<nCount; j++ ) + { + LanguageType eLang = aList[j]; + ChangeIntl( eLang ); + sal_uInt32 CLOffset = ImpGetCLOffset( eLang ); + ImpGenerateAdditionalFormats( CLOffset, aNumberFormatCode, TRUE ); + } + ChangeIntl( eOldLanguage ); + + if (rStream.GetError()) + return FALSE; + else + return TRUE; +} + +BOOL SvNumberFormatter::Save( SvStream& rStream ) const +{ + ImpSvNumMultipleWriteHeader aHdr( rStream ); + // ab 364i wird gespeichert was SYSTEM wirklich war, vorher hart LANGUAGE_SYSTEM + rStream << (USHORT) SV_NUMBERFORMATTER_VERSION; + rStream << (USHORT) SvtSysLocale().GetLanguage() << (USHORT) IniLnge; + SvNumberFormatTable* pTable = (SvNumberFormatTable*) &aFTable; + SvNumberformat* pEntry = (SvNumberformat*) pTable->First(); + while (pEntry) + { + // Gespeichert werden alle markierten, benutzerdefinierten Formate und + // jeweils das Standardformat zu allen angewaehlten CL-Kombinationen + // sowie NewStandardDefined + if ( pEntry->GetUsed() || (pEntry->GetType() & NUMBERFORMAT_DEFINED) || + pEntry->GetNewStandardDefined() || + (pTable->GetCurKey() % SV_COUNTRY_LANGUAGE_OFFSET == 0) ) + { + rStream << static_cast<sal_uInt32>(pTable->GetCurKey()) + << (USHORT) LANGUAGE_SYSTEM + << (USHORT) pEntry->GetLanguage(); + pEntry->Save(rStream, aHdr); + } + pEntry = (SvNumberformat*) pTable->Next(); + } + rStream << NUMBERFORMAT_ENTRY_NOT_FOUND; // EndeKennung + + // ab SV_NUMBERFORMATTER_VERSION_YEAR2000 + aHdr.StartEntry(); + rStream << (UINT16) GetYear2000(); + aHdr.EndEntry(); + + if (rStream.GetError()) + return FALSE; + else + return TRUE; +} + +// static +void SvNumberFormatter::SkipNumberFormatterInStream( SvStream& rStream ) +{ + ImpSvNumMultipleReadHeader::Skip( rStream ); +} + +void SvNumberFormatter::GetUsedLanguages( SvUShorts& rList ) +{ + rList.Remove( 0, rList.Count() ); + + sal_uInt32 nOffset = 0; + while (nOffset <= MaxCLOffset) + { + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nOffset); + if (pFormat) + rList.Insert( pFormat->GetLanguage(), rList.Count() ); + nOffset += SV_COUNTRY_LANGUAGE_OFFSET; + } +} + + +void SvNumberFormatter::FillKeywordTable( NfKeywordTable& rKeywords, + LanguageType eLang ) +{ + ChangeIntl( eLang ); + const String* pTable = pFormatScanner->GetKeywords(); + for ( USHORT i = 0; i < NF_KEYWORD_ENTRIES_COUNT; ++i ) + { + rKeywords[i] = pTable[i]; + } +} + + +String SvNumberFormatter::GetKeyword( LanguageType eLnge, USHORT nIndex ) +{ + ChangeIntl(eLnge); + const String* pTable = pFormatScanner->GetKeywords(); + if ( pTable && nIndex < NF_KEYWORD_ENTRIES_COUNT ) + return pTable[nIndex]; + + DBG_ERROR("GetKeyword: invalid index"); + return String(); +} + + +String SvNumberFormatter::GetStandardName( LanguageType eLnge ) +{ + ChangeIntl( eLnge ); + return pFormatScanner->GetStandardName(); +} + + +sal_uInt32 SvNumberFormatter::ImpGetCLOffset(LanguageType eLnge) const +{ + SvNumberformat* pFormat; + sal_uInt32 nOffset = 0; + while (nOffset <= MaxCLOffset) + { + pFormat = (SvNumberformat*) aFTable.Get(nOffset); + if (pFormat && pFormat->GetLanguage() == eLnge) + return nOffset; + nOffset += SV_COUNTRY_LANGUAGE_OFFSET; + } + return nOffset; +} + +sal_uInt32 SvNumberFormatter::ImpIsEntry(const String& rString, + sal_uInt32 nCLOffset, + LanguageType eLnge) +{ +#ifndef NF_COMMENT_IN_FORMATSTRING +#error NF_COMMENT_IN_FORMATSTRING not defined (zformat.hxx) +#endif +#if NF_COMMENT_IN_FORMATSTRING + String aStr( rString ); + SvNumberformat::EraseComment( aStr ); +#endif + sal_uInt32 res = NUMBERFORMAT_ENTRY_NOT_FOUND; + SvNumberformat* pEntry; + pEntry = (SvNumberformat*) aFTable.Seek(nCLOffset); + while ( res == NUMBERFORMAT_ENTRY_NOT_FOUND && + pEntry && pEntry->GetLanguage() == eLnge ) + { +#if NF_COMMENT_IN_FORMATSTRING + if ( pEntry->GetComment().Len() ) + { + String aFormat( pEntry->GetFormatstring() ); + SvNumberformat::EraseComment( aFormat ); + if ( aStr == aFormat ) + res = aFTable.GetCurKey(); + else + pEntry = (SvNumberformat*) aFTable.Next(); + } + else + { + if ( aStr == pEntry->GetFormatstring() ) + res = aFTable.GetCurKey(); + else + pEntry = (SvNumberformat*) aFTable.Next(); + } +#else + if ( rString == pEntry->GetFormatstring() ) + res = aFTable.GetCurKey(); + else + pEntry = (SvNumberformat*) aFTable.Next(); +#endif + } + return res; +} + + +SvNumberFormatTable& SvNumberFormatter::GetFirstEntryTable( + short& eType, + sal_uInt32& FIndex, + LanguageType& rLnge) +{ + short eTypetmp = eType; + if (eType == NUMBERFORMAT_ALL) // Leere Zelle oder don't care + rLnge = IniLnge; + else + { + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(FIndex); + if (!pFormat) + { +// DBG_ERROR("SvNumberFormatter:: Unbekanntes altes Zahlformat (1)"); + rLnge = IniLnge; + eType = NUMBERFORMAT_ALL; + eTypetmp = eType; + } + else + { + rLnge = pFormat->GetLanguage(); + eType = pFormat->GetType()&~NUMBERFORMAT_DEFINED; + if (eType == 0) + { + eType = NUMBERFORMAT_DEFINED; + eTypetmp = eType; + } + else if (eType == NUMBERFORMAT_DATETIME) + { + eTypetmp = eType; + eType = NUMBERFORMAT_DATE; + } + else + eTypetmp = eType; + } + } + ChangeIntl(rLnge); + return GetEntryTable(eTypetmp, FIndex, rLnge); +} + +sal_uInt32 SvNumberFormatter::ImpGenerateCL( LanguageType eLnge, BOOL bLoadingSO5 ) +{ + ChangeIntl(eLnge); + sal_uInt32 CLOffset = ImpGetCLOffset(ActLnge); + if (CLOffset > MaxCLOffset) + { // new CL combination + if (LocaleDataWrapper::areChecksEnabled()) + { + Locale aLoadedLocale = xLocaleData->getLoadedLocale(); + if ( aLoadedLocale.Language != aLocale.Language || + aLoadedLocale.Country != aLocale.Country ) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumerFormatter::ImpGenerateCL: locales don't match:")); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg )); + } + // test XML locale data FormatElement entries + { + uno::Sequence< i18n::FormatElement > xSeq = + xLocaleData->getAllFormats(); + // A test for completeness of formatindex="0" ... + // formatindex="47" is not needed here since it is done in + // ImpGenerateFormats(). + + // Test for dupes of formatindex="..." + for ( sal_Int32 j = 0; j < xSeq.getLength(); j++ ) + { + sal_Int16 nIdx = xSeq[j].formatIndex; + String aDupes; + for ( sal_Int32 i = 0; i < xSeq.getLength(); i++ ) + { + if ( i != j && xSeq[i].formatIndex == nIdx ) + { + aDupes += String::CreateFromInt32( i ); + aDupes += '('; + aDupes += String( xSeq[i].formatKey ); + aDupes += ')'; + aDupes += ' '; + } + } + if ( aDupes.Len() ) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "XML locale data FormatElement formatindex dupe: ")); + aMsg += String::CreateFromInt32( nIdx ); + aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( + "\nFormatElements: ")); + aMsg += String::CreateFromInt32( j ); + aMsg += '('; + aMsg += String( xSeq[j].formatKey ); + aMsg += ')'; + aMsg += ' '; + aMsg += aDupes; + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg )); + } + } + } + } + + MaxCLOffset += SV_COUNTRY_LANGUAGE_OFFSET; + ImpGenerateFormats( MaxCLOffset, bLoadingSO5 ); + CLOffset = MaxCLOffset; + } + return CLOffset; +} + +SvNumberFormatTable& SvNumberFormatter::ChangeCL(short eType, + sal_uInt32& FIndex, + LanguageType eLnge) +{ + ImpGenerateCL(eLnge); + return GetEntryTable(eType, FIndex, ActLnge); +} + +SvNumberFormatTable& SvNumberFormatter::GetEntryTable( + short eType, + sal_uInt32& FIndex, + LanguageType eLnge) +{ + if ( pFormatTable ) + pFormatTable->Clear(); + else + pFormatTable = new SvNumberFormatTable; + ChangeIntl(eLnge); + sal_uInt32 CLOffset = ImpGetCLOffset(ActLnge); + + // Might generate and insert a default format for the given type + // (e.g. currency) => has to be done before collecting formats. + sal_uInt32 nDefaultIndex = GetStandardFormat( eType, ActLnge ); + + SvNumberformat* pEntry; + pEntry = (SvNumberformat*) aFTable.Seek(CLOffset); + + if (eType == NUMBERFORMAT_ALL) + { + while (pEntry && pEntry->GetLanguage() == ActLnge) + { // copy all entries to output table + pFormatTable->Insert( aFTable.GetCurKey(), pEntry ); + pEntry = (SvNumberformat*) aFTable.Next(); + } + } + else + { + while (pEntry && pEntry->GetLanguage() == ActLnge) + { // copy entries of queried type to output table + if ((pEntry->GetType()) & eType) + pFormatTable->Insert(aFTable.GetCurKey(),pEntry); + pEntry = (SvNumberformat*) aFTable.Next(); + } + } + if ( pFormatTable->Count() > 0 ) + { // select default if queried format doesn't exist or queried type or + // language differ from existing format + pEntry = aFTable.Get(FIndex); + if ( !pEntry || !(pEntry->GetType() & eType) || pEntry->GetLanguage() != ActLnge ) + FIndex = nDefaultIndex; + } + return *pFormatTable; +} + +BOOL SvNumberFormatter::IsNumberFormat(const String& sString, + sal_uInt32& F_Index, + double& fOutNumber) +{ + short FType; + const SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(F_Index); + if (!pFormat) + { +// DBG_ERROR("SvNumberFormatter:: Unbekanntes altes Zahlformat (2)"); + ChangeIntl(IniLnge); + FType = NUMBERFORMAT_NUMBER; + } + else + { + FType = pFormat->GetType() &~NUMBERFORMAT_DEFINED; + if (FType == 0) + FType = NUMBERFORMAT_DEFINED; + ChangeIntl(pFormat->GetLanguage()); + } + BOOL res; + short RType = FType; + // Ergebnistyp + // ohne def-Kennung + if (RType == NUMBERFORMAT_TEXT) // Zahlzelle ->Stringz. + res = FALSE; + else + res = pStringScanner->IsNumberFormat(sString, RType, fOutNumber, pFormat); + + if (res && !IsCompatible(FType, RType)) // unpassender Typ + { + switch ( RType ) + { + case NUMBERFORMAT_TIME : + { + if ( pStringScanner->GetDecPos() ) + { // 100stel Sekunden + if ( pStringScanner->GetAnzNums() > 3 || fOutNumber < 0.0 ) + F_Index = GetFormatIndex( NF_TIME_HH_MMSS00, ActLnge ); + else + F_Index = GetFormatIndex( NF_TIME_MMSS00, ActLnge ); + } + else if ( fOutNumber >= 1.0 || fOutNumber < 0.0 ) + F_Index = GetFormatIndex( NF_TIME_HH_MMSS, ActLnge ); + else + F_Index = GetStandardFormat( RType, ActLnge ); + } + break; + default: + F_Index = GetStandardFormat( RType, ActLnge ); + } + } + return res; +} + +BOOL SvNumberFormatter::IsCompatible(short eOldType, + short eNewType) +{ + if (eOldType == eNewType) + return TRUE; + else if (eOldType == NUMBERFORMAT_DEFINED) + return TRUE; + else + { + switch (eNewType) + { + case NUMBERFORMAT_NUMBER: + { + switch (eOldType) + { + case NUMBERFORMAT_PERCENT: + case NUMBERFORMAT_CURRENCY: + case NUMBERFORMAT_SCIENTIFIC: + case NUMBERFORMAT_FRACTION: +// case NUMBERFORMAT_LOGICAL: + case NUMBERFORMAT_DEFINED: + return TRUE; + default: + return FALSE; + } + } + break; + case NUMBERFORMAT_DATE: + { + switch (eOldType) + { + case NUMBERFORMAT_DATETIME: + return TRUE; + default: + return FALSE; + } + } + break; + case NUMBERFORMAT_TIME: + { + switch (eOldType) + { + case NUMBERFORMAT_DATETIME: + return TRUE; + default: + return FALSE; + } + } + break; + case NUMBERFORMAT_DATETIME: + { + switch (eOldType) + { + case NUMBERFORMAT_TIME: + case NUMBERFORMAT_DATE: + return TRUE; + default: + return FALSE; + } + } + break; + default: + return FALSE; + } + return FALSE; + } +} + + +sal_uInt32 SvNumberFormatter::ImpGetDefaultFormat( short nType ) +{ + sal_uInt32 CLOffset = ImpGetCLOffset( ActLnge ); + sal_uInt32 nSearch; + switch( nType ) + { + case NUMBERFORMAT_DATE : + nSearch = CLOffset + ZF_STANDARD_DATE; + break; + case NUMBERFORMAT_TIME : + nSearch = CLOffset + ZF_STANDARD_TIME; + break; + case NUMBERFORMAT_DATETIME : + nSearch = CLOffset + ZF_STANDARD_DATETIME; + break; + case NUMBERFORMAT_PERCENT : + nSearch = CLOffset + ZF_STANDARD_PERCENT; + break; + case NUMBERFORMAT_SCIENTIFIC: + nSearch = CLOffset + ZF_STANDARD_SCIENTIFIC; + break; + default: + nSearch = CLOffset + ZF_STANDARD; + } + sal_uInt32 nDefaultFormat = (sal_uInt32)(sal_uIntPtr) aDefaultFormatKeys.Get( nSearch ); + if ( !nDefaultFormat ) + nDefaultFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; + if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) + { // look for a defined standard + sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET; + sal_uInt32 nKey; + aFTable.Seek( CLOffset ); + while ( (nKey = aFTable.GetCurKey()) >= CLOffset && nKey < nStopKey ) + { + const SvNumberformat* pEntry = + (const SvNumberformat*) aFTable.GetCurObject(); + if ( pEntry->IsStandard() && ((pEntry->GetType() & + ~NUMBERFORMAT_DEFINED) == nType) ) + { + nDefaultFormat = nKey; + break; // while + } + aFTable.Next(); + } + + if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) + { // none found, use old fixed standards + switch( nType ) + { + case NUMBERFORMAT_DATE : + nDefaultFormat = CLOffset + ZF_STANDARD_DATE; + break; + case NUMBERFORMAT_TIME : + nDefaultFormat = CLOffset + ZF_STANDARD_TIME+1; + break; + case NUMBERFORMAT_DATETIME : + nDefaultFormat = CLOffset + ZF_STANDARD_DATETIME; + break; + case NUMBERFORMAT_PERCENT : + nDefaultFormat = CLOffset + ZF_STANDARD_PERCENT+1; + break; + case NUMBERFORMAT_SCIENTIFIC: + nDefaultFormat = CLOffset + ZF_STANDARD_SCIENTIFIC; + break; + default: + nDefaultFormat = CLOffset + ZF_STANDARD; + } + } + aDefaultFormatKeys.Insert( nSearch, (void*) nDefaultFormat ); + } + return nDefaultFormat; +} + + +sal_uInt32 SvNumberFormatter::GetStandardFormat( short eType, LanguageType eLnge ) +{ + sal_uInt32 CLOffset = ImpGenerateCL(eLnge); + switch(eType) + { + case NUMBERFORMAT_CURRENCY : + { + if ( eLnge == LANGUAGE_SYSTEM ) + return ImpGetDefaultSystemCurrencyFormat(); + else + return ImpGetDefaultCurrencyFormat(); + } + case NUMBERFORMAT_DATE : + case NUMBERFORMAT_TIME : + case NUMBERFORMAT_DATETIME : + case NUMBERFORMAT_PERCENT : + case NUMBERFORMAT_SCIENTIFIC: + return ImpGetDefaultFormat( eType ); + + case NUMBERFORMAT_FRACTION : return CLOffset + ZF_STANDARD_FRACTION; + case NUMBERFORMAT_LOGICAL : return CLOffset + ZF_STANDARD_LOGICAL; + case NUMBERFORMAT_TEXT : return CLOffset + ZF_STANDARD_TEXT; + case NUMBERFORMAT_ALL : + case NUMBERFORMAT_DEFINED : + case NUMBERFORMAT_NUMBER : + case NUMBERFORMAT_UNDEFINED : + default : return CLOffset + ZF_STANDARD; + } +} + +BOOL SvNumberFormatter::IsSpecialStandardFormat( sal_uInt32 nFIndex, + LanguageType eLnge ) +{ + return + nFIndex == GetFormatIndex( NF_TIME_MMSS00, eLnge ) || + nFIndex == GetFormatIndex( NF_TIME_HH_MMSS00, eLnge ) || + nFIndex == GetFormatIndex( NF_TIME_HH_MMSS, eLnge ) + ; +} + +sal_uInt32 SvNumberFormatter::GetStandardFormat( sal_uInt32 nFIndex, short eType, + LanguageType eLnge ) +{ + if ( IsSpecialStandardFormat( nFIndex, eLnge ) ) + return nFIndex; + else + return GetStandardFormat( eType, eLnge ); +} + +sal_uInt32 SvNumberFormatter::GetStandardFormat( double fNumber, sal_uInt32 nFIndex, + short eType, LanguageType eLnge ) +{ + if ( IsSpecialStandardFormat( nFIndex, eLnge ) ) + return nFIndex; + + switch( eType ) + { + case NUMBERFORMAT_TIME : + { + BOOL bSign; + if ( fNumber < 0.0 ) + { + bSign = TRUE; + fNumber = -fNumber; + } + else + bSign = FALSE; + double fSeconds = fNumber * 86400; + if ( floor( fSeconds + 0.5 ) * 100 != floor( fSeconds * 100 + 0.5 ) ) + { // mit 100stel Sekunden + if ( bSign || fSeconds >= 3600 ) + return GetFormatIndex( NF_TIME_HH_MMSS00, eLnge ); + else + return GetFormatIndex( NF_TIME_MMSS00, eLnge ); + } + else + { + if ( bSign || fNumber >= 1.0 ) + return GetFormatIndex( NF_TIME_HH_MMSS, eLnge ); + else + return GetStandardFormat( eType, eLnge ); + } + } + default: + return GetStandardFormat( eType, eLnge ); + } +} + +void SvNumberFormatter::GetInputLineString(const double& fOutNumber, + sal_uInt32 nFIndex, + String& sOutString) +{ + SvNumberformat* pFormat; + short nOldPrec; + Color* pColor; + pFormat = (SvNumberformat*) aFTable.Get(nFIndex); + if (!pFormat) + pFormat = aFTable.Get(ZF_STANDARD); + LanguageType eLang = pFormat->GetLanguage(); + ChangeIntl( eLang ); + short eType = pFormat->GetType() & ~NUMBERFORMAT_DEFINED; + if (eType == 0) + eType = NUMBERFORMAT_DEFINED; + nOldPrec = -1; + if (eType == NUMBERFORMAT_NUMBER || eType == NUMBERFORMAT_PERCENT + || eType == NUMBERFORMAT_CURRENCY + || eType == NUMBERFORMAT_SCIENTIFIC + || eType == NUMBERFORMAT_FRACTION) + { + if (eType != NUMBERFORMAT_PERCENT) // spaeter Sonderbehandlung % + eType = NUMBERFORMAT_NUMBER; + nOldPrec = pFormatScanner->GetStandardPrec(); + ChangeStandardPrec(300); // Merkwert + } + sal_uInt32 nKey = nFIndex; + switch ( eType ) + { // #61619# immer vierstelliges Jahr editieren + case NUMBERFORMAT_DATE : + nKey = GetFormatIndex( NF_DATE_SYS_DDMMYYYY, eLang ); + break; + case NUMBERFORMAT_DATETIME : + nKey = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eLang ); + break; + default: + nKey = GetStandardFormat( fOutNumber, nFIndex, eType, eLang ); + } + if ( nKey != nFIndex ) + pFormat = (SvNumberformat*) aFTable.Get( nKey ); + if (pFormat) + { + if ( eType == NUMBERFORMAT_TIME && pFormat->GetFormatPrecision() ) + { + nOldPrec = pFormatScanner->GetStandardPrec(); + ChangeStandardPrec(300); // Merkwert + } + pFormat->GetOutputString(fOutNumber, sOutString, &pColor); + } + if (nOldPrec != -1) + ChangeStandardPrec(nOldPrec); +} + +void SvNumberFormatter::GetOutputString(const double& fOutNumber, + sal_uInt32 nFIndex, + String& sOutString, + Color** ppColor) +{ + if (bNoZero && fOutNumber == 0.0) + { + sOutString.Erase(); + return; + } + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex); + if (!pFormat) + pFormat = aFTable.Get(ZF_STANDARD); + ChangeIntl(pFormat->GetLanguage()); + pFormat->GetOutputString(fOutNumber, sOutString, ppColor); +} + +void SvNumberFormatter::GetOutputString(String& sString, + sal_uInt32 nFIndex, + String& sOutString, + Color** ppColor) +{ + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex); + if (!pFormat) + pFormat = aFTable.Get(ZF_STANDARD_TEXT); + if (!pFormat->IsTextFormat() && !pFormat->HasTextFormat()) + { + *ppColor = NULL; + sOutString = sString; + } + else + { + ChangeIntl(pFormat->GetLanguage()); + pFormat->GetOutputString(sString, sOutString, ppColor); + } +} + +BOOL SvNumberFormatter::GetPreviewString(const String& sFormatString, + double fPreviewNumber, + String& sOutString, + Color** ppColor, + LanguageType eLnge) +{ + if (sFormatString.Len() == 0) // keinen Leerstring + return FALSE; + + xub_StrLen nCheckPos = STRING_NOTFOUND; + sal_uInt32 nKey; + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + ChangeIntl(eLnge); // ggfs. austauschen + eLnge = ActLnge; + String sTmpString = sFormatString; + SvNumberformat* p_Entry = new SvNumberformat(sTmpString, + pFormatScanner, + pStringScanner, + nCheckPos, + eLnge); + if (nCheckPos == 0) // String ok + { + sal_uInt32 CLOffset = ImpGenerateCL(eLnge); // ggfs. neu Standard- + // formate anlegen + nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLnge); + if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // schon vorhanden + GetOutputString(fPreviewNumber,nKey,sOutString,ppColor); + else + p_Entry->GetOutputString(fPreviewNumber,sOutString, ppColor); + delete p_Entry; + return TRUE; + } + else + { + delete p_Entry; + return FALSE; + } +} + +BOOL SvNumberFormatter::GetPreviewStringGuess( const String& sFormatString, + double fPreviewNumber, + String& sOutString, + Color** ppColor, + LanguageType eLnge ) +{ + if (sFormatString.Len() == 0) // keinen Leerstring + return FALSE; + + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + + ChangeIntl( eLnge ); + eLnge = ActLnge; + BOOL bEnglish = (eLnge == LANGUAGE_ENGLISH_US); + + String aFormatStringUpper( pCharClass->upper( sFormatString ) ); + sal_uInt32 nCLOffset = ImpGenerateCL( eLnge ); + sal_uInt32 nKey = ImpIsEntry( aFormatStringUpper, nCLOffset, eLnge ); + if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND ) + { // Zielformat vorhanden + GetOutputString( fPreviewNumber, nKey, sOutString, ppColor ); + return TRUE; + } + + SvNumberformat *pEntry = NULL; + xub_StrLen nCheckPos = STRING_NOTFOUND; + String sTmpString; + + if ( bEnglish ) + { + sTmpString = sFormatString; + pEntry = new SvNumberformat( sTmpString, pFormatScanner, + pStringScanner, nCheckPos, eLnge ); + } + else + { + nCLOffset = ImpGenerateCL( LANGUAGE_ENGLISH_US ); + nKey = ImpIsEntry( aFormatStringUpper, nCLOffset, LANGUAGE_ENGLISH_US ); + BOOL bEnglishFormat = (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND); + + // try english --> other bzw. english nach other konvertieren + LanguageType eFormatLang = LANGUAGE_ENGLISH_US; + pFormatScanner->SetConvertMode( LANGUAGE_ENGLISH_US, eLnge ); + sTmpString = sFormatString; + pEntry = new SvNumberformat( sTmpString, pFormatScanner, + pStringScanner, nCheckPos, eFormatLang ); + pFormatScanner->SetConvertMode( FALSE ); + ChangeIntl( eLnge ); + + if ( !bEnglishFormat ) + { + if ( nCheckPos > 0 || xTransliteration->isEqual( sFormatString, + pEntry->GetFormatstring() ) ) + { // other Format + delete pEntry; + sTmpString = sFormatString; + pEntry = new SvNumberformat( sTmpString, pFormatScanner, + pStringScanner, nCheckPos, eLnge ); + } + else + { // verify english + xub_StrLen nCheckPos2 = STRING_NOTFOUND; + // try other --> english + eFormatLang = eLnge; + pFormatScanner->SetConvertMode( eLnge, LANGUAGE_ENGLISH_US ); + sTmpString = sFormatString; + SvNumberformat* pEntry2 = new SvNumberformat( sTmpString, pFormatScanner, + pStringScanner, nCheckPos2, eFormatLang ); + pFormatScanner->SetConvertMode( FALSE ); + ChangeIntl( eLnge ); + if ( nCheckPos2 == 0 && !xTransliteration->isEqual( sFormatString, + pEntry2->GetFormatstring() ) ) + { // other Format + delete pEntry; + sTmpString = sFormatString; + pEntry = new SvNumberformat( sTmpString, pFormatScanner, + pStringScanner, nCheckPos, eLnge ); + } + delete pEntry2; + } + } + } + + if (nCheckPos == 0) // String ok + { + ImpGenerateCL( eLnge ); // ggfs. neu Standardformate anlegen + pEntry->GetOutputString( fPreviewNumber, sOutString, ppColor ); + delete pEntry; + return TRUE; + } + delete pEntry; + return FALSE; +} + +sal_uInt32 SvNumberFormatter::TestNewString(const String& sFormatString, + LanguageType eLnge) +{ + if (sFormatString.Len() == 0) // keinen Leerstring + return NUMBERFORMAT_ENTRY_NOT_FOUND; + + xub_StrLen nCheckPos = STRING_NOTFOUND; + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + ChangeIntl(eLnge); // ggfs. austauschen + eLnge = ActLnge; + sal_uInt32 nRes; + String sTmpString = sFormatString; + SvNumberformat* pEntry = new SvNumberformat(sTmpString, + pFormatScanner, + pStringScanner, + nCheckPos, + eLnge); + if (nCheckPos == 0) // String ok + { + sal_uInt32 CLOffset = ImpGenerateCL(eLnge); // ggfs. neu Standard- + // formate anlegen + nRes = ImpIsEntry(pEntry->GetFormatstring(),CLOffset, eLnge); + // schon vorhanden ? + } + else + nRes = NUMBERFORMAT_ENTRY_NOT_FOUND; + delete pEntry; + return nRes; +} + +SvNumberformat* SvNumberFormatter::ImpInsertFormat( + const ::com::sun::star::i18n::NumberFormatCode& rCode, + sal_uInt32 nPos, BOOL bAfterLoadingSO5, sal_Int16 nOrgIndex ) +{ + String aCodeStr( rCode.Code ); + if ( rCode.Index < NF_INDEX_TABLE_ENTRIES && + rCode.Usage == ::com::sun::star::i18n::KNumberFormatUsage::CURRENCY && + rCode.Index != NF_CURRENCY_1000DEC2_CCC ) + { // strip surrounding [$...] on automatic currency + if ( aCodeStr.SearchAscii( "[$" ) != STRING_NOTFOUND ) + aCodeStr = SvNumberformat::StripNewCurrencyDelimiters( aCodeStr, FALSE ); + else + { + if (LocaleDataWrapper::areChecksEnabled() && + rCode.Index != NF_CURRENCY_1000DEC2_CCC ) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpInsertFormat: no [$...] on currency format code, index ")); + aMsg += String::CreateFromInt32( rCode.Index ); + aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ":\n")); + aMsg += String( rCode.Code ); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + } + } + xub_StrLen nCheckPos = 0; + SvNumberformat* pFormat = new SvNumberformat(aCodeStr, + pFormatScanner, + pStringScanner, + nCheckPos, + ActLnge); + if ( !pFormat || nCheckPos > 0 ) + { + if (LocaleDataWrapper::areChecksEnabled()) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpInsertFormat: bad format code, index ")); + aMsg += String::CreateFromInt32( rCode.Index ); + aMsg += '\n'; + aMsg += String( rCode.Code ); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + delete pFormat; + return NULL; + } + if ( rCode.Index >= NF_INDEX_TABLE_ENTRIES ) + { + sal_uInt32 nCLOffset = nPos - (nPos % SV_COUNTRY_LANGUAGE_OFFSET); + sal_uInt32 nKey = ImpIsEntry( aCodeStr, nCLOffset, ActLnge ); + if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND ) + { + if (LocaleDataWrapper::areChecksEnabled()) + { + switch ( nOrgIndex ) + { + // These may be dupes of integer versions for locales where + // currencies have no decimals like Italian Lira. + case NF_CURRENCY_1000DEC2 : // NF_CURRENCY_1000INT + case NF_CURRENCY_1000DEC2_RED : // NF_CURRENCY_1000INT_RED + case NF_CURRENCY_1000DEC2_DASHED : // NF_CURRENCY_1000INT_RED + break; + default: + if ( !bAfterLoadingSO5 ) + { // If bAfterLoadingSO5 there will definitely be some dupes, + // don't cry. But we need this test for verification of locale + // data if not loading old SO5 documents. + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpInsertFormat: dup format code, index ")); + aMsg += String::CreateFromInt32( rCode.Index ); + aMsg += '\n'; + aMsg += String( rCode.Code ); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + } + } + delete pFormat; + return NULL; + } + else if ( nPos - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET ) + { + if (LocaleDataWrapper::areChecksEnabled()) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpInsertFormat: too many format codes, index ")); + aMsg += String::CreateFromInt32( rCode.Index ); + aMsg += '\n'; + aMsg += String( rCode.Code ); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + delete pFormat; + return NULL; + } + } + if ( !aFTable.Insert( nPos, pFormat ) ) + { + if (LocaleDataWrapper::areChecksEnabled()) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "ImpInsertFormat: can't insert number format key pos: ")); + aMsg += String::CreateFromInt32( nPos ); + aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ", code index ")); + aMsg += String::CreateFromInt32( rCode.Index ); + aMsg += '\n'; + aMsg += String( rCode.Code ); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + delete pFormat; + return NULL; + } + if ( rCode.Default ) + pFormat->SetStandard(); + if ( rCode.DefaultName.getLength() ) + pFormat->SetComment( rCode.DefaultName ); + return pFormat; +} + +SvNumberformat* SvNumberFormatter::ImpInsertNewStandardFormat( + const ::com::sun::star::i18n::NumberFormatCode& rCode, + sal_uInt32 nPos, USHORT nVersion, BOOL bAfterLoadingSO5, + sal_Int16 nOrgIndex ) +{ + SvNumberformat* pNewFormat = ImpInsertFormat( rCode, nPos, + bAfterLoadingSO5, nOrgIndex ); + if (pNewFormat) + pNewFormat->SetNewStandardDefined( nVersion ); + // so that it gets saved, displayed properly, and converted by old versions + return pNewFormat; +} + +void SvNumberFormatter::GetFormatSpecialInfo(sal_uInt32 nFormat, + BOOL& bThousand, + BOOL& IsRed, + USHORT& nPrecision, + USHORT& nAnzLeading) + +{ + const SvNumberformat* pFormat = aFTable.Get(nFormat); + if (pFormat) + pFormat->GetFormatSpecialInfo(bThousand, IsRed, + nPrecision, nAnzLeading); + else + { + bThousand = FALSE; + IsRed = FALSE; + nPrecision = pFormatScanner->GetStandardPrec(); + nAnzLeading = 0; + } +} + +USHORT SvNumberFormatter::GetFormatPrecision( sal_uInt32 nFormat ) const +{ + const SvNumberformat* pFormat = aFTable.Get( nFormat ); + if ( pFormat ) + return pFormat->GetFormatPrecision(); + else + return pFormatScanner->GetStandardPrec(); +} + + +String SvNumberFormatter::GetFormatDecimalSep( sal_uInt32 nFormat ) const +{ + const SvNumberformat* pFormat = aFTable.Get( nFormat ); + if ( !pFormat || pFormat->GetLanguage() == ActLnge ) + return GetNumDecimalSep(); + + String aRet; + LanguageType eSaveLang = xLocaleData.getCurrentLanguage(); + if ( pFormat->GetLanguage() == eSaveLang ) + aRet = xLocaleData->getNumDecimalSep(); + else + { + ::com::sun::star::lang::Locale aSaveLocale( xLocaleData->getLocale() ); + ::com::sun::star::lang::Locale aTmpLocale(MsLangId::convertLanguageToLocale(pFormat->GetLanguage())); + ((SvNumberFormatter*)this)->xLocaleData.changeLocale(aTmpLocale, pFormat->GetLanguage() ); + aRet = xLocaleData->getNumDecimalSep(); + ((SvNumberFormatter*)this)->xLocaleData.changeLocale( aSaveLocale, eSaveLang ); + } + return aRet; +} + + +sal_uInt32 SvNumberFormatter::GetFormatSpecialInfo( const String& rFormatString, + BOOL& bThousand, BOOL& IsRed, USHORT& nPrecision, + USHORT& nAnzLeading, LanguageType eLnge ) + +{ + xub_StrLen nCheckPos = 0; + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + ChangeIntl(eLnge); // ggfs. austauschen + eLnge = ActLnge; + String aTmpStr( rFormatString ); + SvNumberformat* pFormat = new SvNumberformat( aTmpStr, + pFormatScanner, pStringScanner, nCheckPos, eLnge ); + if ( nCheckPos == 0 ) + pFormat->GetFormatSpecialInfo( bThousand, IsRed, nPrecision, nAnzLeading ); + else + { + bThousand = FALSE; + IsRed = FALSE; + nPrecision = pFormatScanner->GetStandardPrec(); + nAnzLeading = 0; + } + delete pFormat; + return nCheckPos; +} + + +inline sal_uInt32 SetIndexTable( NfIndexTableOffset nTabOff, sal_uInt32 nIndOff ) +{ + if ( !bIndexTableInitialized ) + { + DBG_ASSERT( theIndexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND, + "SetIndexTable: theIndexTable[nTabOff] already occupied" ); + theIndexTable[nTabOff] = nIndOff; + } + return nIndOff; +} + + +sal_Int32 SvNumberFormatter::ImpGetFormatCodeIndex( + ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode >& rSeq, + const NfIndexTableOffset nTabOff ) +{ + const sal_Int32 nLen = rSeq.getLength(); + for ( sal_Int32 j=0; j<nLen; j++ ) + { + if ( rSeq[j].Index == nTabOff ) + return j; + } + if (LocaleDataWrapper::areChecksEnabled() && (nTabOff < NF_CURRENCY_START + || NF_CURRENCY_END < nTabOff || nTabOff == NF_CURRENCY_1000INT + || nTabOff == NF_CURRENCY_1000INT_RED + || nTabOff == NF_CURRENCY_1000DEC2_CCC)) + { // currency entries with decimals might not exist, e.g. Italian Lira + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpGetFormatCodeIndex: not found: ")); + aMsg += String::CreateFromInt32( nTabOff ); + LocaleDataWrapper::outputCheckMessage( xLocaleData->appendLocaleInfo( + aMsg)); + } + if ( nLen ) + { + sal_Int32 j; + // look for a preset default + for ( j=0; j<nLen; j++ ) + { + if ( rSeq[j].Default ) + return j; + } + // currencies are special, not all format codes must exist, but all + // builtin number format key index positions must have a format assigned + if ( NF_CURRENCY_START <= nTabOff && nTabOff <= NF_CURRENCY_END ) + { + // look for a format with decimals + for ( j=0; j<nLen; j++ ) + { + if ( rSeq[j].Index == NF_CURRENCY_1000DEC2 ) + return j; + } + // last resort: look for a format without decimals + for ( j=0; j<nLen; j++ ) + { + if ( rSeq[j].Index == NF_CURRENCY_1000INT ) + return j; + } + } + } + else + { // we need at least _some_ format + rSeq.realloc(1); + rSeq[0] = ::com::sun::star::i18n::NumberFormatCode(); + String aTmp( '0' ); + aTmp += GetNumDecimalSep(); + aTmp.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "############" ) ); + rSeq[0].Code = aTmp; + } + return 0; +} + + +sal_Int32 SvNumberFormatter::ImpAdjustFormatCodeDefault( + ::com::sun::star::i18n::NumberFormatCode * pFormatArr, + sal_Int32 nCnt, BOOL bCheckCorrectness ) +{ + using namespace ::com::sun::star; + + if ( !nCnt ) + return -1; + if (bCheckCorrectness && LocaleDataWrapper::areChecksEnabled()) + { // check the locale data for correctness + ByteString aMsg; + sal_Int32 nElem, nShort, nMedium, nLong, nShortDef, nMediumDef, nLongDef; + nShort = nMedium = nLong = nShortDef = nMediumDef = nLongDef = -1; + for ( nElem = 0; nElem < nCnt; nElem++ ) + { + switch ( pFormatArr[nElem].Type ) + { + case i18n::KNumberFormatType::SHORT : + nShort = nElem; + break; + case i18n::KNumberFormatType::MEDIUM : + nMedium = nElem; + break; + case i18n::KNumberFormatType::LONG : + nLong = nElem; + break; + default: + aMsg = "unknown type"; + } + if ( pFormatArr[nElem].Default ) + { + switch ( pFormatArr[nElem].Type ) + { + case i18n::KNumberFormatType::SHORT : + if ( nShortDef != -1 ) + aMsg = "dupe short type default"; + nShortDef = nElem; + break; + case i18n::KNumberFormatType::MEDIUM : + if ( nMediumDef != -1 ) + aMsg = "dupe medium type default"; + nMediumDef = nElem; + break; + case i18n::KNumberFormatType::LONG : + if ( nLongDef != -1 ) + aMsg = "dupe long type default"; + nLongDef = nElem; + break; + } + } + if ( aMsg.Len() ) + { + aMsg.Insert( "SvNumberFormatter::ImpAdjustFormatCodeDefault: ", 0 ); + aMsg += "\nXML locale data FormatElement formatindex: "; + aMsg += ByteString::CreateFromInt32( pFormatArr[nElem].Index ); + String aUMsg( aMsg, RTL_TEXTENCODING_ASCII_US); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aUMsg)); + aMsg.Erase(); + } + } + if ( nShort != -1 && nShortDef == -1 ) + aMsg += "no short type default "; + if ( nMedium != -1 && nMediumDef == -1 ) + aMsg += "no medium type default "; + if ( nLong != -1 && nLongDef == -1 ) + aMsg += "no long type default "; + if ( aMsg.Len() ) + { + aMsg.Insert( "SvNumberFormatter::ImpAdjustFormatCodeDefault: ", 0 ); + aMsg += "\nXML locale data FormatElement group of: "; + String aUMsg( aMsg, RTL_TEXTENCODING_ASCII_US); + aUMsg += String( pFormatArr[0].NameID ); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aUMsg)); + aMsg.Erase(); + } + } + // find the default (medium preferred, then long) and reset all other defaults + sal_Int32 nElem, nDef, nMedium; + nDef = nMedium = -1; + for ( nElem = 0; nElem < nCnt; nElem++ ) + { + if ( pFormatArr[nElem].Default ) + { + switch ( pFormatArr[nElem].Type ) + { + case i18n::KNumberFormatType::MEDIUM : + nDef = nMedium = nElem; + break; + case i18n::KNumberFormatType::LONG : + if ( nMedium == -1 ) + nDef = nElem; + // fallthru + default: + if ( nDef == -1 ) + nDef = nElem; + pFormatArr[nElem].Default = sal_False; + } + } + } + if ( nDef == -1 ) + nDef = 0; + pFormatArr[nDef].Default = sal_True; + return nDef; +} + + +void SvNumberFormatter::ImpGenerateFormats( sal_uInt32 CLOffset, BOOL bLoadingSO5 ) +{ + using namespace ::com::sun::star; + + if ( !bIndexTableInitialized ) + { + for ( USHORT j=0; j<NF_INDEX_TABLE_ENTRIES; j++ ) + { + theIndexTable[j] = NUMBERFORMAT_ENTRY_NOT_FOUND; + } + } + BOOL bOldConvertMode = pFormatScanner->GetConvertMode(); + if (bOldConvertMode) + pFormatScanner->SetConvertMode(FALSE); // switch off for this function + + NumberFormatCodeWrapper aNumberFormatCode( xServiceManager, GetLocale() ); + + xub_StrLen nCheckPos = 0; + SvNumberformat* pNewFormat = NULL; + String aFormatCode; + sal_Int32 nIdx; + sal_Bool bDefault; + + // Counter for additional builtin formats not fitting into the first 10 + // of a category (TLOT:=The Legacy Of Templin), altogether about 20 formats. + // Has to be incremented on each ImpInsertNewStandardformat, new formats + // must be appended, not inserted! + USHORT nNewExtended = ZF_STANDARD_NEWEXTENDED; + + // Number + uno::Sequence< i18n::NumberFormatCode > aFormatSeq + = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::FIXED_NUMBER ); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + + // General + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_STANDARD ); + SvNumberformat* pStdFormat = ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_NUMBER_STANDARD, ZF_STANDARD )); + if (pStdFormat) + { + // This is _the_ standard format. + if (LocaleDataWrapper::areChecksEnabled() && + pStdFormat->GetType() != NUMBERFORMAT_NUMBER) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpGenerateFormats: General format not NUMBER")); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + pStdFormat->SetType( NUMBERFORMAT_NUMBER ); + pStdFormat->SetStandard(); + pStdFormat->SetLastInsertKey( SV_MAX_ANZ_STANDARD_FORMATE ); + } + else + { + if (LocaleDataWrapper::areChecksEnabled()) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "SvNumberFormatter::ImpGenerateFormats: General format not insertable, nothing will work")); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + } + + // Boolean + aFormatCode = pFormatScanner->GetBooleanString(); + pNewFormat = new SvNumberformat( aFormatCode, + pFormatScanner, pStringScanner, nCheckPos, ActLnge ); + pNewFormat->SetType(NUMBERFORMAT_LOGICAL); + pNewFormat->SetStandard(); + if ( !aFTable.Insert( + CLOffset + SetIndexTable( NF_BOOLEAN, ZF_STANDARD_LOGICAL ), + pNewFormat)) + delete pNewFormat; + + // Text + aFormatCode = '@'; + pNewFormat = new SvNumberformat( aFormatCode, + pFormatScanner, pStringScanner, nCheckPos, ActLnge ); + pNewFormat->SetType(NUMBERFORMAT_TEXT); + pNewFormat->SetStandard(); + if ( !aFTable.Insert( + CLOffset + SetIndexTable( NF_TEXT, ZF_STANDARD_TEXT ), + pNewFormat)) + delete pNewFormat; + + + + // 0 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_INT ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_NUMBER_INT, ZF_STANDARD+1 )); + + // 0.00 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_DEC2 ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_NUMBER_DEC2, ZF_STANDARD+2 )); + + // #,##0 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_1000INT ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_NUMBER_1000INT, ZF_STANDARD+3 )); + + // #,##0.00 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_1000DEC2 ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_NUMBER_1000DEC2, ZF_STANDARD+4 )); + + // #.##0,00 System country/language dependent since number formatter version 6 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_SYSTEM ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_NUMBER_SYSTEM, ZF_STANDARD+5 ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + + // Percent number + aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::PERCENT_NUMBER ); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + + // 0% + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_PERCENT_INT ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_PERCENT_INT, ZF_STANDARD_PERCENT )); + + // 0.00% + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_PERCENT_DEC2 ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_PERCENT_DEC2, ZF_STANDARD_PERCENT+1 )); + + + + // Currency. NO default standard option! Default is determined of locale + // data default currency and format is generated if needed. + aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY ); + if (LocaleDataWrapper::areChecksEnabled()) + { + // though no default desired here, test for correctness of locale data + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + } + + // #,##0 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000INT ); + bDefault = aFormatSeq[nIdx].Default; + aFormatSeq[nIdx].Default = sal_False; + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_CURRENCY_1000INT, ZF_STANDARD_CURRENCY )); + aFormatSeq[nIdx].Default = bDefault; + + // #,##0.00 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2 ); + bDefault = aFormatSeq[nIdx].Default; + aFormatSeq[nIdx].Default = sal_False; + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2, ZF_STANDARD_CURRENCY+1 )); + aFormatSeq[nIdx].Default = bDefault; + + // #,##0 negative red + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000INT_RED ); + bDefault = aFormatSeq[nIdx].Default; + aFormatSeq[nIdx].Default = sal_False; + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_CURRENCY_1000INT_RED, ZF_STANDARD_CURRENCY+2 )); + aFormatSeq[nIdx].Default = bDefault; + + // #,##0.00 negative red + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_RED ); + bDefault = aFormatSeq[nIdx].Default; + aFormatSeq[nIdx].Default = sal_False; + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2_RED, ZF_STANDARD_CURRENCY+3 )); + aFormatSeq[nIdx].Default = bDefault; + + // #,##0.00 USD since number formatter version 3 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_CCC ); + bDefault = aFormatSeq[nIdx].Default; + aFormatSeq[nIdx].Default = sal_False; + pNewFormat = ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2_CCC, ZF_STANDARD_CURRENCY+4 )); + if ( pNewFormat ) + pNewFormat->SetUsed(TRUE); // must be saved for older versions + aFormatSeq[nIdx].Default = bDefault; + + // #.##0,-- since number formatter version 6 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_DASHED ); + bDefault = aFormatSeq[nIdx].Default; + aFormatSeq[nIdx].Default = sal_False; + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2_DASHED, ZF_STANDARD_CURRENCY+5 ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + aFormatSeq[nIdx].Default = bDefault; + + + + // Date + aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::DATE ); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + + // DD.MM.YY System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYSTEM_SHORT ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYSTEM_SHORT, ZF_STANDARD_DATE )); + + // NN DD.MMM YY + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DEF_NNDDMMMYY ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_DEF_NNDDMMMYY, ZF_STANDARD_DATE+1 )); + + // DD.MM.YY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_MMYY ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_MMYY, ZF_STANDARD_DATE+2 )); + + // DD MMM + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMM ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_DDMMM, ZF_STANDARD_DATE+3 )); + + // MMMM + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_MMMM ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_MMMM, ZF_STANDARD_DATE+4 )); + + // QQ YY + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_QQJJ ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_QQJJ, ZF_STANDARD_DATE+5 )); + + // DD.MM.YYYY since number formatter version 2, was DD.MM.[YY]YY + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMYYYY ); + pNewFormat = ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_DDMMYYYY, ZF_STANDARD_DATE+6 )); + if ( pNewFormat ) + pNewFormat->SetUsed(TRUE); // must be saved for older versions + + // DD.MM.YY def/System, since number formatter version 6 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_DDMMYY, ZF_STANDARD_DATE+7 ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // NNN, D. MMMM YYYY System + // Long day of week: "NNNN" instead of "NNN," because of compatibility + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYSTEM_LONG ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYSTEM_LONG, ZF_STANDARD_DATE+8 ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // Hard coded but system (regional settings) delimiters dependent long date formats + // since numberformatter version 6 + + // D. MMM YY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_DMMMYY, ZF_STANDARD_DATE+9 ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + //! Unfortunally TLOT intended only 10 builtin formats per category, more + //! would overwrite the next category (ZF_STANDARD_TIME) :-(( + //! Therefore they are inserted with nNewExtended++ (which is also limited) + + // D. MMM YYYY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMYYYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_DMMMYYYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // D. MMMM YYYY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMMYYYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_DMMMMYYYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // NN, D. MMM YY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNDMMMYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_NNDMMMYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // NN, D. MMMM YYYY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNDMMMMYYYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_NNDMMMMYYYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // NNN, D. MMMM YYYY def/System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNNNDMMMMYYYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_SYS_NNNNDMMMMYYYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // Hard coded DIN (Deutsche Industrie Norm) and EN (European Norm) date formats + + // D. MMM. YYYY DIN/EN + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_DMMMYYYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_DIN_DMMMYYYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // D. MMMM YYYY DIN/EN + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_DMMMMYYYY ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_DIN_DMMMMYYYY, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // MM-DD DIN/EN + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_MMDD ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_DIN_MMDD, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // YY-MM-DD DIN/EN + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_YYMMDD ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_DIN_YYMMDD, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + // YYYY-MM-DD DIN/EN + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_YYYYMMDD ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATE_DIN_YYYYMMDD, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NEWSTANDARD ); + + + + // Time + aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::TIME ); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + + // HH:MM + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMM ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_HHMM, ZF_STANDARD_TIME )); + + // HH:MM:SS + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMSS ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_HHMMSS, ZF_STANDARD_TIME+1 )); + + // HH:MM AM/PM + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMAMPM ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_HHMMAMPM, ZF_STANDARD_TIME+2 )); + + // HH:MM:SS AM/PM + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMSSAMPM ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_HHMMSSAMPM, ZF_STANDARD_TIME+3 )); + + // [HH]:MM:SS + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HH_MMSS ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_HH_MMSS, ZF_STANDARD_TIME+4 )); + + // MM:SS,00 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_MMSS00 ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_MMSS00, ZF_STANDARD_TIME+5 )); + + // [HH]:MM:SS,00 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HH_MMSS00 ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_TIME_HH_MMSS00, ZF_STANDARD_TIME+6 ), + SV_NUMBERFORMATTER_VERSION_NF_TIME_HH_MMSS00 ); + + + + // DateTime + aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::DATE_TIME ); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + + // DD.MM.YY HH:MM System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYSTEM_SHORT_HHMM ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATETIME_SYSTEM_SHORT_HHMM, ZF_STANDARD_DATETIME )); + + // DD.MM.YYYY HH:MM:SS System + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMMSS ); + ImpInsertNewStandardFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, ZF_STANDARD_DATETIME+1 ), + SV_NUMBERFORMATTER_VERSION_NF_DATETIME_SYS_DDMMYYYY_HHMMSS ); + + + + // Scientific number + aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER ); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); + + // 0.00E+000 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_SCIENTIFIC_000E000 ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_SCIENTIFIC_000E000, ZF_STANDARD_SCIENTIFIC )); + + // 0.00E+00 + nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_SCIENTIFIC_000E00 ); + ImpInsertFormat( aFormatSeq[nIdx], + CLOffset + SetIndexTable( NF_SCIENTIFIC_000E00, ZF_STANDARD_SCIENTIFIC+1 )); + + + + // Fraction number (no default option) + i18n::NumberFormatCode aSingleFormatCode; + + // # ?/? + aSingleFormatCode.Code = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "# ?/?" ) ); + String s25( RTL_CONSTASCII_USTRINGPARAM( "# ?/?" ) ); // # ?/? + ImpInsertFormat( aSingleFormatCode, + CLOffset + SetIndexTable( NF_FRACTION_1, ZF_STANDARD_FRACTION )); + + // # ??/?? + //! "??/" would be interpreted by the compiler as a trigraph for '\' + aSingleFormatCode.Code = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "# ?\?/?\?" ) ); + ImpInsertFormat( aSingleFormatCode, + CLOffset + SetIndexTable( NF_FRACTION_2, ZF_STANDARD_FRACTION+1 )); + + // Week of year must be appended here because of nNewExtended + const String* pKeyword = pFormatScanner->GetKeywords(); + aSingleFormatCode.Code = pKeyword[NF_KEY_WW]; + ImpInsertNewStandardFormat( aSingleFormatCode, + CLOffset + SetIndexTable( NF_DATE_WW, nNewExtended++ ), + SV_NUMBERFORMATTER_VERSION_NF_DATE_WW ); + + + + bIndexTableInitialized = TRUE; + DBG_ASSERT( nNewExtended <= ZF_STANDARD_NEWEXTENDEDMAX, + "ImpGenerateFormats: overflow of nNewExtended standard formats" ); + + // Now all additional format codes provided by I18N, but only if not + // loading from old SO5 file format, then they are appended last. + if ( !bLoadingSO5 ) + ImpGenerateAdditionalFormats( CLOffset, aNumberFormatCode, FALSE ); + + if (bOldConvertMode) + pFormatScanner->SetConvertMode(TRUE); +} + + +void SvNumberFormatter::ImpGenerateAdditionalFormats( sal_uInt32 CLOffset, + NumberFormatCodeWrapper& rNumberFormatCode, BOOL bAfterLoadingSO5 ) +{ + using namespace ::com::sun::star; + + SvNumberformat* pStdFormat = + (SvNumberformat*) aFTable.Get( CLOffset + ZF_STANDARD ); + if ( !pStdFormat ) + { + DBG_ERRORFILE( "ImpGenerateAdditionalFormats: no GENERAL format" ); + return ; + } + sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey(); + rNumberFormatCode.setLocale( GetLocale() ); + sal_Int32 j; + + // All currencies, this time with [$...] which was stripped in + // ImpGenerateFormats for old "automatic" currency formats. + uno::Sequence< i18n::NumberFormatCode > aFormatSeq = + rNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY ); + i18n::NumberFormatCode * pFormatArr = aFormatSeq.getArray(); + sal_Int32 nCodes = aFormatSeq.getLength(); + ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), nCodes ); + for ( j = 0; j < nCodes; j++ ) + { + if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET ) + { + DBG_ERRORFILE( "ImpGenerateAdditionalFormats: too many formats" ); + break; // for + } + if ( pFormatArr[j].Index < NF_INDEX_TABLE_ENTRIES && + pFormatArr[j].Index != NF_CURRENCY_1000DEC2_CCC ) + { // Insert only if not already inserted, but internal index must be + // above so ImpInsertFormat can distinguish it. + sal_Int16 nOrgIndex = pFormatArr[j].Index; + pFormatArr[j].Index = sal::static_int_cast< sal_Int16 >( + pFormatArr[j].Index + nCodes + NF_INDEX_TABLE_ENTRIES); + //! no default on currency + sal_Bool bDefault = aFormatSeq[j].Default; + aFormatSeq[j].Default = sal_False; + if ( ImpInsertNewStandardFormat( pFormatArr[j], nPos+1, + SV_NUMBERFORMATTER_VERSION_ADDITIONAL_I18N_FORMATS, + bAfterLoadingSO5, nOrgIndex ) ) + nPos++; + pFormatArr[j].Index = nOrgIndex; + aFormatSeq[j].Default = bDefault; + } + } + + // all additional format codes provided by I18N that are not old standard index + aFormatSeq = rNumberFormatCode.getAllFormatCodes(); + nCodes = aFormatSeq.getLength(); + if ( nCodes ) + { + pFormatArr = aFormatSeq.getArray(); + // don't check ALL + sal_Int32 nDef = ImpAdjustFormatCodeDefault( pFormatArr, nCodes, FALSE); + // don't have any defaults here + pFormatArr[nDef].Default = sal_False; + for ( j = 0; j < nCodes; j++ ) + { + if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET ) + { + DBG_ERRORFILE( "ImpGenerateAdditionalFormats: too many formats" ); + break; // for + } + if ( pFormatArr[j].Index >= NF_INDEX_TABLE_ENTRIES ) + if ( ImpInsertNewStandardFormat( pFormatArr[j], nPos+1, + SV_NUMBERFORMATTER_VERSION_ADDITIONAL_I18N_FORMATS, + bAfterLoadingSO5 ) ) + nPos++; + } + } + + pStdFormat->SetLastInsertKey( (USHORT)(nPos - CLOffset) ); +} + + +void SvNumberFormatter::ImpGetPosCurrFormat( String& sPosStr, const String& rCurrSymbol ) +{ + NfCurrencyEntry::CompletePositiveFormatString( sPosStr, + rCurrSymbol, xLocaleData->getCurrPositiveFormat() ); +} + +void SvNumberFormatter::ImpGetNegCurrFormat( String& sNegStr, const String& rCurrSymbol ) +{ + NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, + rCurrSymbol, xLocaleData->getCurrNegativeFormat() ); +} + +void SvNumberFormatter::GenerateFormat(String& sString, + sal_uInt32 nIndex, + LanguageType eLnge, + BOOL bThousand, + BOOL IsRed, + USHORT nPrecision, + USHORT nAnzLeading) +{ + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + short eType = GetType(nIndex); + USHORT i; + ImpGenerateCL(eLnge); // ggfs. neu Standard- + // formate anlegen + sString.Erase(); + + utl::DigitGroupingIterator aGrouping( xLocaleData->getDigitGrouping()); + const xub_StrLen nDigitsInFirstGroup = static_cast<xub_StrLen>(aGrouping.get()); + const String& rThSep = GetNumThousandSep(); + if (nAnzLeading == 0) + { + if (!bThousand) + sString += '#'; + else + { + sString += '#'; + sString += rThSep; + sString.Expand( sString.Len() + nDigitsInFirstGroup, '#' ); + } + } + else + { + for (i = 0; i < nAnzLeading; i++) + { + if (bThousand && i > 0 && i == aGrouping.getPos()) + { + sString.Insert( rThSep, 0 ); + aGrouping.advance(); + } + sString.Insert('0',0); + } + if (bThousand && nAnzLeading < nDigitsInFirstGroup + 1) + { + for (i = nAnzLeading; i < nDigitsInFirstGroup + 1; i++) + { + if (bThousand && i % nDigitsInFirstGroup == 0) + sString.Insert( rThSep, 0 ); + sString.Insert('#',0); + } + } + } + if (nPrecision > 0) + { + sString += GetNumDecimalSep(); + sString.Expand( sString.Len() + nPrecision, '0' ); + } + if (eType == NUMBERFORMAT_PERCENT) + sString += '%'; + else if (eType == NUMBERFORMAT_CURRENCY) + { + String sNegStr = sString; + String aCurr; + const NfCurrencyEntry* pEntry; + BOOL bBank; + if ( GetNewCurrencySymbolString( nIndex, aCurr, &pEntry, &bBank ) ) + { + if ( pEntry ) + { + USHORT nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( + xLocaleData->getCurrPositiveFormat(), + pEntry->GetPositiveFormat(), bBank ); + USHORT nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( + xLocaleData->getCurrNegativeFormat(), + pEntry->GetNegativeFormat(), bBank ); + pEntry->CompletePositiveFormatString( sString, bBank, + nPosiForm ); + pEntry->CompleteNegativeFormatString( sNegStr, bBank, + nNegaForm ); + } + else + { // assume currency abbreviation (AKA banking symbol), not symbol + USHORT nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( + xLocaleData->getCurrPositiveFormat(), + xLocaleData->getCurrPositiveFormat(), TRUE ); + USHORT nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( + xLocaleData->getCurrNegativeFormat(), + xLocaleData->getCurrNegativeFormat(), TRUE ); + NfCurrencyEntry::CompletePositiveFormatString( sString, aCurr, + nPosiForm ); + NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, aCurr, + nNegaForm ); + } + } + else + { // "automatic" old style + String aSymbol, aAbbrev; + GetCompatibilityCurrency( aSymbol, aAbbrev ); + ImpGetPosCurrFormat( sString, aSymbol ); + ImpGetNegCurrFormat( sNegStr, aSymbol ); + } + if (IsRed) + { + sString += ';'; + sString += '['; + sString += pFormatScanner->GetRedString(); + sString += ']'; + } + else + sString += ';'; + sString += sNegStr; + } + if (IsRed && eType != NUMBERFORMAT_CURRENCY) + { + String sTmpStr = sString; + sTmpStr += ';'; + sTmpStr += '['; + sTmpStr += pFormatScanner->GetRedString(); + sTmpStr += ']'; + sTmpStr += '-'; + sTmpStr +=sString; + sString = sTmpStr; + } +} + +BOOL SvNumberFormatter::IsUserDefined(const String& sStr, + LanguageType eLnge) +{ + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + sal_uInt32 CLOffset = ImpGenerateCL(eLnge); // ggfs. neu Standard- + // formate anlegen + eLnge = ActLnge; + sal_uInt32 nKey = ImpIsEntry(sStr, CLOffset, eLnge); + if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) + return TRUE; + SvNumberformat* pEntry = aFTable.Get(nKey); + if ( pEntry && ((pEntry->GetType() & NUMBERFORMAT_DEFINED) != 0) ) + return TRUE; + return FALSE; +} + +sal_uInt32 SvNumberFormatter::GetEntryKey(const String& sStr, + LanguageType eLnge) +{ + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + sal_uInt32 CLOffset = ImpGenerateCL(eLnge); // ggfs. neu Standard- + // formate anlegen + return ImpIsEntry(sStr, CLOffset, eLnge); +} + +sal_uInt32 SvNumberFormatter::GetStandardIndex(LanguageType eLnge) +{ + if (eLnge == LANGUAGE_DONTKNOW) + eLnge = IniLnge; + return GetStandardFormat(NUMBERFORMAT_NUMBER, eLnge); +} + +short SvNumberFormatter::GetType(sal_uInt32 nFIndex) +{ + short eType; + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex); + if (!pFormat) + eType = NUMBERFORMAT_UNDEFINED; + else + { + eType = pFormat->GetType() &~NUMBERFORMAT_DEFINED; + if (eType == 0) + eType = NUMBERFORMAT_DEFINED; + } + return eType; +} + +void SvNumberFormatter::ClearMergeTable() +{ + if ( pMergeTable ) + { + sal_uInt32* pIndex = (sal_uInt32*) pMergeTable->First(); + while (pIndex) + { + delete pIndex; + pIndex = pMergeTable->Next(); + } + pMergeTable->Clear(); + } +} + +SvNumberFormatterIndexTable* SvNumberFormatter::MergeFormatter(SvNumberFormatter& rTable) +{ + if ( pMergeTable ) + ClearMergeTable(); + else + pMergeTable = new SvNumberFormatterIndexTable; + sal_uInt32 nCLOffset = 0; + sal_uInt32 nOldKey, nOffset, nNewKey; + sal_uInt32* pNewIndex; + SvNumberformat* pNewEntry; + SvNumberformat* pFormat = rTable.aFTable.First(); + while (pFormat) + { + nOldKey = rTable.aFTable.GetCurKey(); + nOffset = nOldKey % SV_COUNTRY_LANGUAGE_OFFSET; // relativIndex + if (nOffset == 0) // 1. Format von CL + nCLOffset = ImpGenerateCL(pFormat->GetLanguage()); + + if (nOffset <= SV_MAX_ANZ_STANDARD_FORMATE) // Std.form. + { + nNewKey = nCLOffset + nOffset; + if (!aFTable.Get(nNewKey)) // noch nicht da + { +// pNewEntry = new SvNumberformat(*pFormat); // Copy reicht nicht !!! + pNewEntry = new SvNumberformat( *pFormat, *pFormatScanner ); + if (!aFTable.Insert(nNewKey, pNewEntry)) + delete pNewEntry; + } + if (nNewKey != nOldKey) // neuer Index + { + pNewIndex = new sal_uInt32(nNewKey); + if (!pMergeTable->Insert(nOldKey,pNewIndex)) + delete pNewIndex; + } + } + else // benutzerdef. + { +// pNewEntry = new SvNumberformat(*pFormat); // Copy reicht nicht !!! + pNewEntry = new SvNumberformat( *pFormat, *pFormatScanner ); + nNewKey = ImpIsEntry(pNewEntry->GetFormatstring(), + nCLOffset, + pFormat->GetLanguage()); + if (nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // schon vorhanden + delete pNewEntry; + else + { + SvNumberformat* pStdFormat = + (SvNumberformat*) aFTable.Get(nCLOffset + ZF_STANDARD); + sal_uInt32 nPos = nCLOffset + pStdFormat->GetLastInsertKey(); + nNewKey = nPos+1; + if (nPos - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET) + { + DBG_ERROR( + "SvNumberFormatter:: Zu viele Formate pro CL"); + delete pNewEntry; + } + else if (!aFTable.Insert(nNewKey, pNewEntry)) + delete pNewEntry; + else + pStdFormat->SetLastInsertKey((USHORT) (nNewKey - nCLOffset)); + } + if (nNewKey != nOldKey) // neuer Index + { + pNewIndex = new sal_uInt32(nNewKey); + if (!pMergeTable->Insert(nOldKey,pNewIndex)) + delete pNewIndex; + } + } + pFormat = rTable.aFTable.Next(); + } + return pMergeTable; +} + + +SvNumberFormatterMergeMap SvNumberFormatter::ConvertMergeTableToMap() +{ + if (!HasMergeFmtTbl()) + return SvNumberFormatterMergeMap(); + + SvNumberFormatterMergeMap aMap; + for (sal_uInt32* pIndex = pMergeTable->First(); pIndex; pIndex = pMergeTable->Next()) + { + sal_uInt32 nOldKey = pMergeTable->GetCurKey(); + aMap.insert( SvNumberFormatterMergeMap::value_type( nOldKey, *pIndex)); + } + ClearMergeTable(); + return aMap; +} + + +sal_uInt32 SvNumberFormatter::GetFormatForLanguageIfBuiltIn( sal_uInt32 nFormat, + LanguageType eLnge ) +{ + if ( eLnge == LANGUAGE_DONTKNOW ) + eLnge = IniLnge; + if ( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLnge == IniLnge ) + return nFormat; // es bleibt wie es ist + sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relativIndex + if ( nOffset > SV_MAX_ANZ_STANDARD_FORMATE ) + return nFormat; // kein eingebautes Format + sal_uInt32 nCLOffset = ImpGenerateCL(eLnge); // ggbf. generieren + return nCLOffset + nOffset; +} + + +sal_uInt32 SvNumberFormatter::GetFormatIndex( NfIndexTableOffset nTabOff, + LanguageType eLnge ) +{ + if ( nTabOff >= NF_INDEX_TABLE_ENTRIES + || theIndexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND ) + return NUMBERFORMAT_ENTRY_NOT_FOUND; + if ( eLnge == LANGUAGE_DONTKNOW ) + eLnge = IniLnge; + sal_uInt32 nCLOffset = ImpGenerateCL(eLnge); // ggbf. generieren + return nCLOffset + theIndexTable[nTabOff]; +} + + +NfIndexTableOffset SvNumberFormatter::GetIndexTableOffset( sal_uInt32 nFormat ) const +{ + sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relativIndex + if ( nOffset > SV_MAX_ANZ_STANDARD_FORMATE ) + return NF_INDEX_TABLE_ENTRIES; // kein eingebautes Format + for ( USHORT j = 0; j < NF_INDEX_TABLE_ENTRIES; j++ ) + { + if ( theIndexTable[j] == nOffset ) + return (NfIndexTableOffset) j; + } + return NF_INDEX_TABLE_ENTRIES; // bad luck +} + + +void SvNumberFormatter::SetYear2000( USHORT nVal ) +{ + pStringScanner->SetYear2000( nVal ); +} + + +USHORT SvNumberFormatter::GetYear2000() const +{ + return pStringScanner->GetYear2000(); +} + + +USHORT SvNumberFormatter::ExpandTwoDigitYear( USHORT nYear ) const +{ + if ( nYear < 100 ) + return SvNumberFormatter::ExpandTwoDigitYear( nYear, + pStringScanner->GetYear2000() ); + return nYear; +} + + +// static +USHORT SvNumberFormatter::GetYear2000Default() +{ + return (USHORT) ::utl::MiscCfg().GetYear2000(); +} + + +// static +const NfCurrencyTable& SvNumberFormatter::GetTheCurrencyTable() +{ + ::osl::MutexGuard aGuard( GetMutex() ); + while ( !bCurrencyTableInitialized ) + ImpInitCurrencyTable(); + return theCurrencyTable::get(); +} + + +// static +const NfCurrencyEntry* SvNumberFormatter::MatchSystemCurrency() +{ + // MUST call GetTheCurrencyTable() before accessing nSystemCurrencyPosition + const NfCurrencyTable& rTable = GetTheCurrencyTable(); + return nSystemCurrencyPosition ? rTable[nSystemCurrencyPosition] : NULL; +} + + +// static +const NfCurrencyEntry& SvNumberFormatter::GetCurrencyEntry( LanguageType eLang ) +{ + if ( eLang == LANGUAGE_SYSTEM ) + { + const NfCurrencyEntry* pCurr = MatchSystemCurrency(); + return pCurr ? *pCurr : *(GetTheCurrencyTable()[0]); + } + else + { + eLang = MsLangId::getRealLanguage( eLang ); + const NfCurrencyTable& rTable = GetTheCurrencyTable(); + USHORT nCount = rTable.Count(); + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + for ( USHORT j = 0; j < nCount; j++, ppData++ ) + { + if ( (*ppData)->GetLanguage() == eLang ) + return **ppData; + } + return *(rTable[0]); + } +} + + +// static +const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry( + const String& rAbbrev, LanguageType eLang ) +{ + eLang = MsLangId::getRealLanguage( eLang ); + const NfCurrencyTable& rTable = GetTheCurrencyTable(); + USHORT nCount = rTable.Count(); + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + for ( USHORT j = 0; j < nCount; j++, ppData++ ) + { + if ( (*ppData)->GetLanguage() == eLang && + (*ppData)->GetBankSymbol() == rAbbrev ) + return *ppData; + } + return NULL; +} + + +// static +const NfCurrencyEntry* SvNumberFormatter::GetLegacyOnlyCurrencyEntry( + const String& rSymbol, const String& rAbbrev ) +{ + if (!bCurrencyTableInitialized) + GetTheCurrencyTable(); // just for initialization + const NfCurrencyTable& rTable = theLegacyOnlyCurrencyTable::get(); + USHORT nCount = rTable.Count(); + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + for ( USHORT j = 0; j < nCount; j++, ppData++ ) + { + if ( (*ppData)->GetSymbol() == rSymbol && + (*ppData)->GetBankSymbol() == rAbbrev ) + return *ppData; + } + return NULL; +} + + +// static +IMPL_STATIC_LINK_NOINSTANCE( SvNumberFormatter, CurrencyChangeLink, void*, EMPTYARG ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + String aAbbrev; + LanguageType eLang = LANGUAGE_SYSTEM; + SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage( aAbbrev, eLang ); + SetDefaultSystemCurrency( aAbbrev, eLang ); + return 0; +} + + +// static +void SvNumberFormatter::SetDefaultSystemCurrency( const String& rAbbrev, LanguageType eLang ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + if ( eLang == LANGUAGE_SYSTEM ) + eLang = SvtSysLocale().GetLanguage(); + const NfCurrencyTable& rTable = GetTheCurrencyTable(); + USHORT nCount = rTable.Count(); + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + if ( rAbbrev.Len() ) + { + for ( USHORT j = 0; j < nCount; j++, ppData++ ) + { + if ( (*ppData)->GetLanguage() == eLang && (*ppData)->GetBankSymbol() == rAbbrev ) + { + nSystemCurrencyPosition = j; + return ; + } + } + } + else + { + for ( USHORT j = 0; j < nCount; j++, ppData++ ) + { + if ( (*ppData)->GetLanguage() == eLang ) + { + nSystemCurrencyPosition = j; + return ; + } + } + } + nSystemCurrencyPosition = 0; // not found => simple SYSTEM +} + + +void SvNumberFormatter::ResetDefaultSystemCurrency() +{ + nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; +} + + +sal_uInt32 SvNumberFormatter::ImpGetDefaultSystemCurrencyFormat() +{ + if ( nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) + { + xub_StrLen nCheck; + short nType; + NfWSStringsDtor aCurrList; + USHORT nDefault = GetCurrencyFormatStrings( aCurrList, + GetCurrencyEntry( LANGUAGE_SYSTEM ), FALSE ); + DBG_ASSERT( aCurrList.Count(), "where is the NewCurrency System standard format?!?" ); + // if already loaded or user defined nDefaultSystemCurrencyFormat + // will be set to the right value + PutEntry( *aCurrList.GetObject( nDefault ), nCheck, nType, + nDefaultSystemCurrencyFormat, LANGUAGE_SYSTEM ); + DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" ); + DBG_ASSERT( nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND, + "nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" ); + } + return nDefaultSystemCurrencyFormat; +} + + +sal_uInt32 SvNumberFormatter::ImpGetDefaultCurrencyFormat() +{ + sal_uInt32 CLOffset = ImpGetCLOffset( ActLnge ); + sal_uInt32 nDefaultCurrencyFormat = + (sal_uInt32)(sal_uIntPtr) aDefaultFormatKeys.Get( CLOffset + ZF_STANDARD_CURRENCY ); + if ( !nDefaultCurrencyFormat ) + nDefaultCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; + if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) + { + // look for a defined standard + sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET; + sal_uInt32 nKey; + aFTable.Seek( CLOffset ); + while ( (nKey = aFTable.GetCurKey()) >= CLOffset && nKey < nStopKey ) + { + const SvNumberformat* pEntry = + (const SvNumberformat*) aFTable.GetCurObject(); + if ( pEntry->IsStandard() && (pEntry->GetType() & NUMBERFORMAT_CURRENCY) ) + { + nDefaultCurrencyFormat = nKey; + break; // while + } + aFTable.Next(); + } + + if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) + { // none found, create one + xub_StrLen nCheck; + short nType; + NfWSStringsDtor aCurrList; + USHORT nDefault = GetCurrencyFormatStrings( aCurrList, + GetCurrencyEntry( ActLnge ), FALSE ); + DBG_ASSERT( aCurrList.Count(), "where is the NewCurrency standard format?" ); + if ( aCurrList.Count() ) + { + // if already loaded or user defined nDefaultSystemCurrencyFormat + // will be set to the right value + PutEntry( *aCurrList.GetObject( nDefault ), nCheck, nType, + nDefaultCurrencyFormat, ActLnge ); + DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" ); + DBG_ASSERT( nDefaultCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND, + "nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" ); + } + // old automatic currency format as a last resort + if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) + nDefaultCurrencyFormat = CLOffset + ZF_STANDARD_CURRENCY+3; + else + { // mark as standard so that it is found next time + SvNumberformat* pEntry = aFTable.Get( nDefaultCurrencyFormat ); + if ( pEntry ) + pEntry->SetStandard(); + } + } + aDefaultFormatKeys.Insert( CLOffset + ZF_STANDARD_CURRENCY, + (void*) nDefaultCurrencyFormat ); + } + return nDefaultCurrencyFormat; +} + + +// static +// try to make it inline if possible since this a loop body +// TRUE: continue; FALSE: break loop, if pFoundEntry==NULL dupe found +#ifndef DBG_UTIL +inline +#endif + BOOL SvNumberFormatter::ImpLookupCurrencyEntryLoopBody( + const NfCurrencyEntry*& pFoundEntry, BOOL& bFoundBank, + const NfCurrencyEntry* pData, USHORT nPos, const String& rSymbol ) +{ + BOOL bFound; + if ( pData->GetSymbol() == rSymbol ) + { + bFound = TRUE; + bFoundBank = FALSE; + } + else if ( pData->GetBankSymbol() == rSymbol ) + { + bFound = TRUE; + bFoundBank = TRUE; + } + else + bFound = FALSE; + if ( bFound ) + { + if ( pFoundEntry && pFoundEntry != pData ) + { + pFoundEntry = NULL; + return FALSE; // break loop, not unique + } + if ( nPos == 0 ) + { // first entry is SYSTEM + pFoundEntry = MatchSystemCurrency(); + if ( pFoundEntry ) + return FALSE; // break loop + // even if there are more matching entries + // this one is propably the one we are looking for + else + pFoundEntry = pData; + } + else + pFoundEntry = pData; + } + return TRUE; +} + + +BOOL SvNumberFormatter::GetNewCurrencySymbolString( sal_uInt32 nFormat, + String& rStr, const NfCurrencyEntry** ppEntry /* = NULL */, + BOOL* pBank /* = NULL */ ) const +{ + rStr.Erase(); + if ( ppEntry ) + *ppEntry = NULL; + if ( pBank ) + *pBank = FALSE; + SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get( nFormat ); + if ( pFormat ) + { + String aSymbol, aExtension; + if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) ) + { + if ( ppEntry ) + { + BOOL bFoundBank = FALSE; + // we definiteley need an entry matching the format code string + const NfCurrencyEntry* pFoundEntry = GetCurrencyEntry( + bFoundBank, aSymbol, aExtension, pFormat->GetLanguage(), + TRUE ); + if ( pFoundEntry ) + { + *ppEntry = pFoundEntry; + if ( pBank ) + *pBank = bFoundBank; + pFoundEntry->BuildSymbolString( rStr, bFoundBank ); + } + } + if ( !rStr.Len() ) + { // analog zu BuildSymbolString + rStr = '['; + rStr += '$'; + if ( aSymbol.Search( '-' ) != STRING_NOTFOUND || + aSymbol.Search( ']' ) != STRING_NOTFOUND ) + { + rStr += '"'; + rStr += aSymbol; + rStr += '"'; + } + else + rStr += aSymbol; + if ( aExtension.Len() ) + rStr += aExtension; + rStr += ']'; + } + return TRUE; + } + } + return FALSE; +} + + +// static +const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry( BOOL & bFoundBank, + const String& rSymbol, const String& rExtension, + LanguageType eFormatLanguage, BOOL bOnlyStringLanguage ) +{ + xub_StrLen nExtLen = rExtension.Len(); + LanguageType eExtLang; + if ( nExtLen ) + { + sal_Int32 nExtLang = ::rtl::OUString( rExtension ).toInt32( 16 ); + if ( !nExtLang ) + eExtLang = LANGUAGE_DONTKNOW; + else + eExtLang = (LanguageType) ((nExtLang < 0) ? + -nExtLang : nExtLang); + } + else + eExtLang = LANGUAGE_DONTKNOW; + const NfCurrencyEntry* pFoundEntry = NULL; + const NfCurrencyTable& rTable = GetTheCurrencyTable(); + USHORT nCount = rTable.Count(); + BOOL bCont = TRUE; + + // first try with given extension language/country + if ( nExtLen ) + { + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + for ( USHORT j = 0; j < nCount && bCont; j++, ppData++ ) + { + LanguageType eLang = (*ppData)->GetLanguage(); + if ( eLang == eExtLang || + ((eExtLang == LANGUAGE_DONTKNOW) && + (eLang == LANGUAGE_SYSTEM)) + ) + { + bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank, + *ppData, j, rSymbol ); + } + } + } + + // ok? + if ( pFoundEntry || !bCont || (bOnlyStringLanguage && nExtLen) ) + return pFoundEntry; + + if ( !bOnlyStringLanguage ) + { + // now try the language/country of the number format + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + for ( USHORT j = 0; j < nCount && bCont; j++, ppData++ ) + { + LanguageType eLang = (*ppData)->GetLanguage(); + if ( eLang == eFormatLanguage || + ((eFormatLanguage == LANGUAGE_DONTKNOW) && + (eLang == LANGUAGE_SYSTEM)) + ) + { + bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank, + *ppData, j, rSymbol ); + } + } + + // ok? + if ( pFoundEntry || !bCont ) + return pFoundEntry; + } + + // then try without language/country if no extension specified + if ( !nExtLen ) + { + const NfCurrencyEntryPtr* ppData = rTable.GetData(); + for ( USHORT j = 0; j < nCount && bCont; j++, ppData++ ) + { + bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank, + *ppData, j, rSymbol ); + } + } + + return pFoundEntry; +} + + +void SvNumberFormatter::GetCompatibilityCurrency( String& rSymbol, String& rAbbrev ) const +{ + ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 > + xCurrencies = xLocaleData->getAllCurrencies(); + sal_Int32 nCurrencies = xCurrencies.getLength(); + sal_Int32 j; + for ( j=0; j < nCurrencies; ++j ) + { + if ( xCurrencies[j].UsedInCompatibleFormatCodes ) + { + rSymbol = xCurrencies[j].Symbol; + rAbbrev = xCurrencies[j].BankSymbol; + break; + } + } + if ( j >= nCurrencies ) + { + if (LocaleDataWrapper::areChecksEnabled()) + { + String aMsg( RTL_CONSTASCII_USTRINGPARAM( + "GetCompatibilityCurrency: none?")); + LocaleDataWrapper::outputCheckMessage( + xLocaleData->appendLocaleInfo( aMsg)); + } + rSymbol = xLocaleData->getCurrSymbol(); + rAbbrev = xLocaleData->getCurrBankSymbol(); + } +} + + +void lcl_CheckCurrencySymbolPosition( const NfCurrencyEntry& rCurr ) +{ + short nPos = -1; // -1:=unknown, 0:=vorne, 1:=hinten + short nNeg = -1; + switch ( rCurr.GetPositiveFormat() ) + { + case 0: // $1 + nPos = 0; + break; + case 1: // 1$ + nPos = 1; + break; + case 2: // $ 1 + nPos = 0; + break; + case 3: // 1 $ + nPos = 1; + break; + default: + LocaleDataWrapper::outputCheckMessage( + "lcl_CheckCurrencySymbolPosition: unknown PositiveFormat"); + break; + } + switch ( rCurr.GetNegativeFormat() ) + { + case 0: // ($1) + nNeg = 0; + break; + case 1: // -$1 + nNeg = 0; + break; + case 2: // $-1 + nNeg = 0; + break; + case 3: // $1- + nNeg = 0; + break; + case 4: // (1$) + nNeg = 1; + break; + case 5: // -1$ + nNeg = 1; + break; + case 6: // 1-$ + nNeg = 1; + break; + case 7: // 1$- + nNeg = 1; + break; + case 8: // -1 $ + nNeg = 1; + break; + case 9: // -$ 1 + nNeg = 0; + break; + case 10: // 1 $- + nNeg = 1; + break; + case 11: // $ -1 + nNeg = 0; + break; + case 12 : // $ 1- + nNeg = 0; + break; + case 13 : // 1- $ + nNeg = 1; + break; + case 14 : // ($ 1) + nNeg = 0; + break; + case 15 : // (1 $) + nNeg = 1; + break; + default: + LocaleDataWrapper::outputCheckMessage( + "lcl_CheckCurrencySymbolPosition: unknown NegativeFormat"); + break; + } + if ( nPos >= 0 && nNeg >= 0 && nPos != nNeg ) + { + ByteString aStr( "positions of currency symbols differ\nLanguage: " ); + aStr += ByteString::CreateFromInt32( rCurr.GetLanguage() ); + aStr += " <"; + aStr += ByteString( rCurr.GetSymbol(), RTL_TEXTENCODING_UTF8 ); + aStr += "> positive: "; + aStr += ByteString::CreateFromInt32( rCurr.GetPositiveFormat() ); + aStr += ( nPos ? " (postfix)" : " (prefix)" ); + aStr += ", negative: "; + aStr += ByteString::CreateFromInt32( rCurr.GetNegativeFormat() ); + aStr += ( nNeg ? " (postfix)" : " (prefix)" ); +#if 0 +// seems that there really are some currencies which differ, e.g. YugoDinar + DBG_ERRORFILE( aStr.GetBuffer() ); +#endif + } +} + + +// static +void SvNumberFormatter::ImpInitCurrencyTable() +{ + // racing condition possible: + // ::osl::MutexGuard aGuard( GetMutex() ); + // while ( !bCurrencyTableInitialized ) + // ImpInitCurrencyTable(); + static BOOL bInitializing = FALSE; + if ( bCurrencyTableInitialized || bInitializing ) + return ; + bInitializing = TRUE; + + RTL_LOGFILE_CONTEXT_AUTHOR( aTimeLog, "svl", "er93726", "SvNumberFormatter::ImpInitCurrencyTable" ); + + LanguageType eSysLang = SvtSysLocale().GetLanguage(); + LocaleDataWrapper* pLocaleData = new LocaleDataWrapper( + ::comphelper::getProcessServiceFactory(), + MsLangId::convertLanguageToLocale( eSysLang ) ); + // get user configured currency + String aConfiguredCurrencyAbbrev; + LanguageType eConfiguredCurrencyLanguage = LANGUAGE_SYSTEM; + SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage( + aConfiguredCurrencyAbbrev, eConfiguredCurrencyLanguage ); + USHORT nSecondarySystemCurrencyPosition = 0; + USHORT nMatchingSystemCurrencyPosition = 0; + NfCurrencyEntryPtr pEntry; + + // first entry is SYSTEM + pEntry = new NfCurrencyEntry( *pLocaleData, LANGUAGE_SYSTEM ); + theCurrencyTable::get().Insert( pEntry, 0 ); + USHORT nCurrencyPos = 1; + + ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > xLoc = + LocaleDataWrapper::getInstalledLocaleNames(); + sal_Int32 nLocaleCount = xLoc.getLength(); + RTL_LOGFILE_CONTEXT_TRACE1( aTimeLog, "number of locales: %ld", nLocaleCount ); + Locale const * const pLocales = xLoc.getConstArray(); + NfCurrencyTable &rCurrencyTable = theCurrencyTable::get(); + NfCurrencyTable &rLegacyOnlyCurrencyTable = theLegacyOnlyCurrencyTable::get(); + USHORT nLegacyOnlyCurrencyPos = 0; + for ( sal_Int32 nLocale = 0; nLocale < nLocaleCount; nLocale++ ) + { + LanguageType eLang = MsLangId::convertLocaleToLanguage( + pLocales[nLocale]); +#if OSL_DEBUG_LEVEL > 1 + LanguageType eReal = MsLangId::getRealLanguage( eLang ); + if ( eReal != eLang ) { + BOOL bBreak; + bBreak = TRUE; + } +#endif + pLocaleData->setLocale( pLocales[nLocale] ); + Sequence< Currency2 > aCurrSeq = pLocaleData->getAllCurrencies(); + sal_Int32 nCurrencyCount = aCurrSeq.getLength(); + Currency2 const * const pCurrencies = aCurrSeq.getConstArray(); + + // one default currency for each locale, insert first so it is found first + sal_Int32 nDefault; + for ( nDefault = 0; nDefault < nCurrencyCount; nDefault++ ) + { + if ( pCurrencies[nDefault].Default ) + break; + } + if ( nDefault < nCurrencyCount ) + pEntry = new NfCurrencyEntry( pCurrencies[nDefault], *pLocaleData, eLang ); + else + pEntry = new NfCurrencyEntry( *pLocaleData, eLang ); // first or ShellsAndPebbles + + if (LocaleDataWrapper::areChecksEnabled()) + lcl_CheckCurrencySymbolPosition( *pEntry ); + + rCurrencyTable.Insert( pEntry, nCurrencyPos++ ); + if ( !nSystemCurrencyPosition && (aConfiguredCurrencyAbbrev.Len() ? + pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev && + pEntry->GetLanguage() == eConfiguredCurrencyLanguage : FALSE) ) + nSystemCurrencyPosition = nCurrencyPos-1; + if ( !nMatchingSystemCurrencyPosition && + pEntry->GetLanguage() == eSysLang ) + nMatchingSystemCurrencyPosition = nCurrencyPos-1; + + // all remaining currencies for each locale + if ( nCurrencyCount > 1 ) + { + sal_Int32 nCurrency; + for ( nCurrency = 0; nCurrency < nCurrencyCount; nCurrency++ ) + { + if (pCurrencies[nCurrency].LegacyOnly) + { + pEntry = new NfCurrencyEntry( pCurrencies[nCurrency], *pLocaleData, eLang ); + rLegacyOnlyCurrencyTable.Insert( pEntry, nLegacyOnlyCurrencyPos++ ); + } + else if ( nCurrency != nDefault ) + { + pEntry = new NfCurrencyEntry( pCurrencies[nCurrency], *pLocaleData, eLang ); + // no dupes + BOOL bInsert = TRUE; + NfCurrencyEntry const * const * pData = rCurrencyTable.GetData(); + USHORT n = rCurrencyTable.Count(); + pData++; // skip first SYSTEM entry + for ( USHORT j=1; j<n; j++ ) + { + if ( *(*pData++) == *pEntry ) + { + bInsert = FALSE; + break; // for + } + } + if ( !bInsert ) + delete pEntry; + else + { + rCurrencyTable.Insert( pEntry, nCurrencyPos++ ); + if ( !nSecondarySystemCurrencyPosition && + (aConfiguredCurrencyAbbrev.Len() ? + pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev : + pEntry->GetLanguage() == eConfiguredCurrencyLanguage) ) + nSecondarySystemCurrencyPosition = nCurrencyPos-1; + if ( !nMatchingSystemCurrencyPosition && + pEntry->GetLanguage() == eSysLang ) + nMatchingSystemCurrencyPosition = nCurrencyPos-1; + } + } + } + } + } + if ( !nSystemCurrencyPosition ) + nSystemCurrencyPosition = nSecondarySystemCurrencyPosition; + if ((aConfiguredCurrencyAbbrev.Len() && !nSystemCurrencyPosition) && + LocaleDataWrapper::areChecksEnabled()) + LocaleDataWrapper::outputCheckMessage( + "SvNumberFormatter::ImpInitCurrencyTable: configured currency not in I18N locale data."); + // match SYSTEM if no configured currency found + if ( !nSystemCurrencyPosition ) + nSystemCurrencyPosition = nMatchingSystemCurrencyPosition; + if ((!aConfiguredCurrencyAbbrev.Len() && !nSystemCurrencyPosition) && + LocaleDataWrapper::areChecksEnabled()) + LocaleDataWrapper::outputCheckMessage( + "SvNumberFormatter::ImpInitCurrencyTable: system currency not in I18N locale data."); + delete pLocaleData; + SvtSysLocaleOptions::SetCurrencyChangeLink( + STATIC_LINK( NULL, SvNumberFormatter, CurrencyChangeLink ) ); + bInitializing = FALSE; + bCurrencyTableInitialized = TRUE; +} + + +USHORT SvNumberFormatter::GetCurrencyFormatStrings( NfWSStringsDtor& rStrArr, + const NfCurrencyEntry& rCurr, BOOL bBank ) const +{ + USHORT nDefault = 0; + if ( bBank ) + { // nur Bankensymbole + String aPositiveBank, aNegativeBank; + rCurr.BuildPositiveFormatString( aPositiveBank, TRUE, *xLocaleData, 1 ); + rCurr.BuildNegativeFormatString( aNegativeBank, TRUE, *xLocaleData, 1 ); + + WSStringPtr pFormat1 = new String( aPositiveBank ); + *pFormat1 += ';'; + WSStringPtr pFormat2 = new String( *pFormat1 ); + + String aRed( '[' ); + aRed += pFormatScanner->GetRedString(); + aRed += ']'; + + *pFormat2 += aRed; + + *pFormat1 += aNegativeBank; + *pFormat2 += aNegativeBank; + + rStrArr.Insert( pFormat1, rStrArr.Count() ); + rStrArr.Insert( pFormat2, rStrArr.Count() ); + nDefault = rStrArr.Count() - 1; + } + else + { // gemischte Formate wie in SvNumberFormatter::ImpGenerateFormats + // aber keine doppelten, wenn keine Nachkommastellen in Waehrung + String aPositive, aNegative, aPositiveNoDec, aNegativeNoDec, + aPositiveDashed, aNegativeDashed; + WSStringPtr pFormat1, pFormat2, pFormat3, pFormat4, pFormat5; + + String aRed( '[' ); + aRed += pFormatScanner->GetRedString(); + aRed += ']'; + + rCurr.BuildPositiveFormatString( aPositive, FALSE, *xLocaleData, 1 ); + rCurr.BuildNegativeFormatString( aNegative, FALSE, *xLocaleData, 1 ); + if ( rCurr.GetDigits() ) + { + rCurr.BuildPositiveFormatString( aPositiveNoDec, FALSE, *xLocaleData, 0 ); + rCurr.BuildNegativeFormatString( aNegativeNoDec, FALSE, *xLocaleData, 0 ); + rCurr.BuildPositiveFormatString( aPositiveDashed, FALSE, *xLocaleData, 2 ); + rCurr.BuildNegativeFormatString( aNegativeDashed, FALSE, *xLocaleData, 2 ); + + pFormat1 = new String( aPositiveNoDec ); + *pFormat1 += ';'; + pFormat3 = new String( *pFormat1 ); + pFormat5 = new String( aPositiveDashed ); + *pFormat5 += ';'; + + *pFormat1 += aNegativeNoDec; + + *pFormat3 += aRed; + *pFormat5 += aRed; + + *pFormat3 += aNegativeNoDec; + *pFormat5 += aNegativeDashed; + } + else + { + pFormat1 = NULL; + pFormat3 = NULL; + pFormat5 = NULL; + } + + pFormat2 = new String( aPositive ); + *pFormat2 += ';'; + pFormat4 = new String( *pFormat2 ); + + *pFormat2 += aNegative; + + *pFormat4 += aRed; + *pFormat4 += aNegative; + + if ( pFormat1 ) + rStrArr.Insert( pFormat1, rStrArr.Count() ); + rStrArr.Insert( pFormat2, rStrArr.Count() ); + if ( pFormat3 ) + rStrArr.Insert( pFormat3, rStrArr.Count() ); + rStrArr.Insert( pFormat4, rStrArr.Count() ); + nDefault = rStrArr.Count() - 1; + if ( pFormat5 ) + rStrArr.Insert( pFormat5, rStrArr.Count() ); + } + return nDefault; +} + + +//--- NfCurrencyEntry ---------------------------------------------------- + +NfCurrencyEntry::NfCurrencyEntry() + : eLanguage( LANGUAGE_DONTKNOW ), + nPositiveFormat(3), + nNegativeFormat(8), + nDigits(2), + cZeroChar('0') +{ +} + + +NfCurrencyEntry::NfCurrencyEntry( const LocaleDataWrapper& rLocaleData, LanguageType eLang ) +{ + aSymbol = rLocaleData.getCurrSymbol(); + aBankSymbol = rLocaleData.getCurrBankSymbol(); + eLanguage = eLang; + nPositiveFormat = rLocaleData.getCurrPositiveFormat(); + nNegativeFormat = rLocaleData.getCurrNegativeFormat(); + nDigits = rLocaleData.getCurrDigits(); + cZeroChar = rLocaleData.getCurrZeroChar(); +} + + +NfCurrencyEntry::NfCurrencyEntry( const ::com::sun::star::i18n::Currency & rCurr, + const LocaleDataWrapper& rLocaleData, LanguageType eLang ) +{ + aSymbol = rCurr.Symbol; + aBankSymbol = rCurr.BankSymbol; + eLanguage = eLang; + nPositiveFormat = rLocaleData.getCurrPositiveFormat(); + nNegativeFormat = rLocaleData.getCurrNegativeFormat(); + nDigits = rCurr.DecimalPlaces; + cZeroChar = rLocaleData.getCurrZeroChar(); +} + + +BOOL NfCurrencyEntry::operator==( const NfCurrencyEntry& r ) const +{ + return aSymbol == r.aSymbol + && aBankSymbol == r.aBankSymbol + && eLanguage == r.eLanguage + ; +} + + +void NfCurrencyEntry::SetEuro() +{ + aSymbol = NfCurrencyEntry::GetEuroSymbol(); + aBankSymbol.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "EUR" ) ); + eLanguage = LANGUAGE_DONTKNOW; + nPositiveFormat = 3; + nNegativeFormat = 8; + nDigits = 2; + cZeroChar = '0'; +} + + +BOOL NfCurrencyEntry::IsEuro() const +{ + if ( aBankSymbol.EqualsAscii( "EUR" ) ) + return TRUE; + String aEuro( NfCurrencyEntry::GetEuroSymbol() ); + return aSymbol == aEuro; +} + + +void NfCurrencyEntry::ApplyVariableInformation( const NfCurrencyEntry& r ) +{ + nPositiveFormat = r.nPositiveFormat; + nNegativeFormat = r.nNegativeFormat; + cZeroChar = r.cZeroChar; +} + + +void NfCurrencyEntry::BuildSymbolString( String& rStr, BOOL bBank, + BOOL bWithoutExtension ) const +{ + rStr = '['; + rStr += '$'; + if ( bBank ) + rStr += aBankSymbol; + else + { + if ( aSymbol.Search( '-' ) != STRING_NOTFOUND || aSymbol.Search( ']' ) != STRING_NOTFOUND ) + { + rStr += '"'; + rStr += aSymbol; + rStr += '"'; + } + else + rStr += aSymbol; + if ( !bWithoutExtension && eLanguage != LANGUAGE_DONTKNOW && eLanguage != LANGUAGE_SYSTEM ) + { + rStr += '-'; + rStr += String::CreateFromInt32( sal_Int32( eLanguage ), 16 ).ToUpperAscii(); + } + } + rStr += ']'; +} + + +void NfCurrencyEntry::Impl_BuildFormatStringNumChars( String& rStr, + const LocaleDataWrapper& rLoc, USHORT nDecimalFormat ) const +{ + rStr.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "###0" ) ); + rStr.Insert( rLoc.getNumThousandSep(), 1 ); + if ( nDecimalFormat && nDigits ) + { + rStr += rLoc.getNumDecimalSep(); + rStr.Expand( rStr.Len() + nDigits, (nDecimalFormat == 2 ? '-' : cZeroChar) ); + } +} + + +void NfCurrencyEntry::BuildPositiveFormatString( String& rStr, BOOL bBank, + const LocaleDataWrapper& rLoc, USHORT nDecimalFormat ) const +{ + Impl_BuildFormatStringNumChars( rStr, rLoc, nDecimalFormat ); + USHORT nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( + rLoc.getCurrPositiveFormat(), nPositiveFormat, bBank ); + CompletePositiveFormatString( rStr, bBank, nPosiForm ); +} + + +void NfCurrencyEntry::BuildNegativeFormatString( String& rStr, BOOL bBank, + const LocaleDataWrapper& rLoc, USHORT nDecimalFormat ) const +{ + Impl_BuildFormatStringNumChars( rStr, rLoc, nDecimalFormat ); + USHORT nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( + rLoc.getCurrNegativeFormat(), nNegativeFormat, bBank ); + CompleteNegativeFormatString( rStr, bBank, nNegaForm ); +} + + +void NfCurrencyEntry::CompletePositiveFormatString( String& rStr, BOOL bBank, + USHORT nPosiForm ) const +{ + String aSymStr; + BuildSymbolString( aSymStr, bBank ); + NfCurrencyEntry::CompletePositiveFormatString( rStr, aSymStr, nPosiForm ); +} + + +void NfCurrencyEntry::CompleteNegativeFormatString( String& rStr, BOOL bBank, + USHORT nNegaForm ) const +{ + String aSymStr; + BuildSymbolString( aSymStr, bBank ); + NfCurrencyEntry::CompleteNegativeFormatString( rStr, aSymStr, nNegaForm ); +} + + +// static +void NfCurrencyEntry::CompletePositiveFormatString( String& rStr, + const String& rSymStr, USHORT nPositiveFormat ) +{ + switch( nPositiveFormat ) + { + case 0: // $1 + rStr.Insert( rSymStr , 0 ); + break; + case 1: // 1$ + rStr += rSymStr; + break; + case 2: // $ 1 + { + rStr.Insert( ' ', 0 ); + rStr.Insert( rSymStr, 0 ); + } + break; + case 3: // 1 $ + { + rStr += ' '; + rStr += rSymStr; + } + break; + default: + DBG_ERROR("NfCurrencyEntry::CompletePositiveFormatString: unknown option"); + break; + } +} + + +// static +void NfCurrencyEntry::CompleteNegativeFormatString( String& rStr, + const String& rSymStr, USHORT nNegativeFormat ) +{ + switch( nNegativeFormat ) + { + case 0: // ($1) + { + rStr.Insert( rSymStr, 0); + rStr.Insert('(',0); + rStr += ')'; + } + break; + case 1: // -$1 + { + rStr.Insert( rSymStr, 0); + rStr.Insert('-',0); + } + break; + case 2: // $-1 + { + rStr.Insert('-',0); + rStr.Insert( rSymStr, 0); + } + break; + case 3: // $1- + { + rStr.Insert( rSymStr, 0); + rStr += '-'; + } + break; + case 4: // (1$) + { + rStr.Insert('(',0); + rStr += rSymStr; + rStr += ')'; + } + break; + case 5: // -1$ + { + rStr += rSymStr; + rStr.Insert('-',0); + } + break; + case 6: // 1-$ + { + rStr += '-'; + rStr += rSymStr; + } + break; + case 7: // 1$- + { + rStr += rSymStr; + rStr += '-'; + } + break; + case 8: // -1 $ + { + rStr += ' '; + rStr += rSymStr; + rStr.Insert('-',0); + } + break; + case 9: // -$ 1 + { + rStr.Insert(' ',0); + rStr.Insert( rSymStr, 0); + rStr.Insert('-',0); + } + break; + case 10: // 1 $- + { + rStr += ' '; + rStr += rSymStr; + rStr += '-'; + } + break; + case 11: // $ -1 + { + String aTmp( rSymStr ); + aTmp += ' '; + aTmp += '-'; + rStr.Insert( aTmp, 0 ); + } + break; + case 12 : // $ 1- + { + rStr.Insert(' ', 0); + rStr.Insert( rSymStr, 0); + rStr += '-'; + } + break; + case 13 : // 1- $ + { + rStr += '-'; + rStr += ' '; + rStr += rSymStr; + } + break; + case 14 : // ($ 1) + { + rStr.Insert(' ',0); + rStr.Insert( rSymStr, 0); + rStr.Insert('(',0); + rStr += ')'; + } + break; + case 15 : // (1 $) + { + rStr.Insert('(',0); + rStr += ' '; + rStr += rSymStr; + rStr += ')'; + } + break; + default: + DBG_ERROR("NfCurrencyEntry::CompleteNegativeFormatString: unknown option"); + break; + } +} + + +// static +USHORT NfCurrencyEntry::GetEffectivePositiveFormat( USHORT +#if ! NF_BANKSYMBOL_FIX_POSITION + nIntlFormat +#endif + , USHORT nCurrFormat, BOOL bBank ) +{ + if ( bBank ) + { +#if NF_BANKSYMBOL_FIX_POSITION + return 3; +#else + switch ( nIntlFormat ) + { + case 0: // $1 + nIntlFormat = 2; // $ 1 + break; + case 1: // 1$ + nIntlFormat = 3; // 1 $ + break; + case 2: // $ 1 + break; + case 3: // 1 $ + break; + default: + DBG_ERROR("NfCurrencyEntry::GetEffectivePositiveFormat: unknown option"); + break; + } + return nIntlFormat; +#endif + } + else + return nCurrFormat; +} + + +// nur aufrufen, wenn nCurrFormat wirklich mit Klammern ist +USHORT lcl_MergeNegativeParenthesisFormat( USHORT nIntlFormat, USHORT nCurrFormat ) +{ + short nSign = 0; // -1:=Klammer 0:=links, 1:=mitte, 2:=rechts + switch ( nIntlFormat ) + { + case 0: // ($1) + case 4: // (1$) + case 14 : // ($ 1) + case 15 : // (1 $) + return nCurrFormat; + case 1: // -$1 + case 5: // -1$ + case 8: // -1 $ + case 9: // -$ 1 + nSign = 0; + break; + case 2: // $-1 + case 6: // 1-$ + case 11 : // $ -1 + case 13 : // 1- $ + nSign = 1; + break; + case 3: // $1- + case 7: // 1$- + case 10: // 1 $- + case 12 : // $ 1- + nSign = 2; + break; + default: + DBG_ERROR("lcl_MergeNegativeParenthesisFormat: unknown option"); + break; + } + + switch ( nCurrFormat ) + { + case 0: // ($1) + switch ( nSign ) + { + case 0: + return 1; // -$1 + case 1: + return 2; // $-1 + case 2: + return 3; // $1- + } + break; + case 4: // (1$) + switch ( nSign ) + { + case 0: + return 5; // -1$ + case 1: + return 6; // 1-$ + case 2: + return 7; // 1$- + } + break; + case 14 : // ($ 1) + switch ( nSign ) + { + case 0: + return 9; // -$ 1 + case 1: + return 11; // $ -1 + case 2: + return 12; // $ 1- + } + break; + case 15 : // (1 $) + switch ( nSign ) + { + case 0: + return 8; // -1 $ + case 1: + return 13; // 1- $ + case 2: + return 10; // 1 $- + } + break; + } + return nCurrFormat; +} + + +// static +USHORT NfCurrencyEntry::GetEffectiveNegativeFormat( USHORT nIntlFormat, + USHORT nCurrFormat, BOOL bBank ) +{ + if ( bBank ) + { +#if NF_BANKSYMBOL_FIX_POSITION + return 8; +#else + switch ( nIntlFormat ) + { + case 0: // ($1) +// nIntlFormat = 14; // ($ 1) + nIntlFormat = 9; // -$ 1 + break; + case 1: // -$1 + nIntlFormat = 9; // -$ 1 + break; + case 2: // $-1 + nIntlFormat = 11; // $ -1 + break; + case 3: // $1- + nIntlFormat = 12; // $ 1- + break; + case 4: // (1$) +// nIntlFormat = 15; // (1 $) + nIntlFormat = 8; // -1 $ + break; + case 5: // -1$ + nIntlFormat = 8; // -1 $ + break; + case 6: // 1-$ + nIntlFormat = 13; // 1- $ + break; + case 7: // 1$- + nIntlFormat = 10; // 1 $- + break; + case 8: // -1 $ + break; + case 9: // -$ 1 + break; + case 10: // 1 $- + break; + case 11: // $ -1 + break; + case 12 : // $ 1- + break; + case 13 : // 1- $ + break; + case 14 : // ($ 1) +// nIntlFormat = 14; // ($ 1) + nIntlFormat = 9; // -$ 1 + break; + case 15 : // (1 $) +// nIntlFormat = 15; // (1 $) + nIntlFormat = 8; // -1 $ + break; + default: + DBG_ERROR("NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option"); + break; + } +#endif + } + else if ( nIntlFormat != nCurrFormat ) + { + switch ( nCurrFormat ) + { + case 0: // ($1) + nIntlFormat = lcl_MergeNegativeParenthesisFormat( + nIntlFormat, nCurrFormat ); + break; + case 1: // -$1 + nIntlFormat = nCurrFormat; + break; + case 2: // $-1 + nIntlFormat = nCurrFormat; + break; + case 3: // $1- + nIntlFormat = nCurrFormat; + break; + case 4: // (1$) + nIntlFormat = lcl_MergeNegativeParenthesisFormat( + nIntlFormat, nCurrFormat ); + break; + case 5: // -1$ + nIntlFormat = nCurrFormat; + break; + case 6: // 1-$ + nIntlFormat = nCurrFormat; + break; + case 7: // 1$- + nIntlFormat = nCurrFormat; + break; + case 8: // -1 $ + nIntlFormat = nCurrFormat; + break; + case 9: // -$ 1 + nIntlFormat = nCurrFormat; + break; + case 10: // 1 $- + nIntlFormat = nCurrFormat; + break; + case 11: // $ -1 + nIntlFormat = nCurrFormat; + break; + case 12 : // $ 1- + nIntlFormat = nCurrFormat; + break; + case 13 : // 1- $ + nIntlFormat = nCurrFormat; + break; + case 14 : // ($ 1) + nIntlFormat = lcl_MergeNegativeParenthesisFormat( + nIntlFormat, nCurrFormat ); + break; + case 15 : // (1 $) + nIntlFormat = lcl_MergeNegativeParenthesisFormat( + nIntlFormat, nCurrFormat ); + break; + default: + DBG_ERROR("NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option"); + break; + } + } + return nIntlFormat; +} + + +// we only support default encodings here +// static +sal_Char NfCurrencyEntry::GetEuroSymbol( rtl_TextEncoding eTextEncoding ) +{ + switch ( eTextEncoding ) + { + case RTL_TEXTENCODING_MS_1252 : // WNT Ansi + case RTL_TEXTENCODING_ISO_8859_1 : // UNX for use with TrueType fonts + return '\x80'; + case RTL_TEXTENCODING_ISO_8859_15 : // UNX real + return '\xA4'; + case RTL_TEXTENCODING_IBM_850 : // OS2 + return '\xD5'; + case RTL_TEXTENCODING_APPLE_ROMAN : // MAC + return '\xDB'; + default: // default system +#if WNT + return '\x80'; +#elif OS2 + return '\xD5'; +#elif UNX +// return '\xA4'; // #56121# 0xA4 waere korrekt fuer iso-8859-15 + return '\x80'; // aber Windoze-Code fuer die konvertierten TrueType-Fonts +#else +#error EuroSymbol is what? + return '\x80'; +#endif + } + return '\x80'; +} + + + diff --git a/svl/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx new file mode 100644 index 000000000000..52d37b9cd26f --- /dev/null +++ b/svl/source/numbers/zformat.cxx @@ -0,0 +1,4480 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zformat.cxx,v $ + * $Revision: 1.78.138.1 $ + * + * 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_svl.hxx" +#include <stdio.h> +#include <ctype.h> +#include <float.h> +// #include <math.h> +#include <errno.h> +#include <stdlib.h> +#include <tools/debug.hxx> +#include <i18npool/mslangid.hxx> +#include <rtl/math.hxx> +#include <rtl/instance.hxx> +#include <unotools/charclass.hxx> +#include <unotools/calendarwrapper.hxx> +#include <unotools/nativenumberwrapper.hxx> +#include <com/sun/star/i18n/CalendarFieldIndex.hpp> +#include <com/sun/star/i18n/CalendarDisplayIndex.hpp> +#include <com/sun/star/i18n/CalendarDisplayCode.hpp> +#include <com/sun/star/i18n/AmPmValue.hpp> + +#define _ZFORMAT_CXX +#include <svl/zformat.hxx> +#include "zforscan.hxx" + +#include "zforfind.hxx" +#include <svl/zforlist.hxx> +#include "numhead.hxx" +#include <unotools/digitgroupingiterator.hxx> +#include "nfsymbol.hxx" +using namespace svt; + +namespace { +struct Gregorian + : public rtl::StaticWithInit<const ::rtl::OUString, Gregorian> { + const ::rtl::OUString operator () () { + return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gregorian")); + } +}; +} + +const double _D_MAX_U_LONG_ = (double) 0xffffffff; // 4294967295.0 +const double _D_MAX_LONG_ = (double) 0x7fffffff; // 2147483647.0 +const USHORT _MAX_FRACTION_PREC = 3; +const double D_EPS = 1.0E-2; + +const double _D_MAX_D_BY_100 = 1.7E306; +const double _D_MIN_M_BY_1000 = 2.3E-305; + +static BYTE cCharWidths[ 128-32 ] = { + 1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2, + 3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3, + 2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2, + 1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2, + 2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1 +}; + +// static +xub_StrLen SvNumberformat::InsertBlanks( String& r, xub_StrLen nPos, sal_Unicode c ) +{ + if( c >= 32 ) + { + USHORT n = 2; // Default fuer Zeichen > 128 (HACK!) + if( c <= 127 ) + n = cCharWidths[ c - 32 ]; + while( n-- ) + r.Insert( ' ', nPos++ ); + } + return nPos; +} + +static long GetPrecExp( double fAbsVal ) +{ + DBG_ASSERT( fAbsVal > 0.0, "GetPrecExp: fAbsVal <= 0.0" ); + if ( fAbsVal < 1e-7 || fAbsVal > 1e7 ) + { // die Schere, ob's schneller ist oder nicht, liegt zwischen 1e6 und 1e7 + return (long) floor( log10( fAbsVal ) ) + 1; + } + else + { + long nPrecExp = 1; + while( fAbsVal < 1 ) + { + fAbsVal *= 10; + nPrecExp--; + } + while( fAbsVal >= 10 ) + { + fAbsVal /= 10; + nPrecExp++; + } + return nPrecExp; + } +} + +const USHORT nNewCurrencyVersionId = 0x434E; // "NC" +const sal_Unicode cNewCurrencyMagic = 0x01; // Magic for format code in comment +const USHORT nNewStandardFlagVersionId = 0x4653; // "SF" + +/***********************Funktion SvNumberformatInfo******************************/ + +void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo& rNumFor, USHORT nAnz ) +{ + for (USHORT i = 0; i < nAnz; i++) + { + sStrArray[i] = rNumFor.sStrArray[i]; + nTypeArray[i] = rNumFor.nTypeArray[i]; + } + eScannedType = rNumFor.eScannedType; + bThousand = rNumFor.bThousand; + nThousand = rNumFor.nThousand; + nCntPre = rNumFor.nCntPre; + nCntPost = rNumFor.nCntPost; + nCntExp = rNumFor.nCntExp; +} + +void ImpSvNumberformatInfo::Save(SvStream& rStream, USHORT nAnz) const +{ + for (USHORT i = 0; i < nAnz; i++) + { + rStream.WriteByteString( sStrArray[i], rStream.GetStreamCharSet() ); + short nType = nTypeArray[i]; + switch ( nType ) + { // der Krampf fuer Versionen vor SV_NUMBERFORMATTER_VERSION_NEW_CURR + case NF_SYMBOLTYPE_CURRENCY : + rStream << short( NF_SYMBOLTYPE_STRING ); + break; + case NF_SYMBOLTYPE_CURRDEL : + case NF_SYMBOLTYPE_CURREXT : + rStream << short(0); // werden ignoriert (hoffentlich..) + break; + default: + if ( nType > NF_KEY_LASTKEYWORD_SO5 ) + rStream << short( NF_SYMBOLTYPE_STRING ); // all new keywords are string + else + rStream << nType; + } + + } + rStream << eScannedType << bThousand << nThousand + << nCntPre << nCntPost << nCntExp; +} + +void ImpSvNumberformatInfo::Load(SvStream& rStream, USHORT nAnz) +{ + for (USHORT i = 0; i < nAnz; i++) + { + SvNumberformat::LoadString( rStream, sStrArray[i] ); + rStream >> nTypeArray[i]; + } + rStream >> eScannedType >> bThousand >> nThousand + >> nCntPre >> nCntPost >> nCntExp; +} + + +//============================================================================ + +// static +BYTE SvNumberNatNum::MapDBNumToNatNum( BYTE nDBNum, LanguageType eLang, BOOL bDate ) +{ + BYTE nNatNum = 0; + eLang = MsLangId::getRealLanguage( eLang ); // resolve SYSTEM etc. + eLang &= 0x03FF; // 10 bit primary language + if ( bDate ) + { + if ( nDBNum == 4 && eLang == LANGUAGE_KOREAN ) + nNatNum = 9; + else if ( nDBNum <= 3 ) + nNatNum = nDBNum; // known to be good for: zh,ja,ko / 1,2,3 + } + else + { + switch ( nDBNum ) + { + case 1: + switch ( eLang ) + { + case (LANGUAGE_CHINESE & 0x03FF) : nNatNum = 4; break; + case (LANGUAGE_JAPANESE & 0x03FF) : nNatNum = 1; break; + case (LANGUAGE_KOREAN & 0x03FF) : nNatNum = 1; break; + } + break; + case 2: + switch ( eLang ) + { + case (LANGUAGE_CHINESE & 0x03FF) : nNatNum = 5; break; + case (LANGUAGE_JAPANESE & 0x03FF) : nNatNum = 4; break; + case (LANGUAGE_KOREAN & 0x03FF) : nNatNum = 2; break; + } + break; + case 3: + switch ( eLang ) + { + case (LANGUAGE_CHINESE & 0x03FF) : nNatNum = 6; break; + case (LANGUAGE_JAPANESE & 0x03FF) : nNatNum = 5; break; + case (LANGUAGE_KOREAN & 0x03FF) : nNatNum = 3; break; + } + break; + case 4: + switch ( eLang ) + { + case (LANGUAGE_JAPANESE & 0x03FF) : nNatNum = 7; break; + case (LANGUAGE_KOREAN & 0x03FF) : nNatNum = 9; break; + } + break; + } + } + return nNatNum; +} + + +// static +BYTE SvNumberNatNum::MapNatNumToDBNum( BYTE nNatNum, LanguageType eLang, BOOL bDate ) +{ + BYTE nDBNum = 0; + eLang = MsLangId::getRealLanguage( eLang ); // resolve SYSTEM etc. + eLang &= 0x03FF; // 10 bit primary language + if ( bDate ) + { + if ( nNatNum == 9 && eLang == LANGUAGE_KOREAN ) + nDBNum = 4; + else if ( nNatNum <= 3 ) + nDBNum = nNatNum; // known to be good for: zh,ja,ko / 1,2,3 + } + else + { + switch ( nNatNum ) + { + case 1: + switch ( eLang ) + { + case (LANGUAGE_JAPANESE & 0x03FF) : nDBNum = 1; break; + case (LANGUAGE_KOREAN & 0x03FF) : nDBNum = 1; break; + } + break; + case 2: + switch ( eLang ) + { + case (LANGUAGE_KOREAN & 0x03FF) : nDBNum = 2; break; + } + break; + case 3: + switch ( eLang ) + { + case (LANGUAGE_KOREAN & 0x03FF) : nDBNum = 3; break; + } + break; + case 4: + switch ( eLang ) + { + case (LANGUAGE_CHINESE & 0x03FF) : nDBNum = 1; break; + case (LANGUAGE_JAPANESE & 0x03FF) : nDBNum = 2; break; + } + break; + case 5: + switch ( eLang ) + { + case (LANGUAGE_CHINESE & 0x03FF) : nDBNum = 2; break; + case (LANGUAGE_JAPANESE & 0x03FF) : nDBNum = 3; break; + } + break; + case 6: + switch ( eLang ) + { + case (LANGUAGE_CHINESE & 0x03FF) : nDBNum = 3; break; + } + break; + case 7: + switch ( eLang ) + { + case (LANGUAGE_JAPANESE & 0x03FF) : nDBNum = 4; break; + } + break; + case 8: + break; + case 9: + switch ( eLang ) + { + case (LANGUAGE_KOREAN & 0x03FF) : nDBNum = 4; break; + } + break; + case 10: + break; + case 11: + break; + } + } + return nDBNum; +} + +/***********************Funktionen SvNumFor******************************/ + +ImpSvNumFor::ImpSvNumFor() +{ + nAnzStrings = 0; + aI.nTypeArray = NULL; + aI.sStrArray = NULL; + aI.eScannedType = NUMBERFORMAT_UNDEFINED; + aI.bThousand = FALSE; + aI.nThousand = 0; + aI.nCntPre = 0; + aI.nCntPost = 0; + aI.nCntExp = 0; + pColor = NULL; +} + +ImpSvNumFor::~ImpSvNumFor() +{ + for (USHORT i = 0; i < nAnzStrings; i++) + aI.sStrArray[i].Erase(); + delete [] aI.sStrArray; + delete [] aI.nTypeArray; +} + +void ImpSvNumFor::Enlarge(USHORT nAnz) +{ + if ( nAnzStrings != nAnz ) + { + if ( aI.nTypeArray ) + delete [] aI.nTypeArray; + if ( aI.sStrArray ) + delete [] aI.sStrArray; + nAnzStrings = nAnz; + if ( nAnz ) + { + aI.nTypeArray = new short[nAnz]; + aI.sStrArray = new String[nAnz]; + } + else + { + aI.nTypeArray = NULL; + aI.sStrArray = NULL; + } + } +} + +void ImpSvNumFor::Copy( const ImpSvNumFor& rNumFor, ImpSvNumberformatScan* pSc ) +{ + Enlarge( rNumFor.nAnzStrings ); + aI.Copy( rNumFor.aI, nAnzStrings ); + sColorName = rNumFor.sColorName; + if ( pSc ) + pColor = pSc->GetColor( sColorName ); // #121103# don't copy pointer between documents + else + pColor = rNumFor.pColor; + aNatNum = rNumFor.aNatNum; +} + +void ImpSvNumFor::Save(SvStream& rStream) const +{ + rStream << nAnzStrings; + aI.Save(rStream, nAnzStrings); + rStream.WriteByteString( sColorName, rStream.GetStreamCharSet() ); +} + +void ImpSvNumFor::Load(SvStream& rStream, ImpSvNumberformatScan& rSc, + String& rLoadedColorName ) +{ + USHORT nAnz; + rStream >> nAnz; //! noch nicht direkt nAnzStrings wg. Enlarge + Enlarge( nAnz ); + aI.Load( rStream, nAnz ); + rStream.ReadByteString( sColorName, rStream.GetStreamCharSet() ); + rLoadedColorName = sColorName; + pColor = rSc.GetColor(sColorName); +} + + +BOOL ImpSvNumFor::HasNewCurrency() const +{ + for ( USHORT j=0; j<nAnzStrings; j++ ) + { + if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY ) + return TRUE; + } + return FALSE; +} + + +BOOL ImpSvNumFor::GetNewCurrencySymbol( String& rSymbol, + String& rExtension ) const +{ + for ( USHORT j=0; j<nAnzStrings; j++ ) + { + if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY ) + { + rSymbol = aI.sStrArray[j]; + if ( j < nAnzStrings-1 && aI.nTypeArray[j+1] == NF_SYMBOLTYPE_CURREXT ) + rExtension = aI.sStrArray[j+1]; + else + rExtension.Erase(); + return TRUE; + } + } + //! kein Erase an rSymbol, rExtension + return FALSE; +} + + +void ImpSvNumFor::SaveNewCurrencyMap( SvStream& rStream ) const +{ + USHORT j; + USHORT nCnt = 0; + for ( j=0; j<nAnzStrings; j++ ) + { + switch ( aI.nTypeArray[j] ) + { + case NF_SYMBOLTYPE_CURRENCY : + case NF_SYMBOLTYPE_CURRDEL : + case NF_SYMBOLTYPE_CURREXT : + nCnt++; + break; + } + } + rStream << nCnt; + for ( j=0; j<nAnzStrings; j++ ) + { + switch ( aI.nTypeArray[j] ) + { + case NF_SYMBOLTYPE_CURRENCY : + case NF_SYMBOLTYPE_CURRDEL : + case NF_SYMBOLTYPE_CURREXT : + rStream << j << aI.nTypeArray[j]; + break; + } + } +} + + +void ImpSvNumFor::LoadNewCurrencyMap( SvStream& rStream ) +{ + USHORT nCnt; + rStream >> nCnt; + for ( USHORT j=0; j<nCnt; j++ ) + { + USHORT nPos; + short nType; + rStream >> nPos >> nType; + if ( nPos < nAnzStrings ) + aI.nTypeArray[nPos] = nType; + } +} + + +/***********************Funktionen SvNumberformat************************/ + +enum BracketFormatSymbolType +{ + BRACKET_SYMBOLTYPE_FORMAT = -1, // subformat string + BRACKET_SYMBOLTYPE_COLOR = -2, // color + BRACKET_SYMBOLTYPE_ERROR = -3, // error + BRACKET_SYMBOLTYPE_DBNUM1 = -4, // DoubleByteNumber, represent numbers + BRACKET_SYMBOLTYPE_DBNUM2 = -5, // using CJK characters, Excel compatible. + BRACKET_SYMBOLTYPE_DBNUM3 = -6, + BRACKET_SYMBOLTYPE_DBNUM4 = -7, + BRACKET_SYMBOLTYPE_DBNUM5 = -8, + BRACKET_SYMBOLTYPE_DBNUM6 = -9, + BRACKET_SYMBOLTYPE_DBNUM7 = -10, + BRACKET_SYMBOLTYPE_DBNUM8 = -11, + BRACKET_SYMBOLTYPE_DBNUM9 = -12, + BRACKET_SYMBOLTYPE_LOCALE = -13, + BRACKET_SYMBOLTYPE_NATNUM0 = -14, // Our NativeNumber support, ASCII + BRACKET_SYMBOLTYPE_NATNUM1 = -15, // Our NativeNumber support, represent + BRACKET_SYMBOLTYPE_NATNUM2 = -16, // numbers using CJK, CTL, ... + BRACKET_SYMBOLTYPE_NATNUM3 = -17, + BRACKET_SYMBOLTYPE_NATNUM4 = -18, + BRACKET_SYMBOLTYPE_NATNUM5 = -19, + BRACKET_SYMBOLTYPE_NATNUM6 = -20, + BRACKET_SYMBOLTYPE_NATNUM7 = -21, + BRACKET_SYMBOLTYPE_NATNUM8 = -22, + BRACKET_SYMBOLTYPE_NATNUM9 = -23, + BRACKET_SYMBOLTYPE_NATNUM10 = -24, + BRACKET_SYMBOLTYPE_NATNUM11 = -25, + BRACKET_SYMBOLTYPE_NATNUM12 = -26, + BRACKET_SYMBOLTYPE_NATNUM13 = -27, + BRACKET_SYMBOLTYPE_NATNUM14 = -28, + BRACKET_SYMBOLTYPE_NATNUM15 = -29, + BRACKET_SYMBOLTYPE_NATNUM16 = -30, + BRACKET_SYMBOLTYPE_NATNUM17 = -31, + BRACKET_SYMBOLTYPE_NATNUM18 = -32, + BRACKET_SYMBOLTYPE_NATNUM19 = -33 +}; + +SvNumberformat::SvNumberformat( ImpSvNumberformatScan& rSc, LanguageType eLge ) + : + rScan(rSc), + eLnge(eLge), + nNewStandardDefined(0), + bStarFlag( FALSE ) +{ +} + +void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat ) +{ + sFormatstring = rFormat.sFormatstring; + eType = rFormat.eType; + eLnge = rFormat.eLnge; + fLimit1 = rFormat.fLimit1; + fLimit2 = rFormat.fLimit2; + eOp1 = rFormat.eOp1; + eOp2 = rFormat.eOp2; + bStandard = rFormat.bStandard; + bIsUsed = rFormat.bIsUsed; + sComment = rFormat.sComment; + nNewStandardDefined = rFormat.nNewStandardDefined; + + // #121103# when copying between documents, get color pointers from own scanner + ImpSvNumberformatScan* pColorSc = ( &rScan != &rFormat.rScan ) ? &rScan : NULL; + + for (USHORT i = 0; i < 4; i++) + NumFor[i].Copy(rFormat.NumFor[i], pColorSc); +} + +SvNumberformat::SvNumberformat( SvNumberformat& rFormat ) + : rScan(rFormat.rScan), bStarFlag( rFormat.bStarFlag ) +{ + ImpCopyNumberformat( rFormat ); +} + +SvNumberformat::SvNumberformat( SvNumberformat& rFormat, ImpSvNumberformatScan& rSc ) + : rScan(rSc), bStarFlag( rFormat.bStarFlag ) +{ + ImpCopyNumberformat( rFormat ); +} + + +BOOL lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType ) +{ + if ( nSymbolType > 0 ) + return TRUE; // conditions + switch ( nSymbolType ) + { + case BRACKET_SYMBOLTYPE_COLOR : + case BRACKET_SYMBOLTYPE_DBNUM1 : + case BRACKET_SYMBOLTYPE_DBNUM2 : + case BRACKET_SYMBOLTYPE_DBNUM3 : + case BRACKET_SYMBOLTYPE_DBNUM4 : + case BRACKET_SYMBOLTYPE_DBNUM5 : + case BRACKET_SYMBOLTYPE_DBNUM6 : + case BRACKET_SYMBOLTYPE_DBNUM7 : + case BRACKET_SYMBOLTYPE_DBNUM8 : + case BRACKET_SYMBOLTYPE_DBNUM9 : + case BRACKET_SYMBOLTYPE_LOCALE : + case BRACKET_SYMBOLTYPE_NATNUM0 : + case BRACKET_SYMBOLTYPE_NATNUM1 : + case BRACKET_SYMBOLTYPE_NATNUM2 : + case BRACKET_SYMBOLTYPE_NATNUM3 : + case BRACKET_SYMBOLTYPE_NATNUM4 : + case BRACKET_SYMBOLTYPE_NATNUM5 : + case BRACKET_SYMBOLTYPE_NATNUM6 : + case BRACKET_SYMBOLTYPE_NATNUM7 : + case BRACKET_SYMBOLTYPE_NATNUM8 : + case BRACKET_SYMBOLTYPE_NATNUM9 : + case BRACKET_SYMBOLTYPE_NATNUM10 : + case BRACKET_SYMBOLTYPE_NATNUM11 : + case BRACKET_SYMBOLTYPE_NATNUM12 : + case BRACKET_SYMBOLTYPE_NATNUM13 : + case BRACKET_SYMBOLTYPE_NATNUM14 : + case BRACKET_SYMBOLTYPE_NATNUM15 : + case BRACKET_SYMBOLTYPE_NATNUM16 : + case BRACKET_SYMBOLTYPE_NATNUM17 : + case BRACKET_SYMBOLTYPE_NATNUM18 : + case BRACKET_SYMBOLTYPE_NATNUM19 : + return TRUE; + } + return FALSE; +} + + +SvNumberformat::SvNumberformat(String& rString, + ImpSvNumberformatScan* pSc, + ImpSvNumberInputScan* pISc, + xub_StrLen& nCheckPos, + LanguageType& eLan, + BOOL bStan) + : + rScan(*pSc), + nNewStandardDefined(0), + bStarFlag( FALSE ) +{ + // If the group (AKA thousand) separator is a Non-Breaking Space (French) + // replace all occurences by a simple space. + // The tokens will be changed to the LocaleData separator again later on. + const sal_Unicode cNBSp = 0xA0; + const String& rThSep = GetFormatter().GetNumThousandSep(); + if ( rThSep.GetChar(0) == cNBSp && rThSep.Len() == 1 ) + { + xub_StrLen nIndex = 0; + do + nIndex = rString.SearchAndReplace( cNBSp, ' ', nIndex ); + while ( nIndex != STRING_NOTFOUND ); + } + + if (rScan.GetConvertMode()) + { + eLnge = rScan.GetNewLnge(); + eLan = eLnge; // Wechsel auch zurueckgeben + } + else + eLnge = eLan; + bStandard = bStan; + bIsUsed = FALSE; + fLimit1 = 0.0; + fLimit2 = 0.0; + eOp1 = NUMBERFORMAT_OP_NO; + eOp2 = NUMBERFORMAT_OP_NO; + eType = NUMBERFORMAT_DEFINED; + + BOOL bCancel = FALSE; + BOOL bCondition = FALSE; + short eSymbolType; + xub_StrLen nPos = 0; + xub_StrLen nPosOld; + nCheckPos = 0; + String aComment; + + // Split into 4 sub formats + USHORT nIndex; + for ( nIndex = 0; nIndex < 4 && !bCancel; nIndex++ ) + { + // Original language/country may have to be reestablished + if (rScan.GetConvertMode()) + (rScan.GetNumberformatter())->ChangeIntl(rScan.GetTmpLnge()); + + String sStr; + nPosOld = nPos; // Start position of substring + // first get bracketed prefixes; e.g. conditions, color + do + { + eSymbolType = ImpNextSymbol(rString, nPos, sStr); + if (eSymbolType > 0) // condition + { + if ( nIndex == 0 && !bCondition ) + { + bCondition = TRUE; + eOp1 = (SvNumberformatLimitOps) eSymbolType; + } + else if ( nIndex == 1 && bCondition ) + eOp2 = (SvNumberformatLimitOps) eSymbolType; + else // error + { + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + if (!bCancel) + { + double fNumber; + xub_StrLen nAnzChars = ImpGetNumber(rString, nPos, sStr); + if (nAnzChars > 0) + { + short F_Type; + if (!pISc->IsNumberFormat(sStr,F_Type,fNumber) || + ( F_Type != NUMBERFORMAT_NUMBER && + F_Type != NUMBERFORMAT_SCIENTIFIC) ) + { + fNumber = 0.0; + nPos = nPos - nAnzChars; + rString.Erase(nPos, nAnzChars); + rString.Insert('0',nPos); + nPos++; + } + } + else + { + fNumber = 0.0; + rString.Insert('0',nPos++); + } + if (nIndex == 0) + fLimit1 = fNumber; + else + fLimit2 = fNumber; + if ( rString.GetChar(nPos) == ']' ) + nPos++; + else + { + bCancel = TRUE; // break for + nCheckPos = nPos; + } + } + nPosOld = nPos; // position before string + } + else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) ) + { + switch ( eSymbolType ) + { + case BRACKET_SYMBOLTYPE_COLOR : + { + if ( NumFor[nIndex].GetColor() != NULL ) + { // error, more than one color + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + else + { + Color* pColor = pSc->GetColor( sStr); + NumFor[nIndex].SetColor( pColor, sStr); + if (pColor == NULL) + { // error + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + } + } + break; + case BRACKET_SYMBOLTYPE_NATNUM0 : + case BRACKET_SYMBOLTYPE_NATNUM1 : + case BRACKET_SYMBOLTYPE_NATNUM2 : + case BRACKET_SYMBOLTYPE_NATNUM3 : + case BRACKET_SYMBOLTYPE_NATNUM4 : + case BRACKET_SYMBOLTYPE_NATNUM5 : + case BRACKET_SYMBOLTYPE_NATNUM6 : + case BRACKET_SYMBOLTYPE_NATNUM7 : + case BRACKET_SYMBOLTYPE_NATNUM8 : + case BRACKET_SYMBOLTYPE_NATNUM9 : + case BRACKET_SYMBOLTYPE_NATNUM10 : + case BRACKET_SYMBOLTYPE_NATNUM11 : + case BRACKET_SYMBOLTYPE_NATNUM12 : + case BRACKET_SYMBOLTYPE_NATNUM13 : + case BRACKET_SYMBOLTYPE_NATNUM14 : + case BRACKET_SYMBOLTYPE_NATNUM15 : + case BRACKET_SYMBOLTYPE_NATNUM16 : + case BRACKET_SYMBOLTYPE_NATNUM17 : + case BRACKET_SYMBOLTYPE_NATNUM18 : + case BRACKET_SYMBOLTYPE_NATNUM19 : + { + if ( NumFor[nIndex].GetNatNum().IsSet() ) + { + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + else + { + sStr.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "NatNum" ) ); + //! eSymbolType is negative + BYTE nNum = sal::static_int_cast< BYTE >(0 - (eSymbolType - BRACKET_SYMBOLTYPE_NATNUM0)); + sStr += String::CreateFromInt32( nNum ); + NumFor[nIndex].SetNatNumNum( nNum, FALSE ); + } + } + break; + case BRACKET_SYMBOLTYPE_DBNUM1 : + case BRACKET_SYMBOLTYPE_DBNUM2 : + case BRACKET_SYMBOLTYPE_DBNUM3 : + case BRACKET_SYMBOLTYPE_DBNUM4 : + case BRACKET_SYMBOLTYPE_DBNUM5 : + case BRACKET_SYMBOLTYPE_DBNUM6 : + case BRACKET_SYMBOLTYPE_DBNUM7 : + case BRACKET_SYMBOLTYPE_DBNUM8 : + case BRACKET_SYMBOLTYPE_DBNUM9 : + { + if ( NumFor[nIndex].GetNatNum().IsSet() ) + { + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + else + { + sStr.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "DBNum" ) ); + //! eSymbolType is negative + BYTE nNum = sal::static_int_cast< BYTE >(1 - (eSymbolType - BRACKET_SYMBOLTYPE_DBNUM1)); + sStr += static_cast< sal_Unicode >('0' + nNum); + NumFor[nIndex].SetNatNumNum( nNum, TRUE ); + } + } + break; + case BRACKET_SYMBOLTYPE_LOCALE : + { + if ( NumFor[nIndex].GetNatNum().GetLang() != LANGUAGE_DONTKNOW ) + { + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + else + { + xub_StrLen nTmp = 2; + LanguageType eLang = ImpGetLanguageType( sStr, nTmp ); + if ( eLang == LANGUAGE_DONTKNOW ) + { + bCancel = TRUE; // break for + nCheckPos = nPosOld; + } + else + { + sStr.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "$-" ) ); + sStr += String::CreateFromInt32( sal_Int32( eLang ), 16 ).ToUpperAscii(); + NumFor[nIndex].SetNatNumLang( eLang ); + } + } + } + break; + } + if ( !bCancel ) + { + rString.Erase(nPosOld,nPos-nPosOld); + rString.Insert(sStr,nPosOld); + nPos = nPosOld + sStr.Len(); + rString.Insert(']', nPos); + rString.Insert('[', nPosOld); + nPos += 2; + nPosOld = nPos; // position before string + } + } + } while ( !bCancel && lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) ); + + // The remaining format code string + if ( !bCancel ) + { + if (eSymbolType == BRACKET_SYMBOLTYPE_FORMAT) + { + if (nIndex == 1 && eOp1 == NUMBERFORMAT_OP_NO) + eOp1 = NUMBERFORMAT_OP_GT; // undefined condition, default: > 0 + else if (nIndex == 2 && eOp2 == NUMBERFORMAT_OP_NO) + eOp2 = NUMBERFORMAT_OP_LT; // undefined condition, default: < 0 + if (sStr.Len() == 0) + { // empty sub format + } + else + { + xub_StrLen nStrPos = pSc->ScanFormat( sStr, aComment ); + USHORT nAnz = pSc->GetAnzResStrings(); + if (nAnz == 0) // error + nStrPos = 1; + if (nStrPos == 0) // ok + { + // e.g. Thai T speciality + if (pSc->GetNatNumModifier() && !NumFor[nIndex].GetNatNum().IsSet()) + { + String aNat( RTL_CONSTASCII_USTRINGPARAM( "[NatNum")); + aNat += String::CreateFromInt32( pSc->GetNatNumModifier()); + aNat += ']'; + sStr.Insert( aNat, 0); + NumFor[nIndex].SetNatNumNum( pSc->GetNatNumModifier(), FALSE ); + } + // #i53826# #i42727# For the Thai T speciality we need + // to freeze the locale and immunize it against + // conversions during exports, just in case we want to + // save to Xcl. This disables the feature of being able + // to convert a NatNum to another locale. You can't + // have both. + // FIXME: implement a specialized export conversion + // that works on tokens (have to tokenize all first) + // and doesn't use the format string and + // PutandConvertEntry() to LANGUAGE_ENGLISH_US in + // sc/source/filter/excel/xestyle.cxx + // XclExpNumFmtBuffer::WriteFormatRecord(). + LanguageType eLanguage; + if (NumFor[nIndex].GetNatNum().GetNatNum() == 1 && + ((eLanguage = + MsLangId::getRealLanguage( eLan)) + == LANGUAGE_THAI) && + NumFor[nIndex].GetNatNum().GetLang() == + LANGUAGE_DONTKNOW) + { + String aLID( RTL_CONSTASCII_USTRINGPARAM( "[$-")); + aLID += String::CreateFromInt32( sal_Int32( + eLanguage), 16 ).ToUpperAscii(); + aLID += ']'; + sStr.Insert( aLID, 0); + NumFor[nIndex].SetNatNumLang( eLanguage); + } + rString.Erase(nPosOld,nPos-nPosOld); + rString.Insert(sStr,nPosOld); + nPos = nPosOld + sStr.Len(); + if (nPos < rString.Len()) + { + rString.Insert(';',nPos); + nPos++; + } + NumFor[nIndex].Enlarge(nAnz); + pSc->CopyInfo(&(NumFor[nIndex].Info()), nAnz); + // type check + if (nIndex == 0) + eType = (short) NumFor[nIndex].Info().eScannedType; + else if (nIndex == 3) + { // #77026# Everything recognized IS text + NumFor[nIndex].Info().eScannedType = NUMBERFORMAT_TEXT; + } + else if ( (short) NumFor[nIndex].Info().eScannedType != + eType) + eType = NUMBERFORMAT_DEFINED; + } + else + { + nCheckPos = nPosOld + nStrPos; // error in string + bCancel = TRUE; // break for + } + } + } + else if (eSymbolType == BRACKET_SYMBOLTYPE_ERROR) // error + { + nCheckPos = nPosOld; + bCancel = TRUE; + } + else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) ) + { + nCheckPos = nPosOld+1; // error, prefix in string + bCancel = TRUE; // break for + } + } + if ( bCancel && !nCheckPos ) + nCheckPos = 1; // nCheckPos is used as an error condition + if ( !bCancel ) + { + if ( NumFor[nIndex].GetNatNum().IsSet() && + NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW ) + NumFor[nIndex].SetNatNumLang( eLan ); + } + if (rString.Len() == nPos) + { + if ( nIndex == 2 && eSymbolType == BRACKET_SYMBOLTYPE_FORMAT && + rString.GetChar(nPos-1) == ';' ) + { // #83510# A 4th subformat explicitly specified to be empty + // hides any text. Need the type here for HasTextFormat() + NumFor[3].Info().eScannedType = NUMBERFORMAT_TEXT; + } + bCancel = TRUE; + } + if ( NumFor[nIndex].GetNatNum().IsSet() ) + NumFor[nIndex].SetNatNumDate( + (NumFor[nIndex].Info().eScannedType & NUMBERFORMAT_DATE) != 0 ); + } + + if ( bCondition && !nCheckPos ) + { + if ( nIndex == 1 && NumFor[0].GetnAnz() == 0 && + rString.GetChar(rString.Len()-1) != ';' ) + { // No format code => GENERAL but not if specified empty + String aAdd( pSc->GetStandardName() ); + String aTmp; + if ( !pSc->ScanFormat( aAdd, aTmp ) ) + { + USHORT nAnz = pSc->GetAnzResStrings(); + if ( nAnz ) + { + NumFor[0].Enlarge(nAnz); + pSc->CopyInfo( &(NumFor[0].Info()), nAnz ); + rString += aAdd; + } + } + } + else if ( nIndex == 1 && NumFor[nIndex].GetnAnz() == 0 && + rString.GetChar(rString.Len()-1) != ';' && + (NumFor[0].GetnAnz() > 1 || (NumFor[0].GetnAnz() == 1 && + NumFor[0].Info().nTypeArray[0] != NF_KEY_GENERAL)) ) + { // No trailing second subformat => GENERAL but not if specified empty + // and not if first subformat is GENERAL + String aAdd( pSc->GetStandardName() ); + String aTmp; + if ( !pSc->ScanFormat( aAdd, aTmp ) ) + { + USHORT nAnz = pSc->GetAnzResStrings(); + if ( nAnz ) + { + NumFor[nIndex].Enlarge(nAnz); + pSc->CopyInfo( &(NumFor[nIndex].Info()), nAnz ); + rString += ';'; + rString += aAdd; + } + } + } + else if ( nIndex == 2 && NumFor[nIndex].GetnAnz() == 0 && + rString.GetChar(rString.Len()-1) != ';' && + eOp2 != NUMBERFORMAT_OP_NO ) + { // No trailing third subformat => GENERAL but not if specified empty + String aAdd( pSc->GetStandardName() ); + String aTmp; + if ( !pSc->ScanFormat( aAdd, aTmp ) ) + { + USHORT nAnz = pSc->GetAnzResStrings(); + if ( nAnz ) + { + NumFor[nIndex].Enlarge(nAnz); + pSc->CopyInfo( &(NumFor[nIndex].Info()), nAnz ); + rString += ';'; + rString += aAdd; + } + } + } + } + sFormatstring = rString; + if ( aComment.Len() ) + { + SetComment( aComment ); // setzt sComment und sFormatstring + rString = sFormatstring; // geaenderten sFormatstring uebernehmen + } + if (NumFor[2].GetnAnz() == 0 && // kein 3. Teilstring + eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO && + fLimit1 == 0.0 && fLimit2 == 0.0) + eOp1 = NUMBERFORMAT_OP_GE; // 0 zum ersten Format dazu + +} + +SvNumberformat::~SvNumberformat() +{ +} + +//--------------------------------------------------------------------------- +// Next_Symbol +//--------------------------------------------------------------------------- +// Zerlegt die Eingabe in Symbole fuer die weitere +// Verarbeitung (Turing-Maschine). +//--------------------------------------------------------------------------- +// Ausgangs Zustand = SsStart +//---------------+-------------------+-----------------------+--------------- +// Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand +//---------------+-------------------+-----------------------+--------------- +// SsStart | ; | Pos-- | SsGetString +// | [ | Symbol += Zeichen | SsGetBracketed +// | ] | Fehler | SsStop +// | BLANK | | +// | Sonst | Symbol += Zeichen | SsGetString +//---------------+-------------------+-----------------------+--------------- +// SsGetString | ; | | SsStop +// | Sonst | Symbol+=Zeichen | +//---------------+-------------------+-----------------------+--------------- +// SsGetBracketed| <, > = | del [ | +// | | Symbol += Zeichen | SsGetCon +// | BLANK | | +// | h, H, m, M, s, S | Symbol += Zeichen | SsGetTime +// | sonst | del [ | +// | | Symbol += Zeichen | SsGetPrefix +//---------------+-------------------+-----------------------+--------------- +// SsGetTime | ] | Symbol += Zeichen | SsGetString +// | h, H, m, M, s, S | Symbol += Zeichen, * | SsGetString +// | sonst | del [; Symbol+=Zeichen| SsGetPrefix +//---------------+-------------------+-----------------------+--------------- +// SsGetPrefix | ] | | SsStop +// | sonst | Symbol += Zeichen | +//---------------+-------------------+-----------------------+--------------- +// SsGetCon | >, = | Symbol+=Zeichen | +// | ] | | SsStop +// | sonst | Fehler | SsStop +//---------------+-------------------+-----------------------+--------------- +// * : Sonderbedingung + +enum ScanState +{ + SsStop, + SsStart, + SsGetCon, // condition + SsGetString, // format string + SsGetPrefix, // color or NatNumN + SsGetTime, // [HH] for time + SsGetBracketed // any [...] not decided yet +}; + + +// read a string until ']' and delete spaces in input +// static +xub_StrLen SvNumberformat::ImpGetNumber(String& rString, + xub_StrLen& nPos, + String& sSymbol) +{ + xub_StrLen nStartPos = nPos; + sal_Unicode cToken; + xub_StrLen nLen = rString.Len(); + sSymbol.Erase(); + while ( nPos < nLen && ((cToken = rString.GetChar(nPos)) != ']') ) + { + if (cToken == ' ') + { // delete spaces + rString.Erase(nPos,1); + nLen--; + } + else + { + nPos++; + sSymbol += cToken; + } + } + return nPos - nStartPos; +} + + +// static +LanguageType SvNumberformat::ImpGetLanguageType( const String& rString, + xub_StrLen& nPos ) +{ + sal_Int32 nNum = 0; + sal_Unicode cToken = 0; + xub_StrLen nLen = rString.Len(); + while ( nPos < nLen && ((cToken = rString.GetChar(nPos)) != ']') ) + { + if ( '0' <= cToken && cToken <= '9' ) + { + nNum *= 16; + nNum += cToken - '0'; + } + else if ( 'a' <= cToken && cToken <= 'f' ) + { + nNum *= 16; + nNum += cToken - 'a' + 10; + } + else if ( 'A' <= cToken && cToken <= 'F' ) + { + nNum *= 16; + nNum += cToken - 'A' + 10; + } + else + return LANGUAGE_DONTKNOW; + ++nPos; + } + return (nNum && (cToken == ']' || nPos == nLen)) ? (LanguageType)nNum : + LANGUAGE_DONTKNOW; +} + + +short SvNumberformat::ImpNextSymbol(String& rString, + xub_StrLen& nPos, + String& sSymbol) +{ + short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; + sal_Unicode cToken; + sal_Unicode cLetter = ' '; // Zwischenergebnis + xub_StrLen nLen = rString.Len(); + ScanState eState = SsStart; + sSymbol.Erase(); + const String* pKeywords = rScan.GetKeywords(); + while (nPos < nLen && eState != SsStop) + { + cToken = rString.GetChar(nPos); + nPos++; + switch (eState) + { + case SsStart: + { + if (cToken == '[') + { + eState = SsGetBracketed; + sSymbol += cToken; + } + else if (cToken == ';') + { + eState = SsGetString; + nPos--; + eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; + } + else if (cToken == ']') + { + eState = SsStop; + eSymbolType = BRACKET_SYMBOLTYPE_ERROR; + } + else if (cToken == ' ') // Skip Blanks + { + rString.Erase(nPos-1,1); + nPos--; + nLen--; + } + else + { + sSymbol += cToken; + eState = SsGetString; + eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; + } + } + break; + case SsGetBracketed: + { + switch (cToken) + { + case '<': + case '>': + case '=': + { + sSymbol.EraseAllChars('['); + sSymbol += cToken; + cLetter = cToken; + eState = SsGetCon; + switch (cToken) + { + case '<': eSymbolType = NUMBERFORMAT_OP_LT; break; + case '>': eSymbolType = NUMBERFORMAT_OP_GT; break; + case '=': eSymbolType = NUMBERFORMAT_OP_EQ; break; + default: break; + } + } + break; + case ' ': + { + rString.Erase(nPos-1,1); + nPos--; + nLen--; + } + break; + case '$' : + { + if ( rString.GetChar(nPos) == '-' ) + { // [$-xxx] locale + sSymbol.EraseAllChars('['); + eSymbolType = BRACKET_SYMBOLTYPE_LOCALE; + eState = SsGetPrefix; + } + else + { // currency as of SV_NUMBERFORMATTER_VERSION_NEW_CURR + eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; + eState = SsGetString; + } + sSymbol += cToken; + } + break; + case '~' : + { // calendarID as of SV_NUMBERFORMATTER_VERSION_CALENDAR + eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; + sSymbol += cToken; + eState = SsGetString; + } + break; + default: + { + static const String aNatNum( RTL_CONSTASCII_USTRINGPARAM( "NATNUM" ) ); + static const String aDBNum( RTL_CONSTASCII_USTRINGPARAM( "DBNUM" ) ); + String aUpperNatNum( rChrCls().toUpper( rString, nPos-1, aNatNum.Len() ) ); + String aUpperDBNum( rChrCls().toUpper( rString, nPos-1, aDBNum.Len() ) ); + sal_Unicode cUpper = aUpperNatNum.GetChar(0); + sal_Int32 nNatNumNum = rString.Copy( nPos-1+aNatNum.Len() ).ToInt32(); + sal_Unicode cDBNum = rString.GetChar( nPos-1+aDBNum.Len() ); + if ( aUpperNatNum == aNatNum && 0 <= nNatNumNum && nNatNumNum <= 19 ) + { + sSymbol.EraseAllChars('['); + sSymbol += rString.Copy( --nPos, aNatNum.Len()+1 ); + nPos += aNatNum.Len()+1; + //! SymbolType is negative + eSymbolType = (short) (BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum); + eState = SsGetPrefix; + } + else if ( aUpperDBNum == aDBNum && '1' <= cDBNum && cDBNum <= '9' ) + { + sSymbol.EraseAllChars('['); + sSymbol += rString.Copy( --nPos, aDBNum.Len()+1 ); + nPos += aDBNum.Len()+1; + //! SymbolType is negative + eSymbolType = sal::static_int_cast< short >( + BRACKET_SYMBOLTYPE_DBNUM1 - (cDBNum - '1')); + eState = SsGetPrefix; + } + else if (cUpper == pKeywords[NF_KEY_H].GetChar(0) || // H + cUpper == pKeywords[NF_KEY_MI].GetChar(0) || // M + cUpper == pKeywords[NF_KEY_S].GetChar(0) ) // S + { + sSymbol += cToken; + eState = SsGetTime; + cLetter = cToken; + } + else + { + sSymbol.EraseAllChars('['); + sSymbol += cToken; + eSymbolType = BRACKET_SYMBOLTYPE_COLOR; + eState = SsGetPrefix; + } + } + break; + } + } + break; + case SsGetString: + { + if (cToken == ';') + eState = SsStop; + else + sSymbol += cToken; + } + break; + case SsGetTime: + { + if (cToken == ']') + { + sSymbol += cToken; + eState = SsGetString; + eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; + } + else + { + sal_Unicode cUpper = rChrCls().toUpper( rString, nPos-1, 1 ).GetChar(0); + if (cUpper == pKeywords[NF_KEY_H].GetChar(0) || // H + cUpper == pKeywords[NF_KEY_MI].GetChar(0) || // M + cUpper == pKeywords[NF_KEY_S].GetChar(0) ) // S + { + if (cLetter == cToken) + { + sSymbol += cToken; + cLetter = ' '; + } + else + { + sSymbol.EraseAllChars('['); + sSymbol += cToken; + eState = SsGetPrefix; + } + } + else + { + sSymbol.EraseAllChars('['); + sSymbol += cToken; + eSymbolType = BRACKET_SYMBOLTYPE_COLOR; + eState = SsGetPrefix; + } + } + } + break; + case SsGetCon: + { + switch (cToken) + { + case '<': + { + eState = SsStop; + eSymbolType = BRACKET_SYMBOLTYPE_ERROR; + } + break; + case '>': + { + if (cLetter == '<') + { + sSymbol += cToken; + cLetter = ' '; + eState = SsStop; + eSymbolType = NUMBERFORMAT_OP_NE; + } + else + { + eState = SsStop; + eSymbolType = BRACKET_SYMBOLTYPE_ERROR; + } + } + break; + case '=': + { + if (cLetter == '<') + { + sSymbol += cToken; + cLetter = ' '; + eSymbolType = NUMBERFORMAT_OP_LE; + } + else if (cLetter == '>') + { + sSymbol += cToken; + cLetter = ' '; + eSymbolType = NUMBERFORMAT_OP_GE; + } + else + { + eState = SsStop; + eSymbolType = BRACKET_SYMBOLTYPE_ERROR; + } + } + break; + case ' ': + { + rString.Erase(nPos-1,1); + nPos--; + nLen--; + } + break; + default: + { + eState = SsStop; + nPos--; + } + break; + } + } + break; + case SsGetPrefix: + { + if (cToken == ']') + eState = SsStop; + else + sSymbol += cToken; + } + break; + default: + break; + } // of switch + } // of while + + return eSymbolType; +} + +NfHackConversion SvNumberformat::Load( SvStream& rStream, + ImpSvNumMultipleReadHeader& rHdr, SvNumberFormatter* pHackConverter, + ImpSvNumberInputScan& rISc ) +{ + rHdr.StartEntry(); + USHORT nOp1, nOp2; + SvNumberformat::LoadString( rStream, sFormatstring ); + rStream >> eType >> fLimit1 >> fLimit2 + >> nOp1 >> nOp2 >> bStandard >> bIsUsed; + NfHackConversion eHackConversion = NF_CONVERT_NONE; + BOOL bOldConvert = FALSE; + LanguageType eOldTmpLang = 0; + LanguageType eOldNewLang = 0; + if ( pHackConverter ) + { // werden nur hierbei gebraucht + bOldConvert = rScan.GetConvertMode(); + eOldTmpLang = rScan.GetTmpLnge(); + eOldNewLang = rScan.GetNewLnge(); + } + String aLoadedColorName; + for (USHORT i = 0; i < 4; i++) + { + NumFor[i].Load( rStream, rScan, aLoadedColorName ); + if ( pHackConverter && eHackConversion == NF_CONVERT_NONE ) + { + //! HACK! ER 29.07.97 13:52 + // leider wurde nicht gespeichert, was SYSTEM on Save wirklich war :-/ + // aber immerhin wird manchmal fuer einen Entry FARBE oder COLOR gespeichert.. + // System-German FARBE nach System-xxx COLOR umsetzen und vice versa, + //! geht davon aus, dass onSave nur GERMAN und ENGLISH KeyWords in + //! ImpSvNumberformatScan existierten + if ( aLoadedColorName.Len() && !NumFor[i].GetColor() + && aLoadedColorName != rScan.GetColorString() ) + { + if ( rScan.GetColorString().EqualsAscii( "FARBE" ) ) + { // English -> German + eHackConversion = NF_CONVERT_ENGLISH_GERMAN; + rScan.GetNumberformatter()->ChangeIntl( LANGUAGE_ENGLISH_US ); + rScan.SetConvertMode( LANGUAGE_ENGLISH_US, LANGUAGE_GERMAN ); + } + else + { // German -> English + eHackConversion = NF_CONVERT_GERMAN_ENGLISH; + rScan.GetNumberformatter()->ChangeIntl( LANGUAGE_GERMAN ); + rScan.SetConvertMode( LANGUAGE_GERMAN, LANGUAGE_ENGLISH_US ); + } + String aColorName = NumFor[i].GetColorName(); + const Color* pColor = rScan.GetColor( aColorName ); + if ( !pColor && aLoadedColorName == aColorName ) + eHackConversion = NF_CONVERT_NONE; + rScan.GetNumberformatter()->ChangeIntl( LANGUAGE_SYSTEM ); + rScan.SetConvertMode( eOldTmpLang, eOldNewLang ); + rScan.SetConvertMode( bOldConvert ); + } + } + } + eOp1 = (SvNumberformatLimitOps) nOp1; + eOp2 = (SvNumberformatLimitOps) nOp2; + String aComment; // wird nach dem NewCurrency-Geraffel richtig gesetzt + if ( rHdr.BytesLeft() ) + { // ab SV_NUMBERFORMATTER_VERSION_NEWSTANDARD + SvNumberformat::LoadString( rStream, aComment ); + rStream >> nNewStandardDefined; + } + + xub_StrLen nNewCurrencyEnd = STRING_NOTFOUND; + BOOL bNewCurrencyComment = ( aComment.GetChar(0) == cNewCurrencyMagic && + (nNewCurrencyEnd = aComment.Search( cNewCurrencyMagic, 1 )) != STRING_NOTFOUND ); + BOOL bNewCurrencyLoaded = FALSE; + BOOL bNewCurrency = FALSE; + + BOOL bGoOn = TRUE; + while ( rHdr.BytesLeft() && bGoOn ) + { // as of SV_NUMBERFORMATTER_VERSION_NEW_CURR + USHORT nId; + rStream >> nId; + switch ( nId ) + { + case nNewCurrencyVersionId : + { + bNewCurrencyLoaded = TRUE; + rStream >> bNewCurrency; + if ( bNewCurrency ) + { + for ( USHORT j=0; j<4; j++ ) + { + NumFor[j].LoadNewCurrencyMap( rStream ); + } + } + } + break; + case nNewStandardFlagVersionId : + rStream >> bStandard; // the real standard flag + break; + default: + DBG_ERRORFILE( "SvNumberformat::Load: unknown header bytes left nId" ); + bGoOn = FALSE; // stop reading unknown stream left over of newer versions + // Would be nice to have multiple read/write headers instead + // but old versions wouldn't know it, TLOT. + } + } + rHdr.EndEntry(); + + if ( bNewCurrencyLoaded ) + { + if ( bNewCurrency && bNewCurrencyComment ) + { // original Formatstring und Kommentar wiederherstellen + sFormatstring = aComment.Copy( 1, nNewCurrencyEnd-1 ); + aComment.Erase( 0, nNewCurrencyEnd+1 ); + } + } + else if ( bNewCurrencyComment ) + { // neu, aber mit Version vor SV_NUMBERFORMATTER_VERSION_NEW_CURR gespeichert + // original Formatstring und Kommentar wiederherstellen + sFormatstring = aComment.Copy( 1, nNewCurrencyEnd-1 ); + aComment.Erase( 0, nNewCurrencyEnd+1 ); + // Zustaende merken + short nDefined = ( eType & NUMBERFORMAT_DEFINED ); + USHORT nNewStandard = nNewStandardDefined; + // neu parsen etc. + String aStr( sFormatstring ); + xub_StrLen nCheckPos = 0; + SvNumberformat* pFormat = new SvNumberformat( aStr, &rScan, &rISc, + nCheckPos, eLnge, bStandard ); + DBG_ASSERT( !nCheckPos, "SvNumberformat::Load: NewCurrencyRescan nCheckPos" ); + ImpCopyNumberformat( *pFormat ); + delete pFormat; + // Zustaende wiederherstellen + eType |= nDefined; + if ( nNewStandard ) + SetNewStandardDefined( nNewStandard ); + } + SetComment( aComment ); + + if ( eHackConversion != NF_CONVERT_NONE ) + { //! und weiter mit dem HACK! + switch ( eHackConversion ) + { + case NF_CONVERT_ENGLISH_GERMAN : + ConvertLanguage( *pHackConverter, + LANGUAGE_ENGLISH_US, LANGUAGE_GERMAN, TRUE ); + break; + case NF_CONVERT_GERMAN_ENGLISH : + ConvertLanguage( *pHackConverter, + LANGUAGE_GERMAN, LANGUAGE_ENGLISH_US, TRUE ); + break; + default: + DBG_ERRORFILE( "SvNumberformat::Load: eHackConversion unknown" ); + } + } + return eHackConversion; +} + +void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter, + LanguageType eConvertFrom, LanguageType eConvertTo, BOOL bSystem ) +{ + xub_StrLen nCheckPos; + sal_uInt32 nKey; + short nType = eType; + String aFormatString( sFormatstring ); + if ( bSystem ) + rConverter.PutandConvertEntrySystem( aFormatString, nCheckPos, nType, + nKey, eConvertFrom, eConvertTo ); + else + rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType, + nKey, eConvertFrom, eConvertTo ); + const SvNumberformat* pFormat = rConverter.GetEntry( nKey ); + DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion ohne Format" ); + if ( pFormat ) + { + ImpCopyNumberformat( *pFormat ); + // aus Formatter/Scanner uebernommene Werte zuruecksetzen + if ( bSystem ) + eLnge = LANGUAGE_SYSTEM; + // pColor zeigt noch auf Tabelle in temporaerem Formatter/Scanner + for ( USHORT i = 0; i < 4; i++ ) + { + String aColorName = NumFor[i].GetColorName(); + Color* pColor = rScan.GetColor( aColorName ); + NumFor[i].SetColor( pColor, aColorName ); + } + } +} + + +// static +void SvNumberformat::LoadString( SvStream& rStream, String& rStr ) +{ + CharSet eStream = rStream.GetStreamCharSet(); + ByteString aStr; + rStream.ReadByteString( aStr ); + sal_Char cStream = NfCurrencyEntry::GetEuroSymbol( eStream ); + if ( aStr.Search( cStream ) == STRING_NOTFOUND ) + { // simple conversion to unicode + rStr = UniString( aStr, eStream ); + } + else + { + sal_Unicode cTarget = NfCurrencyEntry::GetEuroSymbol(); + register const sal_Char* p = aStr.GetBuffer(); + register const sal_Char* const pEnd = p + aStr.Len(); + register sal_Unicode* pUni = rStr.AllocBuffer( aStr.Len() ); + while ( p < pEnd ) + { + if ( *p == cStream ) + *pUni = cTarget; + else + *pUni = ByteString::ConvertToUnicode( *p, eStream ); + p++; + pUni++; + } + *pUni = 0; + } +} + + +void SvNumberformat::Save( SvStream& rStream, ImpSvNumMultipleWriteHeader& rHdr ) const +{ + String aFormatstring( sFormatstring ); + String aComment( sComment ); +#if NF_COMMENT_IN_FORMATSTRING + // der Kommentar im Formatstring wird nicht gespeichert, um in alten Versionen + // nicht ins schleudern zu kommen und spaeter getrennte Verarbeitung + // (z.B. im Dialog) zu ermoeglichen + SetComment( "", aFormatstring, aComment ); +#endif + + BOOL bNewCurrency = HasNewCurrency(); + if ( bNewCurrency ) + { // SV_NUMBERFORMATTER_VERSION_NEW_CURR im Kommentar speichern + aComment.Insert( cNewCurrencyMagic, 0 ); + aComment.Insert( cNewCurrencyMagic, 0 ); + aComment.Insert( aFormatstring, 1 ); + Build50Formatstring( aFormatstring ); // alten Formatstring generieren + } + + // old SO5 versions do behave strange (no output) if standard flag is set + // on formats not prepared for it (not having the following exact types) + BOOL bOldStandard = bStandard; + if ( bOldStandard ) + { + switch ( eType ) + { + case NUMBERFORMAT_NUMBER : + case NUMBERFORMAT_DATE : + case NUMBERFORMAT_TIME : + case NUMBERFORMAT_DATETIME : + case NUMBERFORMAT_PERCENT : + case NUMBERFORMAT_SCIENTIFIC : + // ok to save + break; + default: + bOldStandard = FALSE; + } + } + + rHdr.StartEntry(); + rStream.WriteByteString( aFormatstring, rStream.GetStreamCharSet() ); + rStream << eType << fLimit1 << fLimit2 << (USHORT) eOp1 << (USHORT) eOp2 + << bOldStandard << bIsUsed; + for (USHORT i = 0; i < 4; i++) + NumFor[i].Save(rStream); + // ab SV_NUMBERFORMATTER_VERSION_NEWSTANDARD + rStream.WriteByteString( aComment, rStream.GetStreamCharSet() ); + rStream << nNewStandardDefined; + // ab SV_NUMBERFORMATTER_VERSION_NEW_CURR + rStream << nNewCurrencyVersionId; + rStream << bNewCurrency; + if ( bNewCurrency ) + { + for ( USHORT j=0; j<4; j++ ) + { + NumFor[j].SaveNewCurrencyMap( rStream ); + } + } + + // the real standard flag to load with versions >638 if different + if ( bStandard != bOldStandard ) + { + rStream << nNewStandardFlagVersionId; + rStream << bStandard; + } + + rHdr.EndEntry(); +} + + +BOOL SvNumberformat::HasNewCurrency() const +{ + for ( USHORT j=0; j<4; j++ ) + { + if ( NumFor[j].HasNewCurrency() ) + return TRUE; + } + return FALSE; +} + + +BOOL SvNumberformat::GetNewCurrencySymbol( String& rSymbol, + String& rExtension ) const +{ + for ( USHORT j=0; j<4; j++ ) + { + if ( NumFor[j].GetNewCurrencySymbol( rSymbol, rExtension ) ) + return TRUE; + } + rSymbol.Erase(); + rExtension.Erase(); + return FALSE; +} + + +// static +String SvNumberformat::StripNewCurrencyDelimiters( const String& rStr, + BOOL bQuoteSymbol ) +{ + String aTmp; + xub_StrLen nStartPos, nPos, nLen; + nLen = rStr.Len(); + nStartPos = 0; + while ( (nPos = rStr.SearchAscii( "[$", nStartPos )) != STRING_NOTFOUND ) + { + xub_StrLen nEnd; + if ( (nEnd = GetQuoteEnd( rStr, nPos )) < nLen ) + { + aTmp += rStr.Copy( nStartPos, ++nEnd - nStartPos ); + nStartPos = nEnd; + } + else + { + aTmp += rStr.Copy( nStartPos, nPos - nStartPos ); + nStartPos = nPos + 2; + xub_StrLen nDash; + nEnd = nStartPos - 1; + do + { + nDash = rStr.Search( '-', ++nEnd ); + } while ( (nEnd = GetQuoteEnd( rStr, nDash )) < nLen ); + xub_StrLen nClose; + nEnd = nStartPos - 1; + do + { + nClose = rStr.Search( ']', ++nEnd ); + } while ( (nEnd = GetQuoteEnd( rStr, nClose )) < nLen ); + nPos = ( nDash < nClose ? nDash : nClose ); + if ( !bQuoteSymbol || rStr.GetChar( nStartPos ) == '"' ) + aTmp += rStr.Copy( nStartPos, nPos - nStartPos ); + else + { + aTmp += '"'; + aTmp += rStr.Copy( nStartPos, nPos - nStartPos ); + aTmp += '"'; + } + nStartPos = nClose + 1; + } + } + if ( nLen > nStartPos ) + aTmp += rStr.Copy( nStartPos, nLen - nStartPos ); + return aTmp; +} + + +void SvNumberformat::Build50Formatstring( String& rStr ) const +{ + rStr = StripNewCurrencyDelimiters( sFormatstring, TRUE ); +} + + +void SvNumberformat::ImpGetOutputStandard(double& fNumber, String& OutString) +{ + USHORT nStandardPrec = rScan.GetStandardPrec(); + if ( fabs(fNumber) > 1.0E15 ) // #58531# war E16 + OutString = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_E, nStandardPrec /*2*/, + GetFormatter().GetNumDecimalSep().GetChar(0)); + else + { +#if 0 +{ + // debugger test case for ANSI standard correctness + ::rtl::OUString aTest; + // expect 0.00123 OK + aTest = ::rtl::math::doubleToUString( 0.001234567, + rtl_math_StringFormat_G, 3, '.', sal_True ); + // expect 123 OK + aTest = ::rtl::math::doubleToUString( 123.4567, + rtl_math_StringFormat_G, 3, '.', sal_True ); + // expect 123.5 OK + aTest = ::rtl::math::doubleToUString( 123.4567, + rtl_math_StringFormat_G, 4, '.', sal_True ); + // expect 1e+03 (as 999.6 rounded to 3 significant digits results in + // 1000 with an exponent equal to significant digits) + // Currently (24-Jan-2003) we do fail in this case and output 1000 + // instead, negligible. + aTest = ::rtl::math::doubleToUString( 999.6, + rtl_math_StringFormat_G, 3, '.', sal_True ); + // expect what? result is 1.2e+004 + aTest = ::rtl::math::doubleToUString( 12345.6789, + rtl_math_StringFormat_G, -3, '.', sal_True ); +} +#endif + + OutString = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_F, nStandardPrec /*2*/, + GetFormatter().GetNumDecimalSep().GetChar(0), sal_True ); + if (OutString.GetChar(0) == '-' && + OutString.GetTokenCount('0') == OutString.Len()) + OutString.EraseLeadingChars('-'); // nicht -0 + } + ImpTransliterate( OutString, NumFor[0].GetNatNum() ); + return; +} + +void SvNumberformat::ImpGetOutputInputLine(double fNumber, String& OutString) +{ + BOOL bModified = FALSE; + if ( (eType & NUMBERFORMAT_PERCENT) && (fabs(fNumber) < _D_MAX_D_BY_100)) + { + if (fNumber == 0.0) + { + OutString.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "0%" ) ); + return; + } + fNumber *= 100; + bModified = TRUE; + } + + if (fNumber == 0.0) + { + OutString = '0'; + return; + } + + OutString = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, + GetFormatter().GetNumDecimalSep().GetChar(0), sal_True ); + + if ( eType & NUMBERFORMAT_PERCENT && bModified) + OutString += '%'; + return; +} + +short SvNumberformat::ImpCheckCondition(double& fNumber, + double& fLimit, + SvNumberformatLimitOps eOp) +{ + switch(eOp) + { + case NUMBERFORMAT_OP_NO: return -1; + case NUMBERFORMAT_OP_EQ: return (short) (fNumber == fLimit); + case NUMBERFORMAT_OP_NE: return (short) (fNumber != fLimit); + case NUMBERFORMAT_OP_LT: return (short) (fNumber < fLimit); + case NUMBERFORMAT_OP_LE: return (short) (fNumber <= fLimit); + case NUMBERFORMAT_OP_GT: return (short) (fNumber > fLimit); + case NUMBERFORMAT_OP_GE: return (short) (fNumber >= fLimit); + default: return -1; + } +} + +BOOL SvNumberformat::GetOutputString(String& sString, + String& OutString, + Color** ppColor) +{ + OutString.Erase(); + USHORT nIx; + if (eType & NUMBERFORMAT_TEXT) + nIx = 0; + else if (NumFor[3].GetnAnz() > 0) + nIx = 3; + else + { + *ppColor = NULL; // no change of color + return FALSE; + } + *ppColor = NumFor[nIx].GetColor(); + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + if (rInfo.eScannedType == NUMBERFORMAT_TEXT) + { + BOOL bRes = FALSE; + const USHORT nAnz = NumFor[nIx].GetnAnz(); + for (USHORT i = 0; i < nAnz; i++) + { + switch (rInfo.nTypeArray[i]) + { + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + OutString += (sal_Unicode) 0x1B; + OutString += rInfo.sStrArray[i].GetChar(1); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + InsertBlanks( OutString, OutString.Len(), + rInfo.sStrArray[i].GetChar(1) ); + break; + case NF_KEY_GENERAL : // #77026# "General" is the same as "@" + case NF_SYMBOLTYPE_DEL : + OutString += sString; + break; + default: + OutString += rInfo.sStrArray[i]; + } + } + return bRes; + } + return FALSE; +} +/* +void SvNumberformat::GetNextFareyNumber(ULONG nPrec, ULONG x0, ULONG x1, + ULONG y0, ULONG y1, + ULONG& x2,ULONG& y2) +{ + x2 = ((y0+nPrec)/y1)*x1 - x0; + y2 = ((y0+nPrec)/y1)*y1 - y0; +} +*/ +ULONG SvNumberformat::ImpGGT(ULONG x, ULONG y) +{ + if (y == 0) + return x; + else + { + ULONG z = x%y; + while (z) + { + x = y; + y = z; + z = x%y; + } + return y; + } +} + +ULONG SvNumberformat::ImpGGTRound(ULONG x, ULONG y) +{ + if (y == 0) + return x; + else + { + ULONG z = x%y; + while ((double)z/(double)y > D_EPS) + { + x = y; + y = z; + z = x%y; + } + return y; + } +} + +BOOL SvNumberformat::GetOutputString(double fNumber, + String& OutString, + Color** ppColor) +{ + BOOL bRes = FALSE; + OutString.Erase(); // alles loeschen + *ppColor = NULL; // keine Farbaenderung + if (eType & NUMBERFORMAT_LOGICAL) + { + if (fNumber) + OutString = rScan.GetTrueString(); + else + OutString = rScan.GetFalseString(); + return FALSE; + } + if (eType & NUMBERFORMAT_TEXT && bStandard) + { + ImpGetOutputStandard(fNumber, OutString); + return FALSE; + } + BOOL bHadStandard = FALSE; + if (bStandard) // einzelne Standardformate + { + if (rScan.GetStandardPrec() == 300) // alle Zahlformate InputLine + { + ImpGetOutputInputLine(fNumber, OutString); + return FALSE; + } + switch (eType) + { + case NUMBERFORMAT_NUMBER: // Standardzahlformat + ImpGetOutputStandard(fNumber, OutString); + bHadStandard = TRUE; + break; + case NUMBERFORMAT_DATE: + bRes |= ImpGetDateOutput(fNumber, 0, OutString); + bHadStandard = TRUE; + break; + case NUMBERFORMAT_TIME: + bRes |= ImpGetTimeOutput(fNumber, 0, OutString); + bHadStandard = TRUE; + break; + case NUMBERFORMAT_DATETIME: + bRes |= ImpGetDateTimeOutput(fNumber, 0, OutString); + bHadStandard = TRUE; + break; + } + } + if ( !bHadStandard ) + { + USHORT nIx; // Index des Teilformats + short nCheck = ImpCheckCondition(fNumber, fLimit1, eOp1); + if (nCheck == -1 || nCheck == 1) // nur 1 String oder True + nIx = 0; + else + { + nCheck = ImpCheckCondition(fNumber, fLimit2, eOp2); + if (nCheck == -1 || nCheck == 1) + nIx = 1; + else + nIx = 2; + } + if (nIx == 1 && fNumber < 0.0 && // negatives Format + IsNegativeRealNegative() ) // ohne Vorzeichen + fNumber = -fNumber; // Vorzeichen eliminieren + *ppColor = NumFor[nIx].GetColor(); + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + const USHORT nAnz = NumFor[nIx].GetnAnz(); + if (nAnz == 0 && rInfo.eScannedType == NUMBERFORMAT_UNDEFINED) + return FALSE; // leer => nichts + else if (nAnz == 0) // sonst Standard-Format + { + ImpGetOutputStandard(fNumber, OutString); + return FALSE; + } + switch (rInfo.eScannedType) + { + case NUMBERFORMAT_TEXT: + case NUMBERFORMAT_DEFINED: + { + for (USHORT i = 0; i < nAnz; i++) + { + switch (rInfo.nTypeArray[i]) + { + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + OutString += (sal_Unicode) 0x1B; + OutString += rInfo.sStrArray[i].GetChar(1); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + InsertBlanks( OutString, OutString.Len(), + rInfo.sStrArray[i].GetChar(1) ); + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + OutString += rInfo.sStrArray[i]; + break; + case NF_SYMBOLTYPE_THSEP: + if (rInfo.nThousand == 0) + OutString += rInfo.sStrArray[i]; + break; + default: + break; + } + } + } + break; + case NUMBERFORMAT_DATE: + bRes |= ImpGetDateOutput(fNumber, nIx, OutString); + break; + case NUMBERFORMAT_TIME: + bRes |= ImpGetTimeOutput(fNumber, nIx, OutString); + break; + case NUMBERFORMAT_DATETIME: + bRes |= ImpGetDateTimeOutput(fNumber, nIx, OutString); + break; + case NUMBERFORMAT_NUMBER: + case NUMBERFORMAT_PERCENT: + case NUMBERFORMAT_CURRENCY: + bRes |= ImpGetNumberOutput(fNumber, nIx, OutString); + break; + case NUMBERFORMAT_FRACTION: + { + String sStr, sFrac, sDiv; // Strings, Wert fuer + ULONG nFrac, nDiv; // Vorkommaanteil + // Zaehler und Nenner + BOOL bSign = FALSE; + if (fNumber < 0) + { + if (nIx == 0) // nicht in hinteren + bSign = TRUE; // Formaten + fNumber = -fNumber; + } + double fNum = floor(fNumber); // Vorkommateil + fNumber -= fNum; // Nachkommateil + if (fNum > _D_MAX_U_LONG_ || rInfo.nCntExp > 9) + // zu gross + { + OutString = rScan.GetErrorString(); + return FALSE; + } + if (rInfo.nCntExp == 0) + { + DBG_ERROR("SvNumberformat:: Bruch, nCntExp == 0"); + return FALSE; + } + ULONG nBasis = ((ULONG)floor( // 9, 99, 999 ,... + pow(10.0,rInfo.nCntExp))) - 1; + ULONG x0, y0, x1, y1; + + if (rInfo.nCntExp <= _MAX_FRACTION_PREC) + { + BOOL bUpperHalf; + if (fNumber > 0.5) + { + bUpperHalf = TRUE; + fNumber -= (fNumber - 0.5) * 2.0; + } + else + bUpperHalf = FALSE; + // Einstieg in Farey-Serie + // finden: + x0 = (ULONG) floor(fNumber*nBasis); // z.B. 2/9 <= x < 3/9 + if (x0 == 0) // => x0 = 2 + { + y0 = 1; + x1 = 1; + y1 = nBasis; + } + else if (x0 == (nBasis-1)/2) // (b-1)/2, 1/2 + { // geht (nBasis ungerade) + y0 = nBasis; + x1 = 1; + y1 = 2; + } + else if (x0 == 1) + { + y0 = nBasis; // 1/n; 1/(n-1) + x1 = 1; + y1 = nBasis - 1; + } + else + { + y0 = nBasis; // z.B. 2/9 2/8 + x1 = x0; + y1 = nBasis - 1; + double fUg = (double) x0 / (double) y0; + double fOg = (double) x1 / (double) y1; + ULONG nGgt = ImpGGT(y0, x0); // x0/y0 kuerzen + x0 /= nGgt; + y0 /= nGgt; // Einschachteln: + ULONG x2 = 0; + ULONG y2 = 0; + BOOL bStop = FALSE; + while (!bStop) + { +#ifdef GCC + // #i21648# GCC over-optimizes something resulting + // in wrong fTest values throughout the loops. + volatile +#endif + double fTest = (double)x1/(double)y1; + while (!bStop) + { + while (fTest > fOg) + { + x1--; + fTest = (double)x1/(double)y1; + } + while (fTest < fUg && y1 > 1) + { + y1--; + fTest = (double)x1/(double)y1; + } + if (fTest <= fOg) + { + fOg = fTest; + bStop = TRUE; + } + else if (y1 == 1) + bStop = TRUE; + } // of while + nGgt = ImpGGT(y1, x1); // x1/y1 kuerzen + x2 = x1 / nGgt; + y2 = y1 / nGgt; + if (x2*y0 - x0*y2 == 1 || y1 <= 1) // Test, ob x2/y2 + bStop = TRUE; // naechste Farey-Zahl + else + { + y1--; + bStop = FALSE; + } + } // of while + x1 = x2; + y1 = y2; + } // of else + double fup, flow; + flow = (double)x0/(double)y0; + fup = (double)x1/(double)y1; + while (fNumber > fup) + { + ULONG x2 = ((y0+nBasis)/y1)*x1 - x0; // naechste Farey-Zahl + ULONG y2 = ((y0+nBasis)/y1)*y1 - y0; +// GetNextFareyNumber(nBasis, x0, x1, y0, y1, x2, y2); + x0 = x1; + y0 = y1; + x1 = x2; + y1 = y2; + flow = fup; + fup = (double)x1/(double)y1; + } + if (fNumber - flow < fup - fNumber) + { + nFrac = x0; + nDiv = y0; + } + else + { + nFrac = x1; + nDiv = y1; + } + if (bUpperHalf) // Original restaur. + { + if (nFrac == 0 && nDiv == 1) // 1/1 + fNum += 1.0; + else + nFrac = nDiv - nFrac; + } + } + else // grosse Nenner + { // 0,1234->123/1000 + ULONG nGgt; +/* + nDiv = nBasis+1; + nFrac = ((ULONG)floor(0.5 + fNumber * + pow(10.0,rInfo.nCntExp))); +*/ + nDiv = 10000000; + nFrac = ((ULONG)floor(0.5 + fNumber * 10000000.0)); + nGgt = ImpGGT(nDiv, nFrac); + if (nGgt > 1) + { + nDiv /= nGgt; + nFrac /= nGgt; + } + if (nDiv > nBasis) + { + nGgt = ImpGGTRound(nDiv, nFrac); + if (nGgt > 1) + { + nDiv /= nGgt; + nFrac /= nGgt; + } + } + if (nDiv > nBasis) + { + nDiv = nBasis; + nFrac = ((ULONG)floor(0.5 + fNumber * + pow(10.0,rInfo.nCntExp))); + nGgt = ImpGGTRound(nDiv, nFrac); + if (nGgt > 1) + { + nDiv /= nGgt; + nFrac /= nGgt; + } + } + } + + if (rInfo.nCntPre == 0) // unechter Bruch + { + double fNum1 = fNum * (double)nDiv + (double)nFrac; + if (fNum1 > _D_MAX_U_LONG_) + { + OutString = rScan.GetErrorString(); + return FALSE; + } + nFrac = (ULONG) floor(fNum1); + sStr.Erase(); + } + else if (fNum == 0.0 && nFrac != 0) + sStr.Erase(); + else + { + char aBuf[100]; + sprintf( aBuf, "%.f", fNum ); // simple rounded integer (#100211# - checked) + sStr.AssignAscii( aBuf ); + ImpTransliterate( sStr, NumFor[nIx].GetNatNum() ); + } + if (rInfo.nCntPre > 0 && nFrac == 0) + { + sFrac.Erase(); + sDiv.Erase(); + } + else + { + sFrac = ImpIntToString( nIx, nFrac ); + sDiv = ImpIntToString( nIx, nDiv ); + } + + USHORT j = nAnz-1; // letztes Symbol->rueckw. + xub_StrLen k; // Nenner: + bRes |= ImpNumberFill(sDiv, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRAC); + BOOL bCont = TRUE; + if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRAC) + { + if (rInfo.nCntPre > 0 && nFrac == 0) + sDiv.Insert(' ',0); + else + sDiv.Insert( rInfo.sStrArray[j].GetChar(0), 0 ); + if ( j ) + j--; + else + bCont = FALSE; + } + // weiter Zaehler: + if ( !bCont ) + sFrac.Erase(); + else + { + bRes |= ImpNumberFill(sFrac, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRACBLANK); + if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRACBLANK) + { + sFrac.Insert(rInfo.sStrArray[j],0); + if ( j ) + j--; + else + bCont = FALSE; + } + } + // weiter Hauptzahl + if ( !bCont ) + sStr.Erase(); + else + { + k = sStr.Len(); // hinter letzter Ziffer + bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx, + rInfo.nCntPre); + } + if (bSign && !(nFrac == 0 && fNum == 0.0)) + OutString.Insert('-',0); // nicht -0 + OutString += sStr; + OutString += sFrac; + OutString += sDiv; + } + break; + case NUMBERFORMAT_SCIENTIFIC: + { + BOOL bSign = FALSE; + if (fNumber < 0) + { + if (nIx == 0) // nicht in hinteren + bSign = TRUE; // Formaten + fNumber = -fNumber; + } + String sStr( ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_E, + rInfo.nCntPre + rInfo.nCntPost - 1, '.' )); + + String ExpStr; + short nExpSign = 1; + xub_StrLen nExPos = sStr.Search('E'); + if ( nExPos != STRING_NOTFOUND ) + { + // split into mantisse and exponent and get rid of "E+" or "E-" + xub_StrLen nExpStart = nExPos + 1; + switch ( sStr.GetChar( nExpStart ) ) + { + case '-' : + nExpSign = -1; + // fallthru + case '+' : + ++nExpStart; + break; + } + ExpStr = sStr.Copy( nExpStart ); // part following the "E+" + sStr.Erase( nExPos ); + sStr.EraseAllChars('.'); // cut any decimal delimiter + if ( rInfo.nCntPre != 1 ) // rescale Exp + { + sal_Int32 nExp = ExpStr.ToInt32() * nExpSign; + nExp -= sal_Int32(rInfo.nCntPre)-1; + if ( nExp < 0 ) + { + nExpSign = -1; + nExp = -nExp; + } + else + nExpSign = 1; + ExpStr = String::CreateFromInt32( nExp ); + } + } + USHORT j = nAnz-1; // last symbol + xub_StrLen k; // position in ExpStr + bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP); + + xub_StrLen nZeros = 0; // erase leading zeros + while (nZeros < k && ExpStr.GetChar(nZeros) == '0') + ++nZeros; + if (nZeros) + ExpStr.Erase( 0, nZeros); + + BOOL bCont = TRUE; + if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP) + { + const String& rStr = rInfo.sStrArray[j]; + if (nExpSign == -1) + ExpStr.Insert('-',0); + else if (rStr.Len() > 1 && rStr.GetChar(1) == '+') + ExpStr.Insert('+',0); + ExpStr.Insert(rStr.GetChar(0),0); + if ( j ) + j--; + else + bCont = FALSE; + } + // weiter Hauptzahl: + if ( !bCont ) + sStr.Erase(); + else + { + k = sStr.Len(); // hinter letzter Ziffer + bRes |= ImpNumberFillWithThousands(sStr,fNumber, k,j,nIx, + rInfo.nCntPre + + rInfo.nCntPost); + } + if (bSign) + sStr.Insert('-',0); + OutString = sStr; + OutString += ExpStr; + } + break; + } + } + return bRes; +} + +BOOL SvNumberformat::ImpGetTimeOutput(double fNumber, + USHORT nIx, + String& OutString) +{ + using namespace ::com::sun::star::i18n; + BOOL bCalendarSet = FALSE; + double fNumberOrig = fNumber; + BOOL bRes = FALSE; + BOOL bSign = FALSE; + if (fNumber < 0.0) + { + fNumber = -fNumber; + if (nIx == 0) + bSign = TRUE; + } + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + if (rInfo.bThousand) // []-Format + { + if (fNumber > 1.0E10) // zu gross + { + OutString = rScan.GetErrorString(); + return FALSE; + } + } + else + fNumber -= floor(fNumber); // sonst Datum abtrennen + BOOL bInputLine; + xub_StrLen nCntPost; + if ( rScan.GetStandardPrec() == 300 && + 0 < rInfo.nCntPost && rInfo.nCntPost < 7 ) + { // round at 7 decimals (+5 of 86400 == 12 significant digits) + bInputLine = TRUE; + nCntPost = 7; + } + else + { + bInputLine = FALSE; + nCntPost = xub_StrLen(rInfo.nCntPost); + } + if (bSign && !rInfo.bThousand) // kein []-Format + fNumber = 1.0 - fNumber; // "Kehrwert" + double fTime = fNumber * 86400.0; + fTime = ::rtl::math::round( fTime, int(nCntPost) ); + if (bSign && fTime == 0.0) + bSign = FALSE; // nicht -00:00:00 + + if( floor( fTime ) > _D_MAX_U_LONG_ ) + { + OutString = rScan.GetErrorString(); + return FALSE; + } + ULONG nSeconds = (ULONG)floor( fTime ); + + String sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds, + rtl_math_StringFormat_F, int(nCntPost), '.')); + sSecStr.EraseLeadingChars('0'); + sSecStr.EraseLeadingChars('.'); + if ( bInputLine ) + { + sSecStr.EraseTrailingChars('0'); + if ( sSecStr.Len() < xub_StrLen(rInfo.nCntPost) ) + sSecStr.Expand( xub_StrLen(rInfo.nCntPost), '0' ); + ImpTransliterate( sSecStr, NumFor[nIx].GetNatNum() ); + nCntPost = sSecStr.Len(); + } + else + ImpTransliterate( sSecStr, NumFor[nIx].GetNatNum() ); + + xub_StrLen nSecPos = 0; // Zum Ziffernweisen + // abarbeiten + ULONG nHour, nMin, nSec; + if (!rInfo.bThousand) // kein [] Format + { + nHour = (nSeconds/3600) % 24; + nMin = (nSeconds%3600) / 60; + nSec = nSeconds%60; + } + else if (rInfo.nThousand == 3) // [ss] + { + nHour = 0; + nMin = 0; + nSec = nSeconds; + } + else if (rInfo.nThousand == 2) // [mm]:ss + { + nHour = 0; + nMin = nSeconds / 60; + nSec = nSeconds % 60; + } + else if (rInfo.nThousand == 1) // [hh]:mm:ss + { + nHour = nSeconds / 3600; + nMin = (nSeconds%3600) / 60; + nSec = nSeconds%60; + } + else { + // TODO What should these be set to? + nHour = 0; + nMin = 0; + nSec = 0; + } + + sal_Unicode cAmPm = ' '; // a oder p + if (rInfo.nCntExp) // AM/PM + { + if (nHour == 0) + { + nHour = 12; + cAmPm = 'a'; + } + else if (nHour < 12) + cAmPm = 'a'; + else + { + cAmPm = 'p'; + if (nHour > 12) + nHour -= 12; + } + } + const USHORT nAnz = NumFor[nIx].GetnAnz(); + for (USHORT i = 0; i < nAnz; i++) + { + switch (rInfo.nTypeArray[i]) + { + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + OutString += (sal_Unicode) 0x1B; + OutString += rInfo.sStrArray[i].GetChar(1); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + InsertBlanks( OutString, OutString.Len(), + rInfo.sStrArray[i].GetChar(1) ); + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + case NF_SYMBOLTYPE_DATESEP: + case NF_SYMBOLTYPE_TIMESEP: + case NF_SYMBOLTYPE_TIME100SECSEP: + OutString += rInfo.sStrArray[i]; + break; + case NF_SYMBOLTYPE_DIGIT: + { + xub_StrLen nLen = ( bInputLine && i > 0 && + (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING || + rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ? + nCntPost : rInfo.sStrArray[i].Len() ); + for (xub_StrLen j = 0; j < nLen && nSecPos < nCntPost; j++) + { + OutString += sSecStr.GetChar(nSecPos); + nSecPos++; + } + } + break; + case NF_KEY_AMPM: // AM/PM + { + if ( !bCalendarSet ) + { + double fDiff = DateTime(*(rScan.GetNullDate())) - GetCal().getEpochStart(); + fDiff += fNumberOrig; + GetCal().setLocalDateTime( fDiff ); + bCalendarSet = TRUE; + } + if (cAmPm == 'a') + OutString += GetCal().getDisplayName( + CalendarDisplayIndex::AM_PM, AmPmValue::AM, 0 ); + else + OutString += GetCal().getDisplayName( + CalendarDisplayIndex::AM_PM, AmPmValue::PM, 0 ); + } + break; + case NF_KEY_AP: // A/P + { + if (cAmPm == 'a') + OutString += 'a'; + else + OutString += 'p'; + } + break; + case NF_KEY_MI: // M + OutString += ImpIntToString( nIx, nMin ); + break; + case NF_KEY_MMI: // MM + OutString += ImpIntToString( nIx, nMin, 2 ); + break; + case NF_KEY_H: // H + OutString += ImpIntToString( nIx, nHour ); + break; + case NF_KEY_HH: // HH + OutString += ImpIntToString( nIx, nHour, 2 ); + break; + case NF_KEY_S: // S + OutString += ImpIntToString( nIx, nSec ); + break; + case NF_KEY_SS: // SS + OutString += ImpIntToString( nIx, nSec, 2 ); + break; + default: + break; + } + } + if (bSign && rInfo.bThousand) + OutString.Insert('-',0); + return bRes; +} + + +BOOL SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor& rNumFor ) const +{ + if ( GetCal().getUniqueID() != Gregorian::get() ) + return FALSE; + const ImpSvNumberformatInfo& rInfo = rNumFor.Info(); + const USHORT nAnz = rNumFor.GetnAnz(); + USHORT i; + for ( i = 0; i < nAnz; i++ ) + { + switch ( rInfo.nTypeArray[i] ) + { + case NF_SYMBOLTYPE_CALENDAR : + return FALSE; + case NF_KEY_EC : + case NF_KEY_EEC : + case NF_KEY_R : + case NF_KEY_RR : + case NF_KEY_AAA : + case NF_KEY_AAAA : + return TRUE; + } + } + return FALSE; +} + + +void SvNumberformat::SwitchToOtherCalendar( String& rOrgCalendar, + double& fOrgDateTime ) const +{ + CalendarWrapper& rCal = GetCal(); + const rtl::OUString &rGregorian = Gregorian::get(); + if ( rCal.getUniqueID() == rGregorian ) + { + using namespace ::com::sun::star::i18n; + ::com::sun::star::uno::Sequence< ::rtl::OUString > xCals + = rCal.getAllCalendars( rLoc().getLocale() ); + sal_Int32 nCnt = xCals.getLength(); + if ( nCnt > 1 ) + { + for ( sal_Int32 j=0; j < nCnt; j++ ) + { + if ( xCals[j] != rGregorian ) + { + if ( !rOrgCalendar.Len() ) + { + rOrgCalendar = rCal.getUniqueID(); + fOrgDateTime = rCal.getDateTime(); + } + rCal.loadCalendar( xCals[j], rLoc().getLocale() ); + rCal.setDateTime( fOrgDateTime ); + break; // for + } + } + } + } +} + + +void SvNumberformat::SwitchToGregorianCalendar( const String& rOrgCalendar, + double fOrgDateTime ) const +{ + CalendarWrapper& rCal = GetCal(); + const rtl::OUString &rGregorian = Gregorian::get(); + if ( rOrgCalendar.Len() && rCal.getUniqueID() != rGregorian ) + { + rCal.loadCalendar( rGregorian, rLoc().getLocale() ); + rCal.setDateTime( fOrgDateTime ); + } +} + + +BOOL SvNumberformat::ImpFallBackToGregorianCalendar( String& rOrgCalendar, double& fOrgDateTime ) +{ + using namespace ::com::sun::star::i18n; + CalendarWrapper& rCal = GetCal(); + const rtl::OUString &rGregorian = Gregorian::get(); + if ( rCal.getUniqueID() != rGregorian ) + { + sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA ); + if ( nVal == 0 && rCal.getLoadedCalendar().Eras[0].ID.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM( "Dummy" ) ) ) + { + if ( !rOrgCalendar.Len() ) + { + rOrgCalendar = rCal.getUniqueID(); + fOrgDateTime = rCal.getDateTime(); + } + else if ( rOrgCalendar == String(rGregorian) ) + rOrgCalendar.Erase(); + rCal.loadCalendar( rGregorian, rLoc().getLocale() ); + rCal.setDateTime( fOrgDateTime ); + return TRUE; + } + } + return FALSE; +} + + +BOOL SvNumberformat::ImpSwitchToSpecifiedCalendar( String& rOrgCalendar, + double& fOrgDateTime, const ImpSvNumFor& rNumFor ) const +{ + const ImpSvNumberformatInfo& rInfo = rNumFor.Info(); + const USHORT nAnz = rNumFor.GetnAnz(); + for ( USHORT i = 0; i < nAnz; i++ ) + { + if ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_CALENDAR ) + { + CalendarWrapper& rCal = GetCal(); + if ( !rOrgCalendar.Len() ) + { + rOrgCalendar = rCal.getUniqueID(); + fOrgDateTime = rCal.getDateTime(); + } + rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() ); + rCal.setDateTime( fOrgDateTime ); + return TRUE; + } + } + return FALSE; +} + + +// static +void SvNumberformat::ImpAppendEraG( String& OutString, + const CalendarWrapper& rCal, sal_Int16 nNatNum ) +{ + using namespace ::com::sun::star::i18n; + if ( rCal.getUniqueID().equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "gengou" ) ) ) + { + sal_Unicode cEra; + sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA ); + switch ( nVal ) + { + case 1 : cEra = 'M'; break; + case 2 : cEra = 'T'; break; + case 3 : cEra = 'S'; break; + case 4 : cEra = 'H'; break; + default: + cEra = '?'; + } + OutString += cEra; + } + else + OutString += rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ); +} + + +BOOL SvNumberformat::ImpGetDateOutput(double fNumber, + USHORT nIx, + String& OutString) +{ + using namespace ::com::sun::star::i18n; + BOOL bRes = FALSE; + CalendarWrapper& rCal = GetCal(); + double fDiff = DateTime(*(rScan.GetNullDate())) - rCal.getEpochStart(); + fNumber += fDiff; + rCal.setLocalDateTime( fNumber ); + String aOrgCalendar; // empty => not changed yet + double fOrgDateTime; + BOOL bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) ) + bOtherCalendar = FALSE; + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + const USHORT nAnz = NumFor[nIx].GetnAnz(); + sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum(); + for (USHORT i = 0; i < nAnz; i++) + { + switch (rInfo.nTypeArray[i]) + { + case NF_SYMBOLTYPE_CALENDAR : + if ( !aOrgCalendar.Len() ) + { + aOrgCalendar = rCal.getUniqueID(); + fOrgDateTime = rCal.getDateTime(); + } + rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() ); + rCal.setDateTime( fOrgDateTime ); + ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + break; + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + OutString += (sal_Unicode) 0x1B; + OutString += rInfo.sStrArray[i].GetChar(1); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + InsertBlanks( OutString, OutString.Len(), + rInfo.sStrArray[i].GetChar(1) ); + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + case NF_SYMBOLTYPE_DATESEP: + case NF_SYMBOLTYPE_TIMESEP: + case NF_SYMBOLTYPE_TIME100SECSEP: + OutString += rInfo.sStrArray[i]; + break; + case NF_KEY_M: // M + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_MONTH, nNatNum ); + break; + case NF_KEY_MM: // MM + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_MONTH, nNatNum ); + break; + case NF_KEY_MMM: // MMM + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_MONTH_NAME, nNatNum ); + break; + case NF_KEY_MMMM: // MMMM + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_MONTH_NAME, nNatNum ); + break; + case NF_KEY_MMMMM: // MMMMM + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_MONTH_NAME, nNatNum ).GetChar(0); + break; + case NF_KEY_Q: // Q + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_QUARTER, nNatNum ); + break; + case NF_KEY_QQ: // QQ + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_QUARTER, nNatNum ); + break; + case NF_KEY_D: // D + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_DAY, nNatNum ); + break; + case NF_KEY_DD: // DD + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY, nNatNum ); + break; + case NF_KEY_DDD: // DDD + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_DDDD: // DDDD + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY_NAME, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_YY: // YY + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_YEAR, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_YYYY: // YYYY + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_YEAR, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_EC: // E + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_YEAR, nNatNum ); + break; + case NF_KEY_EEC: // EE + case NF_KEY_R: // R + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_YEAR, nNatNum ); + break; + case NF_KEY_NN: // NN + case NF_KEY_AAA: // AAA + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ); + break; + case NF_KEY_NNN: // NNN + case NF_KEY_AAAA: // AAAA + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY_NAME, nNatNum ); + break; + case NF_KEY_NNNN: // NNNN + { + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY_NAME, nNatNum ); + OutString += rLoc().getLongDateDayOfWeekSep(); + } + break; + case NF_KEY_WW : // WW + { + sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR ); + OutString += ImpIntToString( nIx, nVal ); + } + break; + case NF_KEY_G: // G + ImpAppendEraG( OutString, rCal, nNatNum ); + break; + case NF_KEY_GG: // GG + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_ERA, nNatNum ); + break; + case NF_KEY_GGG: // GGG + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_ERA, nNatNum ); + break; + case NF_KEY_RR: // RR => GGGEE + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ); + break; + } + } + if ( aOrgCalendar.Len() ) + rCal.loadCalendar( aOrgCalendar, rLoc().getLocale() ); // restore calendar + return bRes; +} + +BOOL SvNumberformat::ImpGetDateTimeOutput(double fNumber, + USHORT nIx, + String& OutString) +{ + using namespace ::com::sun::star::i18n; + BOOL bRes = FALSE; + + CalendarWrapper& rCal = GetCal(); + double fDiff = DateTime(*(rScan.GetNullDate())) - rCal.getEpochStart(); + fNumber += fDiff; + + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + BOOL bInputLine; + xub_StrLen nCntPost; + if ( rScan.GetStandardPrec() == 300 && + 0 < rInfo.nCntPost && rInfo.nCntPost < 7 ) + { // round at 7 decimals (+5 of 86400 == 12 significant digits) + bInputLine = TRUE; + nCntPost = 7; + } + else + { + bInputLine = FALSE; + nCntPost = xub_StrLen(rInfo.nCntPost); + } + double fTime = (fNumber - floor( fNumber )) * 86400.0; + fTime = ::rtl::math::round( fTime, int(nCntPost) ); + if (fTime >= 86400.0) + { + // result of fNumber==x.999999999... rounded up, use correct date/time + fTime -= 86400.0; + fNumber = floor( fNumber + 0.5) + fTime; + } + rCal.setLocalDateTime( fNumber ); + + String aOrgCalendar; // empty => not changed yet + double fOrgDateTime; + BOOL bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) ) + bOtherCalendar = FALSE; + sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum(); + + ULONG nSeconds = (ULONG)floor( fTime ); + String sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds, + rtl_math_StringFormat_F, int(nCntPost), '.')); + sSecStr.EraseLeadingChars('0'); + sSecStr.EraseLeadingChars('.'); + if ( bInputLine ) + { + sSecStr.EraseTrailingChars('0'); + if ( sSecStr.Len() < xub_StrLen(rInfo.nCntPost) ) + sSecStr.Expand( xub_StrLen(rInfo.nCntPost), '0' ); + ImpTransliterate( sSecStr, NumFor[nIx].GetNatNum() ); + nCntPost = sSecStr.Len(); + } + else + ImpTransliterate( sSecStr, NumFor[nIx].GetNatNum() ); + + xub_StrLen nSecPos = 0; // Zum Ziffernweisen + // abarbeiten + ULONG nHour, nMin, nSec; + if (!rInfo.bThousand) // [] Format + { + nHour = (nSeconds/3600) % 24; + nMin = (nSeconds%3600) / 60; + nSec = nSeconds%60; + } + else if (rInfo.nThousand == 3) // [ss] + { + nHour = 0; + nMin = 0; + nSec = nSeconds; + } + else if (rInfo.nThousand == 2) // [mm]:ss + { + nHour = 0; + nMin = nSeconds / 60; + nSec = nSeconds % 60; + } + else if (rInfo.nThousand == 1) // [hh]:mm:ss + { + nHour = nSeconds / 3600; + nMin = (nSeconds%3600) / 60; + nSec = nSeconds%60; + } + else { + nHour = 0; // TODO What should these values be? + nMin = 0; + nSec = 0; + } + sal_Unicode cAmPm = ' '; // a oder p + if (rInfo.nCntExp) // AM/PM + { + if (nHour == 0) + { + nHour = 12; + cAmPm = 'a'; + } + else if (nHour < 12) + cAmPm = 'a'; + else + { + cAmPm = 'p'; + if (nHour > 12) + nHour -= 12; + } + } + const USHORT nAnz = NumFor[nIx].GetnAnz(); + for (USHORT i = 0; i < nAnz; i++) + { + switch (rInfo.nTypeArray[i]) + { + case NF_SYMBOLTYPE_CALENDAR : + if ( !aOrgCalendar.Len() ) + { + aOrgCalendar = rCal.getUniqueID(); + fOrgDateTime = rCal.getDateTime(); + } + rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() ); + rCal.setDateTime( fOrgDateTime ); + ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + break; + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + OutString += (sal_Unicode) 0x1B; + OutString += rInfo.sStrArray[i].GetChar(1); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + InsertBlanks( OutString, OutString.Len(), + rInfo.sStrArray[i].GetChar(1) ); + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + case NF_SYMBOLTYPE_DATESEP: + case NF_SYMBOLTYPE_TIMESEP: + case NF_SYMBOLTYPE_TIME100SECSEP: + OutString += rInfo.sStrArray[i]; + break; + case NF_SYMBOLTYPE_DIGIT: + { + xub_StrLen nLen = ( bInputLine && i > 0 && + (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING || + rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ? + nCntPost : rInfo.sStrArray[i].Len() ); + for (xub_StrLen j = 0; j < nLen && nSecPos < nCntPost; j++) + { + OutString += sSecStr.GetChar(nSecPos); + nSecPos++; + } + } + break; + case NF_KEY_AMPM: // AM/PM + { + if (cAmPm == 'a') + OutString += rCal.getDisplayName( CalendarDisplayIndex::AM_PM, + AmPmValue::AM, 0 ); + else + OutString += rCal.getDisplayName( CalendarDisplayIndex::AM_PM, + AmPmValue::PM, 0 ); + } + break; + case NF_KEY_AP: // A/P + { + if (cAmPm == 'a') + OutString += 'a'; + else + OutString += 'p'; + } + break; + case NF_KEY_MI: // M + OutString += ImpIntToString( nIx, nMin ); + break; + case NF_KEY_MMI: // MM + OutString += ImpIntToString( nIx, nMin, 2 ); + break; + case NF_KEY_H: // H + OutString += ImpIntToString( nIx, nHour ); + break; + case NF_KEY_HH: // HH + OutString += ImpIntToString( nIx, nHour, 2 ); + break; + case NF_KEY_S: // S + OutString += ImpIntToString( nIx, nSec ); + break; + case NF_KEY_SS: // SS + OutString += ImpIntToString( nIx, nSec, 2 ); + break; + case NF_KEY_M: // M + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_MONTH, nNatNum ); + break; + case NF_KEY_MM: // MM + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_MONTH, nNatNum ); + break; + case NF_KEY_MMM: // MMM + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_MONTH_NAME, nNatNum ); + break; + case NF_KEY_MMMM: // MMMM + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_MONTH_NAME, nNatNum ); + break; + case NF_KEY_MMMMM: // MMMMM + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_MONTH_NAME, nNatNum ).GetChar(0); + break; + case NF_KEY_Q: // Q + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_QUARTER, nNatNum ); + break; + case NF_KEY_QQ: // QQ + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_QUARTER, nNatNum ); + break; + case NF_KEY_D: // D + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_DAY, nNatNum ); + break; + case NF_KEY_DD: // DD + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY, nNatNum ); + break; + case NF_KEY_DDD: // DDD + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_DDDD: // DDDD + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY_NAME, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_YY: // YY + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_YEAR, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_YYYY: // YYYY + { + if ( bOtherCalendar ) + SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime ); + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_YEAR, nNatNum ); + if ( bOtherCalendar ) + SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); + } + break; + case NF_KEY_EC: // E + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_YEAR, nNatNum ); + break; + case NF_KEY_EEC: // EE + case NF_KEY_R: // R + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_YEAR, nNatNum ); + break; + case NF_KEY_NN: // NN + case NF_KEY_AAA: // AAA + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ); + break; + case NF_KEY_NNN: // NNN + case NF_KEY_AAAA: // AAAA + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY_NAME, nNatNum ); + break; + case NF_KEY_NNNN: // NNNN + { + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_DAY_NAME, nNatNum ); + OutString += rLoc().getLongDateDayOfWeekSep(); + } + break; + case NF_KEY_WW : // WW + { + sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR ); + OutString += ImpIntToString( nIx, nVal ); + } + break; + case NF_KEY_G: // G + ImpAppendEraG( OutString, rCal, nNatNum ); + break; + case NF_KEY_GG: // GG + OutString += rCal.getDisplayString( + CalendarDisplayCode::SHORT_ERA, nNatNum ); + break; + case NF_KEY_GGG: // GGG + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_ERA, nNatNum ); + break; + case NF_KEY_RR: // RR => GGGEE + OutString += rCal.getDisplayString( + CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ); + break; + } + } + if ( aOrgCalendar.Len() ) + rCal.loadCalendar( aOrgCalendar, rLoc().getLocale() ); // restore calendar + return bRes; +} + +BOOL SvNumberformat::ImpGetNumberOutput(double fNumber, + USHORT nIx, + String& OutString) +{ + BOOL bRes = FALSE; + BOOL bSign; + if (fNumber < 0.0) + { + if (nIx == 0) // nicht in hinteren + bSign = TRUE; // Formaten + else + bSign = FALSE; + fNumber = -fNumber; + } + else + { + bSign = FALSE; + if ( ::rtl::math::isSignBitSet( fNumber ) ) + fNumber = -fNumber; // yes, -0.0 is possible, eliminate '-' + } + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + if (rInfo.eScannedType == NUMBERFORMAT_PERCENT) + { + if (fNumber < _D_MAX_D_BY_100) + fNumber *= 100.0; + else + { + OutString = rScan.GetErrorString(); + return FALSE; + } + } + USHORT i, j; + xub_StrLen k; + String sStr; + long nPrecExp; + BOOL bInteger = FALSE; + if ( rInfo.nThousand != FLAG_STANDARD_IN_FORMAT ) + { // special formatting only if no GENERAL keyword in format code + const USHORT nThousand = rInfo.nThousand; + for (i = 0; i < nThousand; i++) + { + if (fNumber > _D_MIN_M_BY_1000) + fNumber /= 1000.0; + else + fNumber = 0.0; + } + if (fNumber > 0.0) + nPrecExp = GetPrecExp( fNumber ); + else + nPrecExp = 0; + if (rInfo.nCntPost) // NachkommaStellen + { + if (rInfo.nCntPost + nPrecExp > 15 && nPrecExp < 15) + { + sStr = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_F, 15-nPrecExp, '.'); + for (long l = 15-nPrecExp; l < (long) rInfo.nCntPost; l++) + sStr += '0'; + } + else + sStr = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_F, rInfo.nCntPost, '.' ); + sStr.EraseLeadingChars('0'); // fuehrende Nullen weg + } + else if (fNumber == 0.0) // Null + { + // nothing to be done here, keep empty string sStr, + // ImpNumberFillWithThousands does the rest + } + else // Integer + { + sStr = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_F, 0, '.'); + sStr.EraseLeadingChars('0'); // fuehrende Nullen weg + } + xub_StrLen nPoint = sStr.Search( '.' ); + if ( nPoint != STRING_NOTFOUND ) + { + register const sal_Unicode* p = sStr.GetBuffer() + nPoint; + while ( *++p == '0' ) + ; + if ( !*p ) + bInteger = TRUE; + sStr.Erase( nPoint, 1 ); // . herausnehmen + } + if (bSign && + (sStr.Len() == 0 || sStr.GetTokenCount('0') == sStr.Len()+1)) // nur 00000 + bSign = FALSE; // nicht -0.00 + } // End of != FLAG_STANDARD_IN_FORMAT + + // von hinten nach vorn + // editieren: + k = sStr.Len(); // hinter letzter Ziffer + j = NumFor[nIx].GetnAnz()-1; // letztes Symbol + // Nachkommastellen: + if (rInfo.nCntPost > 0) + { + BOOL bTrailing = TRUE; // ob Endnullen? + BOOL bFilled = FALSE; // ob aufgefuellt wurde ? + short nType; + while (j > 0 && // rueckwaerts + (nType = rInfo.nTypeArray[j]) != NF_SYMBOLTYPE_DECSEP) + { + switch ( nType ) + { + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + sStr.Insert( (sal_Unicode) 0x1B, k /*++*/ ); + sStr.Insert(rInfo.sStrArray[j].GetChar(1),k); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + /*k = */ InsertBlanks( sStr,k,rInfo.sStrArray[j].GetChar(1) ); + break; + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + case NF_SYMBOLTYPE_PERCENT: + sStr.Insert(rInfo.sStrArray[j],k); + break; + case NF_SYMBOLTYPE_THSEP: + if (rInfo.nThousand == 0) + sStr.Insert(rInfo.sStrArray[j],k); + break; + case NF_SYMBOLTYPE_DIGIT: + { + const String& rStr = rInfo.sStrArray[j]; + const sal_Unicode* p1 = rStr.GetBuffer(); + register const sal_Unicode* p = p1 + rStr.Len(); + while ( p1 < p-- ) + { + const sal_Unicode c = *p; + k--; + if ( sStr.GetChar(k) != '0' ) + bTrailing = FALSE; + if (bTrailing) + { + if ( c == '0' ) + bFilled = TRUE; + else if ( c == '-' ) + { + if ( bInteger ) + sStr.SetChar( k, '-' ); + bFilled = TRUE; + } + else if ( c == '?' ) + { + sStr.SetChar( k, ' ' ); + bFilled = TRUE; + } + else if ( !bFilled ) // # + sStr.Erase(k,1); + } + } // of for + } // of case digi + break; + case NF_KEY_CCC: // CCC-Waehrung + sStr.Insert(rScan.GetCurAbbrev(), k); + break; + case NF_KEY_GENERAL: // Standard im String + { + String sNum; + ImpGetOutputStandard(fNumber, sNum); + sNum.EraseLeadingChars('-'); + sStr.Insert(sNum, k); + } + break; + default: + break; + } // of switch + j--; + } // of while + } // of Nachkomma + + bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx, // ggfs Auffuellen mit . + rInfo.nCntPre); + if ( rInfo.nCntPost > 0 ) + { + const String& rDecSep = GetFormatter().GetNumDecimalSep(); + xub_StrLen nLen = rDecSep.Len(); + if ( sStr.Len() > nLen && sStr.Equals( rDecSep, sStr.Len() - nLen, nLen ) ) + sStr.Erase( sStr.Len() - nLen ); // no decimals => strip DecSep + } + if (bSign) + sStr.Insert('-',0); + ImpTransliterate( sStr, NumFor[nIx].GetNatNum() ); + OutString = sStr; + return bRes; +} + +BOOL SvNumberformat::ImpNumberFillWithThousands( + String& sStr, // number string + double& rNumber, // number + xub_StrLen k, // position within string + USHORT j, // symbol index within format code + USHORT nIx, // subformat index + USHORT nDigCnt) // count of integer digits in format +{ + BOOL bRes = FALSE; + xub_StrLen nLeadingStringChars = 0; // inserted StringChars before number + xub_StrLen nDigitCount = 0; // count of integer digits from the right + BOOL bStop = FALSE; + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + // no normal thousands separators if number divided by thousands + BOOL bDoThousands = (rInfo.nThousand == 0); + utl::DigitGroupingIterator aGrouping( + GetFormatter().GetLocaleData()->getDigitGrouping()); + while (!bStop) // backwards + { + if (j == 0) + bStop = TRUE; + switch (rInfo.nTypeArray[j]) + { + case NF_SYMBOLTYPE_DECSEP: + aGrouping.reset(); + // fall thru + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + case NF_SYMBOLTYPE_PERCENT: + sStr.Insert(rInfo.sStrArray[j],k); + if ( k == 0 ) + nLeadingStringChars = + nLeadingStringChars + rInfo.sStrArray[j].Len(); + break; + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + sStr.Insert( (sal_Unicode) 0x1B, k/*++*/ ); + sStr.Insert(rInfo.sStrArray[j].GetChar(1),k); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + /*k = */ InsertBlanks( sStr,k,rInfo.sStrArray[j].GetChar(1) ); + break; + case NF_SYMBOLTYPE_THSEP: + { + // #i7284# #102685# Insert separator also if number is divided + // by thousands and the separator is specified somewhere in + // between and not only at the end. + // #i12596# But do not insert if it's a parenthesized negative + // format like (#,) + // In fact, do not insert if divided and regex [0#,],[^0#] and + // no other digit symbol follows (which was already detected + // during scan of format code, otherwise there would be no + // division), else do insert. Same in ImpNumberFill() below. + if ( !bDoThousands && j < NumFor[nIx].GetnAnz()-1 ) + bDoThousands = ((j == 0) || + (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT && + rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) || + (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT)); + if ( bDoThousands ) + { + if (k > 0) + sStr.Insert(rInfo.sStrArray[j],k); + else if (nDigitCount < nDigCnt) + { + // Leading '#' displays nothing (e.g. no leading + // separator for numbers <1000 with #,##0 format). + // Leading '?' displays blank. + // Everything else, including nothing, displays the + // separator. + sal_Unicode cLeader = 0; + if (j > 0 && rInfo.nTypeArray[j-1] == NF_SYMBOLTYPE_DIGIT) + { + const String& rStr = rInfo.sStrArray[j-1]; + xub_StrLen nLen = rStr.Len(); + if (nLen) + cLeader = rStr.GetChar(nLen-1); + } + switch (cLeader) + { + case '#': + ; // nothing + break; + case '?': + // erAck: 2008-04-03T16:24+0200 + // Actually this currently isn't executed + // because the format scanner in the context of + // "?," doesn't generate a group separator but + // a literal ',' character instead that is + // inserted unconditionally. Should be changed + // on some occasion. + sStr.Insert(' ',k); + break; + default: + sStr.Insert(rInfo.sStrArray[j],k); + } + } + aGrouping.advance(); + } + } + break; + case NF_SYMBOLTYPE_DIGIT: + { + const String& rStr = rInfo.sStrArray[j]; + const sal_Unicode* p1 = rStr.GetBuffer(); + register const sal_Unicode* p = p1 + rStr.Len(); + while ( p1 < p-- ) + { + nDigitCount++; + if (k > 0) + k--; + else + { + switch (*p) + { + case '0': + sStr.Insert('0',0); + break; + case '?': + sStr.Insert(' ',0); + break; + } + } + if (nDigitCount == nDigCnt && k > 0) + { // more digits than specified + ImpDigitFill(sStr, 0, k, nIx, nDigitCount, aGrouping); + } + } + } + break; + case NF_KEY_CCC: // CCC currency + sStr.Insert(rScan.GetCurAbbrev(), k); + break; + case NF_KEY_GENERAL: // "General" in string + { + String sNum; + ImpGetOutputStandard(rNumber, sNum); + sNum.EraseLeadingChars('-'); + sStr.Insert(sNum, k); + } + break; + + default: + break; + } // switch + j--; // next format code string + } // while + k = k + nLeadingStringChars; // MSC converts += to int and then warns, so ... + if (k > nLeadingStringChars) + ImpDigitFill(sStr, nLeadingStringChars, k, nIx, nDigitCount, aGrouping); + return bRes; +} + +void SvNumberformat::ImpDigitFill( + String& sStr, // number string + xub_StrLen nStart, // start of digits + xub_StrLen& k, // position within string + USHORT nIx, // subformat index + xub_StrLen & nDigitCount, // count of integer digits from the right so far + utl::DigitGroupingIterator & rGrouping ) // current grouping +{ + if (NumFor[nIx].Info().bThousand) // only if grouping + { // fill in separators + const String& rThousandSep = GetFormatter().GetNumThousandSep(); + while (k > nStart) + { + if (nDigitCount == rGrouping.getPos()) + { + sStr.Insert( rThousandSep, k ); + rGrouping.advance(); + } + nDigitCount++; + k--; + } + } + else // simply skip + k = nStart; +} + +BOOL SvNumberformat::ImpNumberFill( String& sStr, // number string + double& rNumber, // number for "General" format + xub_StrLen& k, // position within string + USHORT& j, // symbol index within format code + USHORT nIx, // subformat index + short eSymbolType ) // type of stop condition +{ + BOOL bRes = FALSE; + k = sStr.Len(); // behind last digit + const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); + // no normal thousands separators if number divided by thousands + BOOL bDoThousands = (rInfo.nThousand == 0); + short nType; + while (j > 0 && (nType = rInfo.nTypeArray[j]) != eSymbolType ) + { // rueckwaerts: + switch ( nType ) + { + case NF_SYMBOLTYPE_STAR: + if( bStarFlag ) + { + sStr.Insert( sal_Unicode(0x1B), k++ ); + sStr.Insert(rInfo.sStrArray[j].GetChar(1),k); + bRes = TRUE; + } + break; + case NF_SYMBOLTYPE_BLANK: + k = InsertBlanks( sStr,k,rInfo.sStrArray[j].GetChar(1) ); + break; + case NF_SYMBOLTYPE_THSEP: + { + // Same as in ImpNumberFillWithThousands() above, do not insert + // if divided and regex [0#,],[^0#] and no other digit symbol + // follows (which was already detected during scan of format + // code, otherwise there would be no division), else do insert. + if ( !bDoThousands && j < NumFor[nIx].GetnAnz()-1 ) + bDoThousands = ((j == 0) || + (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT && + rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) || + (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT)); + if ( bDoThousands && k > 0 ) + { + sStr.Insert(rInfo.sStrArray[j],k); + } + } + break; + case NF_SYMBOLTYPE_DIGIT: + { + const String& rStr = rInfo.sStrArray[j]; + const sal_Unicode* p1 = rStr.GetBuffer(); + register const sal_Unicode* p = p1 + rStr.Len(); + while ( p1 < p-- ) + { + if (k > 0) + k--; + else + { + switch (*p) + { + case '0': + sStr.Insert('0',0); + break; + case '?': + sStr.Insert(' ',0); + break; + } + } + } + } + break; + case NF_KEY_CCC: // CCC-Waehrung + sStr.Insert(rScan.GetCurAbbrev(), k); + break; + case NF_KEY_GENERAL: // Standard im String + { + String sNum; + ImpGetOutputStandard(rNumber, sNum); + sNum.EraseLeadingChars('-'); // Vorzeichen weg!! + sStr.Insert(sNum, k); + } + break; + + default: + sStr.Insert(rInfo.sStrArray[j],k); + break; + } // of switch + j--; // naechster String + } // of while + return bRes; +} + +void SvNumberformat::GetFormatSpecialInfo(BOOL& bThousand, + BOOL& IsRed, + USHORT& nPrecision, + USHORT& nAnzLeading) const +{ + // as before: take info from nNumFor=0 for whole format (for dialog etc.) + + short nDummyType; + GetNumForInfo( 0, nDummyType, bThousand, nPrecision, nAnzLeading ); + + // "negative in red" is only useful for the whole format + + const Color* pColor = NumFor[1].GetColor(); + if (fLimit1 == 0.0 && fLimit2 == 0.0 && pColor + && (*pColor == rScan.GetRedColor())) + IsRed = TRUE; + else + IsRed = FALSE; +} + +void SvNumberformat::GetNumForInfo( USHORT nNumFor, short& rScannedType, + BOOL& bThousand, USHORT& nPrecision, USHORT& nAnzLeading ) const +{ + // take info from a specified sub-format (for XML export) + + if ( nNumFor > 3 ) + return; // invalid + + const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info(); + rScannedType = rInfo.eScannedType; + bThousand = rInfo.bThousand; + nPrecision = rInfo.nCntPost; + if (bStandard && rInfo.eScannedType == NUMBERFORMAT_NUMBER) + // StandardFormat + nAnzLeading = 1; + else + { + nAnzLeading = 0; + BOOL bStop = FALSE; + USHORT i = 0; + const USHORT nAnz = NumFor[nNumFor].GetnAnz(); + while (!bStop && i < nAnz) + { + short nType = rInfo.nTypeArray[i]; + if ( nType == NF_SYMBOLTYPE_DIGIT) + { + register const sal_Unicode* p = rInfo.sStrArray[i].GetBuffer(); + while ( *p == '#' ) + p++; + while ( *p++ == '0' ) + nAnzLeading++; + } + else if (nType == NF_SYMBOLTYPE_DECSEP || nType == NF_SYMBOLTYPE_EXP) + bStop = TRUE; + i++; + } + } +} + +const String* SvNumberformat::GetNumForString( USHORT nNumFor, USHORT nPos, + BOOL bString /* = FALSE */ ) const +{ + if ( nNumFor > 3 ) + return NULL; + USHORT nAnz = NumFor[nNumFor].GetnAnz(); + if ( !nAnz ) + return NULL; + if ( nPos == 0xFFFF ) + { + nPos = nAnz - 1; + if ( bString ) + { // rueckwaerts + short* pType = NumFor[nNumFor].Info().nTypeArray + nPos; + while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) && + (*pType != NF_SYMBOLTYPE_CURRENCY) ) + { + pType--; + nPos--; + } + if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) ) + return NULL; + } + } + else if ( nPos > nAnz - 1 ) + return NULL; + else if ( bString ) + { // vorwaerts + short* pType = NumFor[nNumFor].Info().nTypeArray + nPos; + while ( nPos < nAnz && (*pType != NF_SYMBOLTYPE_STRING) && + (*pType != NF_SYMBOLTYPE_CURRENCY) ) + { + pType++; + nPos++; + } + if ( nPos >= nAnz || ((*pType != NF_SYMBOLTYPE_STRING) && + (*pType != NF_SYMBOLTYPE_CURRENCY)) ) + return NULL; + } + return &NumFor[nNumFor].Info().sStrArray[nPos]; +} + + +short SvNumberformat::GetNumForType( USHORT nNumFor, USHORT nPos, + BOOL bString /* = FALSE */ ) const +{ + if ( nNumFor > 3 ) + return 0; + USHORT nAnz = NumFor[nNumFor].GetnAnz(); + if ( !nAnz ) + return 0; + if ( nPos == 0xFFFF ) + { + nPos = nAnz - 1; + if ( bString ) + { // rueckwaerts + short* pType = NumFor[nNumFor].Info().nTypeArray + nPos; + while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) && + (*pType != NF_SYMBOLTYPE_CURRENCY) ) + { + pType--; + nPos--; + } + if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) ) + return 0; + } + } + else if ( nPos > nAnz - 1 ) + return 0; + else if ( bString ) + { // vorwaerts + short* pType = NumFor[nNumFor].Info().nTypeArray + nPos; + while ( nPos < nAnz && (*pType != NF_SYMBOLTYPE_STRING) && + (*pType != NF_SYMBOLTYPE_CURRENCY) ) + { + pType++; + nPos++; + } + if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) ) + return 0; + } + return NumFor[nNumFor].Info().nTypeArray[nPos]; +} + + +BOOL SvNumberformat::IsNegativeWithoutSign() const +{ + if ( IsNegativeRealNegative() ) + { + const String* pStr = GetNumForString( 1, 0, TRUE ); + if ( pStr ) + return !HasStringNegativeSign( *pStr ); + } + return FALSE; +} + + +DateFormat SvNumberformat::GetDateOrder() const +{ + if ( (eType & NUMBERFORMAT_DATE) == NUMBERFORMAT_DATE ) + { + short const * const pType = NumFor[0].Info().nTypeArray; + USHORT nAnz = NumFor[0].GetnAnz(); + for ( USHORT j=0; j<nAnz; j++ ) + { + switch ( pType[j] ) + { + case NF_KEY_D : + case NF_KEY_DD : + return DMY; + case NF_KEY_M : + case NF_KEY_MM : + case NF_KEY_MMM : + case NF_KEY_MMMM : + case NF_KEY_MMMMM : + return MDY; + case NF_KEY_YY : + case NF_KEY_YYYY : + case NF_KEY_EC : + case NF_KEY_EEC : + case NF_KEY_R : + case NF_KEY_RR : + return YMD; + } + } + } + else + { + DBG_ERROR( "SvNumberformat::GetDateOrder: no date" ); + } + return rLoc().getDateFormat(); +} + + +sal_uInt32 SvNumberformat::GetExactDateOrder() const +{ + sal_uInt32 nRet = 0; + if ( (eType & NUMBERFORMAT_DATE) != NUMBERFORMAT_DATE ) + { + DBG_ERROR( "SvNumberformat::GetExactDateOrder: no date" ); + return nRet; + } + short const * const pType = NumFor[0].Info().nTypeArray; + USHORT nAnz = NumFor[0].GetnAnz(); + int nShift = 0; + for ( USHORT j=0; j<nAnz && nShift < 3; j++ ) + { + switch ( pType[j] ) + { + case NF_KEY_D : + case NF_KEY_DD : + nRet = (nRet << 8) | 'D'; + ++nShift; + break; + case NF_KEY_M : + case NF_KEY_MM : + case NF_KEY_MMM : + case NF_KEY_MMMM : + case NF_KEY_MMMMM : + nRet = (nRet << 8) | 'M'; + ++nShift; + break; + case NF_KEY_YY : + case NF_KEY_YYYY : + case NF_KEY_EC : + case NF_KEY_EEC : + case NF_KEY_R : + case NF_KEY_RR : + nRet = (nRet << 8) | 'Y'; + ++nShift; + break; + } + } + return nRet; +} + + +void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1, + SvNumberformatLimitOps& rOper2, double& rVal2 ) const +{ + rOper1 = eOp1; + rOper2 = eOp2; + rVal1 = fLimit1; + rVal2 = fLimit2; +} + + +Color* SvNumberformat::GetColor( USHORT nNumFor ) const +{ + if ( nNumFor > 3 ) + return NULL; + + return NumFor[nNumFor].GetColor(); +} + + +void lcl_SvNumberformat_AddLimitStringImpl( String& rStr, + SvNumberformatLimitOps eOp, double fLimit, const String& rDecSep ) +{ + if ( eOp != NUMBERFORMAT_OP_NO ) + { + switch ( eOp ) + { + case NUMBERFORMAT_OP_EQ : + rStr.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "[=" ) ); + break; + case NUMBERFORMAT_OP_NE : + rStr.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "[<>" ) ); + break; + case NUMBERFORMAT_OP_LT : + rStr.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "[<" ) ); + break; + case NUMBERFORMAT_OP_LE : + rStr.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "[<=" ) ); + break; + case NUMBERFORMAT_OP_GT : + rStr.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "[>" ) ); + break; + case NUMBERFORMAT_OP_GE : + rStr.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "[>=" ) ); + break; + default: + OSL_ASSERT( "unsupported number format" ); + break; + } + rStr += String( ::rtl::math::doubleToUString( fLimit, + rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, + rDecSep.GetChar(0), sal_True)); + rStr += ']'; + } +} + + +String SvNumberformat::GetMappedFormatstring( + const NfKeywordTable& rKeywords, const LocaleDataWrapper& rLocWrp, + BOOL bDontQuote ) const +{ + String aStr; + BOOL bDefault[4]; + // 1 subformat matches all if no condition specified, + bDefault[0] = ( NumFor[1].GetnAnz() == 0 && eOp1 == NUMBERFORMAT_OP_NO ); + // with 2 subformats [>=0];[<0] is implied if no condition specified + bDefault[1] = ( !bDefault[0] && NumFor[2].GetnAnz() == 0 && + eOp1 == NUMBERFORMAT_OP_GE && fLimit1 == 0.0 && + eOp2 == NUMBERFORMAT_OP_NO && fLimit2 == 0.0 ); + // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified, + // note that subformats may be empty (;;;) and NumFor[2].GetnAnz()>0 is not checked. + bDefault[2] = ( !bDefault[0] && !bDefault[1] && + eOp1 == NUMBERFORMAT_OP_GT && fLimit1 == 0.0 && + eOp2 == NUMBERFORMAT_OP_LT && fLimit2 == 0.0 ); + BOOL bDefaults = bDefault[0] || bDefault[1] || bDefault[2]; + // from now on bDefault[] values are used to append empty subformats at the end + bDefault[3] = FALSE; + if ( !bDefaults ) + { // conditions specified + if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO ) + bDefault[0] = bDefault[1] = TRUE; // [];x + else if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 != NUMBERFORMAT_OP_NO && + NumFor[2].GetnAnz() == 0 ) + bDefault[0] = bDefault[1] = bDefault[2] = bDefault[3] = TRUE; // [];[];; + // nothing to do if conditions specified for every subformat + } + else if ( bDefault[0] ) + bDefault[0] = FALSE; // a single unconditional subformat is never delimited + else + { + if ( bDefault[2] && NumFor[2].GetnAnz() == 0 && NumFor[1].GetnAnz() > 0 ) + bDefault[3] = TRUE; // special cases x;x;; and ;x;; + for ( int i=0; i<3 && !bDefault[i]; ++i ) + bDefault[i] = TRUE; + } + int nSem = 0; // needed ';' delimiters + int nSub = 0; // subformats delimited so far + for ( int n=0; n<4; n++ ) + { + if ( n > 0 ) + nSem++; + + String aPrefix; + + if ( !bDefaults ) + { + switch ( n ) + { + case 0 : + lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1, + fLimit1, rLocWrp.getNumDecimalSep() ); + break; + case 1 : + lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2, + fLimit2, rLocWrp.getNumDecimalSep() ); + break; + } + } + + const String& rColorName = NumFor[n].GetColorName(); + if ( rColorName.Len() ) + { + const String* pKey = rScan.GetKeywords() + NF_KEY_FIRSTCOLOR; + for ( int j=NF_KEY_FIRSTCOLOR; j<=NF_KEY_LASTCOLOR; j++, pKey++ ) + { + if ( *pKey == rColorName ) + { + aPrefix += '['; + aPrefix += rKeywords[j]; + aPrefix += ']'; + break; // for + } + } + } + + const SvNumberNatNum& rNum = NumFor[n].GetNatNum(); + // The Thai T NatNum modifier during Xcl export. + if (rNum.IsSet() && rNum.GetNatNum() == 1 && + rKeywords[NF_KEY_THAI_T].EqualsAscii( "T") && + MsLangId::getRealLanguage( rNum.GetLang()) == + LANGUAGE_THAI) + { + aPrefix += 't'; // must be lowercase, otherwise taken as literal + } + + USHORT nAnz = NumFor[n].GetnAnz(); + if ( nSem && (nAnz || aPrefix.Len()) ) + { + for ( ; nSem; --nSem ) + aStr += ';'; + for ( ; nSub <= n; ++nSub ) + bDefault[nSub] = FALSE; + } + + if ( aPrefix.Len() ) + aStr += aPrefix; + + if ( nAnz ) + { + const short* pType = NumFor[n].Info().nTypeArray; + const String* pStr = NumFor[n].Info().sStrArray; + for ( USHORT j=0; j<nAnz; j++ ) + { + if ( 0 <= pType[j] && pType[j] < NF_KEYWORD_ENTRIES_COUNT ) + { + aStr += rKeywords[pType[j]]; + if( NF_KEY_NNNN == pType[j] ) + aStr += rLocWrp.getLongDateDayOfWeekSep(); + } + else + { + switch ( pType[j] ) + { + case NF_SYMBOLTYPE_DECSEP : + aStr += rLocWrp.getNumDecimalSep(); + break; + case NF_SYMBOLTYPE_THSEP : + aStr += rLocWrp.getNumThousandSep(); + break; + case NF_SYMBOLTYPE_DATESEP : + aStr += rLocWrp.getDateSep(); + break; + case NF_SYMBOLTYPE_TIMESEP : + aStr += rLocWrp.getTimeSep(); + break; + case NF_SYMBOLTYPE_TIME100SECSEP : + aStr += rLocWrp.getTime100SecSep(); + break; + case NF_SYMBOLTYPE_STRING : + if( bDontQuote ) + aStr += pStr[j]; + else if ( pStr[j].Len() == 1 ) + { + aStr += '\\'; + aStr += pStr[j]; + } + else + { + aStr += '"'; + aStr += pStr[j]; + aStr += '"'; + } + break; + default: + aStr += pStr[j]; + } + + } + } + } + } + for ( ; nSub<4 && bDefault[nSub]; ++nSub ) + { // append empty subformats + aStr += ';'; + } + return aStr; +} + + +String SvNumberformat::ImpGetNatNumString( const SvNumberNatNum& rNum, + sal_Int32 nVal, USHORT nMinDigits ) const +{ + String aStr; + if ( nMinDigits ) + { + if ( nMinDigits == 2 ) + { // speed up the most common case + if ( 0 <= nVal && nVal < 10 ) + { + sal_Unicode* p = aStr.AllocBuffer( 2 ); + *p++ = '0'; + *p = sal_Unicode( '0' + nVal ); + } + else + aStr = String::CreateFromInt32( nVal ); + } + else + { + String aValStr( String::CreateFromInt32( nVal ) ); + if ( aValStr.Len() >= nMinDigits ) + aStr = aValStr; + else + { + aStr.Fill( nMinDigits - aValStr.Len(), '0' ); + aStr += aValStr; + } + } + } + else + aStr = String::CreateFromInt32( nVal ); + ImpTransliterate( aStr, rNum ); + return aStr; +} + + +void SvNumberformat::ImpTransliterateImpl( String& rStr, + const SvNumberNatNum& rNum ) const +{ + com::sun::star::lang::Locale aLocale( + MsLangId::convertLanguageToLocale( rNum.GetLang() ) ); + rStr = GetFormatter().GetNatNum()->getNativeNumberString( rStr, + aLocale, rNum.GetNatNum() ); +} + + +void SvNumberformat::GetNatNumXml( + com::sun::star::i18n::NativeNumberXmlAttributes& rAttr, + USHORT nNumFor ) const +{ + if ( nNumFor <= 3 ) + { + const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum(); + if ( rNum.IsSet() ) + { + com::sun::star::lang::Locale aLocale( + MsLangId::convertLanguageToLocale( rNum.GetLang() ) ); + rAttr = GetFormatter().GetNatNum()->convertToXmlAttributes( + aLocale, rNum.GetNatNum() ); + } + else + rAttr = com::sun::star::i18n::NativeNumberXmlAttributes(); + } + else + rAttr = com::sun::star::i18n::NativeNumberXmlAttributes(); +} + +// static +BOOL SvNumberformat::HasStringNegativeSign( const String& rStr ) +{ + // fuer Sign muss '-' am Anfang oder am Ende des TeilStrings sein (Blanks ignored) + xub_StrLen nLen = rStr.Len(); + if ( !nLen ) + return FALSE; + const sal_Unicode* const pBeg = rStr.GetBuffer(); + const sal_Unicode* const pEnd = pBeg + nLen; + register const sal_Unicode* p = pBeg; + do + { // Anfang + if ( *p == '-' ) + return TRUE; + } while ( *p == ' ' && ++p < pEnd ); + p = pEnd - 1; + do + { // Ende + if ( *p == '-' ) + return TRUE; + } while ( *p == ' ' && pBeg < --p ); + return FALSE; +} + + +// static +void SvNumberformat::SetComment( const String& rStr, String& rFormat, + String& rComment ) +{ + if ( rComment.Len() ) + { // alten Kommentar aus Formatstring loeschen + //! nicht per EraseComment, der Kommentar muss matchen + String aTmp( '{' ); + aTmp += ' '; + aTmp += rComment; + aTmp += ' '; + aTmp += '}'; + xub_StrLen nCom = 0; + do + { + nCom = rFormat.Search( aTmp, nCom ); + } while ( (nCom != STRING_NOTFOUND) && (nCom + aTmp.Len() != rFormat.Len()) ); + if ( nCom != STRING_NOTFOUND ) + rFormat.Erase( nCom ); + } + if ( rStr.Len() ) + { // neuen Kommentar setzen + rFormat += '{'; + rFormat += ' '; + rFormat += rStr; + rFormat += ' '; + rFormat += '}'; + rComment = rStr; + } +} + + +// static +void SvNumberformat::EraseCommentBraces( String& rStr ) +{ + xub_StrLen nLen = rStr.Len(); + if ( nLen && rStr.GetChar(0) == '{' ) + { + rStr.Erase( 0, 1 ); + --nLen; + } + if ( nLen && rStr.GetChar(0) == ' ' ) + { + rStr.Erase( 0, 1 ); + --nLen; + } + if ( nLen && rStr.GetChar( nLen-1 ) == '}' ) + rStr.Erase( --nLen, 1 ); + if ( nLen && rStr.GetChar( nLen-1 ) == ' ' ) + rStr.Erase( --nLen, 1 ); +} + + +// static +void SvNumberformat::EraseComment( String& rStr ) +{ + register const sal_Unicode* p = rStr.GetBuffer(); + BOOL bInString = FALSE; + BOOL bEscaped = FALSE; + BOOL bFound = FALSE; + xub_StrLen nPos = 0; + while ( !bFound && *p ) + { + switch ( *p ) + { + case '\\' : + bEscaped = !bEscaped; + break; + case '\"' : + if ( !bEscaped ) + bInString = !bInString; + break; + case '{' : + if ( !bEscaped && !bInString ) + { + bFound = TRUE; + nPos = sal::static_int_cast< xub_StrLen >( + p - rStr.GetBuffer()); + } + break; + } + if ( bEscaped && *p != '\\' ) + bEscaped = FALSE; + ++p; + } + if ( bFound ) + rStr.Erase( nPos ); +} + + +// static +BOOL SvNumberformat::IsInQuote( const String& rStr, xub_StrLen nPos, + sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut ) +{ + xub_StrLen nLen = rStr.Len(); + if ( nPos >= nLen ) + return FALSE; + register const sal_Unicode* p0 = rStr.GetBuffer(); + register const sal_Unicode* p = p0; + register const sal_Unicode* p1 = p0 + nPos; + BOOL bQuoted = FALSE; + while ( p <= p1 ) + { + if ( *p == cQuote ) + { + if ( p == p0 ) + bQuoted = TRUE; + else if ( bQuoted ) + { + if ( *(p-1) != cEscIn ) + bQuoted = FALSE; + } + else + { + if ( *(p-1) != cEscOut ) + bQuoted = TRUE; + } + } + p++; + } + return bQuoted; +} + + +// static +xub_StrLen SvNumberformat::GetQuoteEnd( const String& rStr, xub_StrLen nPos, + sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut ) +{ + xub_StrLen nLen = rStr.Len(); + if ( nPos >= nLen ) + return STRING_NOTFOUND; + if ( !IsInQuote( rStr, nPos, cQuote, cEscIn, cEscOut ) ) + { + if ( rStr.GetChar( nPos ) == cQuote ) + return nPos; // schliessendes cQuote + return STRING_NOTFOUND; + } + register const sal_Unicode* p0 = rStr.GetBuffer(); + register const sal_Unicode* p = p0 + nPos; + register const sal_Unicode* p1 = p0 + nLen; + while ( p < p1 ) + { + if ( *p == cQuote && p > p0 && *(p-1) != cEscIn ) + return sal::static_int_cast< xub_StrLen >(p - p0); + p++; + } + return nLen; // String Ende +} + + +USHORT SvNumberformat::ImpGetNumForStringElementCount( USHORT nNumFor ) const +{ + USHORT nCnt = 0; + USHORT nAnz = NumFor[nNumFor].GetnAnz(); + short const * const pType = NumFor[nNumFor].Info().nTypeArray; + for ( USHORT j=0; j<nAnz; ++j ) + { + switch ( pType[j] ) + { + case NF_SYMBOLTYPE_STRING: + case NF_SYMBOLTYPE_CURRENCY: + case NF_SYMBOLTYPE_DATESEP: + case NF_SYMBOLTYPE_TIMESEP: + case NF_SYMBOLTYPE_TIME100SECSEP: + case NF_SYMBOLTYPE_PERCENT: + ++nCnt; + break; + } + } + return nCnt; +} + diff --git a/svl/source/numbers/zforscan.cxx b/svl/source/numbers/zforscan.cxx new file mode 100644 index 000000000000..5c0d45a53ed2 --- /dev/null +++ b/svl/source/numbers/zforscan.cxx @@ -0,0 +1,2812 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zforscan.cxx,v $ + * $Revision: 1.49.140.2 $ + * + * 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_svl.hxx" +#ifndef GCC +#endif + +#include <stdlib.h> +#include <tools/debug.hxx> +#include <i18npool/mslangid.hxx> +#include <unotools/charclass.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/numberformatcodewrapper.hxx> +#include <rtl/instance.hxx> + +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <unotools/digitgroupingiterator.hxx> + +#define _ZFORSCAN_CXX +#include "zforscan.hxx" +#undef _ZFORSCAN_CXX +#include "nfsymbol.hxx" +using namespace svt; + +const sal_Unicode cNonBreakingSpace = 0xA0; + +namespace +{ + struct ImplEnglishColors + { + const String* operator()() + { + static const String aEnglishColors[NF_MAX_DEFAULT_COLORS] = + { + String( RTL_CONSTASCII_USTRINGPARAM( "BLACK" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "BLUE" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "GREEN" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "CYAN" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "RED" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "MAGENTA" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "BROWN" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "GREY" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "YELLOW" ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "WHITE" ) ) + }; + return &aEnglishColors[0]; + } + }; + + struct theEnglishColors + : public rtl::StaticAggregate< const String, ImplEnglishColors> {}; + +} + +ImpSvNumberformatScan::ImpSvNumberformatScan( SvNumberFormatter* pFormatterP ) +{ + pFormatter = pFormatterP; + bConvertMode = FALSE; + //! All keywords MUST be UPPERCASE! + sKeyword[NF_KEY_E].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "E" ) ); // Exponent + sKeyword[NF_KEY_AMPM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "AM/PM" ) ); // AM/PM + sKeyword[NF_KEY_AP].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "A/P" ) ); // AM/PM short + sKeyword[NF_KEY_MI].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "M" ) ); // Minute + sKeyword[NF_KEY_MMI].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MM" ) ); // Minute 02 + sKeyword[NF_KEY_S].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "S" ) ); // Second + sKeyword[NF_KEY_SS].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "SS" ) ); // Second 02 + sKeyword[NF_KEY_Q].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "Q" ) ); // Quarter short 'Q' + sKeyword[NF_KEY_QQ].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "QQ" ) ); // Quarter long + sKeyword[NF_KEY_NN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "NN" ) ); // Day of week short + sKeyword[NF_KEY_NNN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "NNN" ) ); // Day of week long + sKeyword[NF_KEY_NNNN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "NNNN" ) ); // Day of week long incl. separator + sKeyword[NF_KEY_WW].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "WW" ) ); // Week of year + sKeyword[NF_KEY_CCC].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "CCC" ) ); // Currency abbreviation + bKeywordsNeedInit = TRUE; // locale dependent keywords + bCompatCurNeedInit = TRUE; // locale dependent compatibility currency strings + + StandardColor[0] = Color(COL_BLACK); + StandardColor[1] = Color(COL_LIGHTBLUE); + StandardColor[2] = Color(COL_LIGHTGREEN); + StandardColor[3] = Color(COL_LIGHTCYAN); + StandardColor[4] = Color(COL_LIGHTRED); + StandardColor[5] = Color(COL_LIGHTMAGENTA); + StandardColor[6] = Color(COL_BROWN); + StandardColor[7] = Color(COL_GRAY); + StandardColor[8] = Color(COL_YELLOW); + StandardColor[9] = Color(COL_WHITE); + + pNullDate = new Date(30,12,1899); + nStandardPrec = 2; + + sErrStr.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "###" ) ); + Reset(); +} + +ImpSvNumberformatScan::~ImpSvNumberformatScan() +{ + delete pNullDate; + Reset(); +} + + +void ImpSvNumberformatScan::ChangeIntl() +{ + bKeywordsNeedInit = TRUE; + bCompatCurNeedInit = TRUE; + // may be initialized by InitSpecialKeyword() + sKeyword[NF_KEY_TRUE].Erase(); + sKeyword[NF_KEY_FALSE].Erase(); +} + + +void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx ) const +{ + switch ( eIdx ) + { + case NF_KEY_TRUE : + ((ImpSvNumberformatScan*)this)->sKeyword[NF_KEY_TRUE] = + pFormatter->GetCharClass()->upper( + pFormatter->GetLocaleData()->getTrueWord() ); + if ( !sKeyword[NF_KEY_TRUE].Len() ) + { + DBG_ERRORFILE( "InitSpecialKeyword: TRUE_WORD?" ); + ((ImpSvNumberformatScan*)this)->sKeyword[NF_KEY_TRUE].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "TRUE" ) ); + } + break; + case NF_KEY_FALSE : + ((ImpSvNumberformatScan*)this)->sKeyword[NF_KEY_FALSE] = + pFormatter->GetCharClass()->upper( + pFormatter->GetLocaleData()->getFalseWord() ); + if ( !sKeyword[NF_KEY_FALSE].Len() ) + { + DBG_ERRORFILE( "InitSpecialKeyword: FALSE_WORD?" ); + ((ImpSvNumberformatScan*)this)->sKeyword[NF_KEY_FALSE].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "FALSE" ) ); + } + break; + default: + DBG_ERRORFILE( "InitSpecialKeyword: unknown request" ); + } +} + + +void ImpSvNumberformatScan::InitCompatCur() const +{ + ImpSvNumberformatScan* pThis = (ImpSvNumberformatScan*)this; + // currency symbol for old style ("automatic") compatibility format codes + pFormatter->GetCompatibilityCurrency( pThis->sCurSymbol, pThis->sCurAbbrev ); + // currency symbol upper case + pThis->sCurString = pFormatter->GetCharClass()->upper( sCurSymbol ); + bCompatCurNeedInit = FALSE; +} + + +void ImpSvNumberformatScan::InitKeywords() const +{ + if ( !bKeywordsNeedInit ) + return ; + ((ImpSvNumberformatScan*)this)->SetDependentKeywords(); + bKeywordsNeedInit = FALSE; +} + + +/** Extract the name of General, Standard, Whatever, ignoring leading modifiers + such as [NatNum1]. */ +static String lcl_extractStandardGeneralName( const ::rtl::OUString & rCode ) +{ + String aStr; + const sal_Unicode* p = rCode.getStr(); + const sal_Unicode* const pStop = p + rCode.getLength(); + const sal_Unicode* pBeg = p; // name begins here + bool bMod = false; + bool bDone = false; + while (p < pStop && !bDone) + { + switch (*p) + { + case '[': + bMod = true; + break; + case ']': + if (bMod) + { + bMod = false; + pBeg = p+1; + } + // else: would be a locale data error, easily to be spotted in + // UI dialog + break; + case ';': + if (!bMod) + { + bDone = true; + --p; // put back, increment by one follows + } + break; + } + ++p; + if (bMod) + pBeg = p; + } + if (pBeg < p) + aStr = rCode.copy( pBeg - rCode.getStr(), p - pBeg); + return aStr; +} + + +void ImpSvNumberformatScan::SetDependentKeywords() +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + + const CharClass* pCharClass = pFormatter->GetCharClass(); + const LocaleDataWrapper* pLocaleData = pFormatter->GetLocaleData(); + // #80023# be sure to generate keywords for the loaded Locale, not for the + // requested Locale, otherwise number format codes might not match + lang::Locale aLoadedLocale = pLocaleData->getLoadedLocale(); + LanguageType eLang = MsLangId::convertLocaleToLanguage( aLoadedLocale ); + NumberFormatCodeWrapper aNumberFormatCode( pFormatter->GetServiceManager(), aLoadedLocale ); + + i18n::NumberFormatCode aFormat = aNumberFormatCode.getFormatCode( NF_NUMBER_STANDARD ); + sNameStandardFormat = lcl_extractStandardGeneralName( aFormat.Code); + sKeyword[NF_KEY_GENERAL] = pCharClass->upper( sNameStandardFormat ); + + // preset new calendar keywords + sKeyword[NF_KEY_AAA].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "AAA" ) ); + sKeyword[NF_KEY_AAAA].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "AAAA" ) ); + sKeyword[NF_KEY_EC].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "E" ) ); + sKeyword[NF_KEY_EEC].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "EE" ) ); + sKeyword[NF_KEY_G].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "G" ) ); + sKeyword[NF_KEY_GG].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GG" ) ); + sKeyword[NF_KEY_GGG].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GGG" ) ); + sKeyword[NF_KEY_R].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "R" ) ); + sKeyword[NF_KEY_RR].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "RR" ) ); + + // 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].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "T")); + else + sKeyword[NF_KEY_THAI_T].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "t")); + + switch ( eLang ) + { + case LANGUAGE_GERMAN: + case LANGUAGE_GERMAN_SWISS: + case LANGUAGE_GERMAN_AUSTRIAN: + case LANGUAGE_GERMAN_LUXEMBOURG: + case LANGUAGE_GERMAN_LIECHTENSTEIN: + { + //! all capital letters + sKeyword[NF_KEY_M].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "M" ) ); // month 1 + sKeyword[NF_KEY_MM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MM" ) ); // month 01 + sKeyword[NF_KEY_MMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MMM" ) ); // month Jan + sKeyword[NF_KEY_MMMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MMMM" ) ); // month Januar + sKeyword[NF_KEY_MMMMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MMMMM" ) );// month J + sKeyword[NF_KEY_H].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "H" ) ); // hour 2 + sKeyword[NF_KEY_HH].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "HH" ) ); // hour 02 + sKeyword[NF_KEY_D].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "T" ) ); + sKeyword[NF_KEY_DD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "TT" ) ); + sKeyword[NF_KEY_DDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "TTT" ) ); + sKeyword[NF_KEY_DDDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "TTTT" ) ); + sKeyword[NF_KEY_YY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJ" ) ); + sKeyword[NF_KEY_YYYY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJJJ" ) ); + sKeyword[NF_KEY_BOOLEAN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "LOGISCH" ) ); + sKeyword[NF_KEY_COLOR].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "FARBE" ) ); + sKeyword[NF_KEY_BLACK].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "SCHWARZ" ) ); + sKeyword[NF_KEY_BLUE].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "BLAU" ) ); + sKeyword[NF_KEY_GREEN] = UniString( "GR" "\xDC" "N", RTL_TEXTENCODING_ISO_8859_1 ); + sKeyword[NF_KEY_CYAN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "CYAN" ) ); + sKeyword[NF_KEY_RED].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "ROT" ) ); + sKeyword[NF_KEY_MAGENTA].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MAGENTA" ) ); + sKeyword[NF_KEY_BROWN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "BRAUN" ) ); + sKeyword[NF_KEY_GREY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GRAU" ) ); + sKeyword[NF_KEY_YELLOW].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GELB" ) ); + sKeyword[NF_KEY_WHITE].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "WEISS" ) ); + } + break; + default: + { + // day + switch ( eLang ) + { + case LANGUAGE_ITALIAN : + case LANGUAGE_ITALIAN_SWISS : + sKeyword[NF_KEY_D].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "G" ) ); + sKeyword[NF_KEY_DD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GG" ) ); + sKeyword[NF_KEY_DDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GGG" ) ); + sKeyword[NF_KEY_DDDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GGGG" ) ); + // must exchange the era code, same as Xcl + sKeyword[NF_KEY_G].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "X" ) ); + sKeyword[NF_KEY_GG].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "XX" ) ); + sKeyword[NF_KEY_GGG].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "XXX" ) ); + break; + case LANGUAGE_FRENCH : + case LANGUAGE_FRENCH_BELGIAN : + case LANGUAGE_FRENCH_CANADIAN : + case LANGUAGE_FRENCH_SWISS : + case LANGUAGE_FRENCH_LUXEMBOURG : + case LANGUAGE_FRENCH_MONACO : + sKeyword[NF_KEY_D].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "J" ) ); + sKeyword[NF_KEY_DD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJ" ) ); + sKeyword[NF_KEY_DDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJJ" ) ); + sKeyword[NF_KEY_DDDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJJJ" ) ); + break; + case LANGUAGE_FINNISH : + sKeyword[NF_KEY_D].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "P" ) ); + sKeyword[NF_KEY_DD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "PP" ) ); + sKeyword[NF_KEY_DDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "PPP" ) ); + sKeyword[NF_KEY_DDDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "PPPP" ) ); + break; + default: + sKeyword[NF_KEY_D].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D" ) ); + sKeyword[NF_KEY_DD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "DD" ) ); + sKeyword[NF_KEY_DDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "DDD" ) ); + sKeyword[NF_KEY_DDDD].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "DDDD" ) ); + } + // month + switch ( eLang ) + { + case LANGUAGE_FINNISH : + sKeyword[NF_KEY_M].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "K" ) ); + sKeyword[NF_KEY_MM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "KK" ) ); + sKeyword[NF_KEY_MMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "KKK" ) ); + sKeyword[NF_KEY_MMMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "KKKK" ) ); + sKeyword[NF_KEY_MMMMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "KKKKK" ) ); + break; + default: + sKeyword[NF_KEY_M].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "M" ) ); + sKeyword[NF_KEY_MM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MM" ) ); + sKeyword[NF_KEY_MMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MMM" ) ); + sKeyword[NF_KEY_MMMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MMMM" ) ); + sKeyword[NF_KEY_MMMMM].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MMMMM" ) ); + } + // year + switch ( eLang ) + { + case LANGUAGE_ITALIAN : + case LANGUAGE_ITALIAN_SWISS : + case LANGUAGE_FRENCH : + case LANGUAGE_FRENCH_BELGIAN : + case LANGUAGE_FRENCH_CANADIAN : + case LANGUAGE_FRENCH_SWISS : + case LANGUAGE_FRENCH_LUXEMBOURG : + case LANGUAGE_FRENCH_MONACO : + case LANGUAGE_PORTUGUESE : + case LANGUAGE_PORTUGUESE_BRAZILIAN : + case LANGUAGE_SPANISH_MODERN : + case LANGUAGE_SPANISH_DATED : + case LANGUAGE_SPANISH_MEXICAN : + case LANGUAGE_SPANISH_GUATEMALA : + case LANGUAGE_SPANISH_COSTARICA : + case LANGUAGE_SPANISH_PANAMA : + case LANGUAGE_SPANISH_DOMINICAN_REPUBLIC : + case LANGUAGE_SPANISH_VENEZUELA : + case LANGUAGE_SPANISH_COLOMBIA : + case LANGUAGE_SPANISH_PERU : + case LANGUAGE_SPANISH_ARGENTINA : + case LANGUAGE_SPANISH_ECUADOR : + case LANGUAGE_SPANISH_CHILE : + case LANGUAGE_SPANISH_URUGUAY : + case LANGUAGE_SPANISH_PARAGUAY : + case LANGUAGE_SPANISH_BOLIVIA : + case LANGUAGE_SPANISH_EL_SALVADOR : + case LANGUAGE_SPANISH_HONDURAS : + case LANGUAGE_SPANISH_NICARAGUA : + case LANGUAGE_SPANISH_PUERTO_RICO : + sKeyword[NF_KEY_YY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "AA" ) ); + sKeyword[NF_KEY_YYYY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "AAAA" ) ); + // must exchange the day of week name code, same as Xcl + sKeyword[NF_KEY_AAA].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "OOO" ) ); + sKeyword[NF_KEY_AAAA].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "OOOO" ) ); + break; + case LANGUAGE_DUTCH : + case LANGUAGE_DUTCH_BELGIAN : + sKeyword[NF_KEY_YY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJ" ) ); + sKeyword[NF_KEY_YYYY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "JJJJ" ) ); + break; + case LANGUAGE_FINNISH : + sKeyword[NF_KEY_YY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "VV" ) ); + sKeyword[NF_KEY_YYYY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "VVVV" ) ); + break; + default: + sKeyword[NF_KEY_YY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "YY" ) ); + sKeyword[NF_KEY_YYYY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "YYYY" ) ); + } + // hour + switch ( eLang ) + { + case LANGUAGE_DUTCH : + case LANGUAGE_DUTCH_BELGIAN : + sKeyword[NF_KEY_H].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "U" ) ); + sKeyword[NF_KEY_HH].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "UU" ) ); + break; + case LANGUAGE_FINNISH : + case LANGUAGE_SWEDISH : + case LANGUAGE_SWEDISH_FINLAND : + case LANGUAGE_DANISH : + case LANGUAGE_NORWEGIAN : + case LANGUAGE_NORWEGIAN_BOKMAL : + case LANGUAGE_NORWEGIAN_NYNORSK : + sKeyword[NF_KEY_H].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "T" ) ); + sKeyword[NF_KEY_HH].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "TT" ) ); + break; + default: + sKeyword[NF_KEY_H].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "H" ) ); + sKeyword[NF_KEY_HH].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "HH" ) ); + } + // boolean + sKeyword[NF_KEY_BOOLEAN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "BOOLEAN" ) ); + // colours + sKeyword[NF_KEY_COLOR].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "COLOR" ) ); + sKeyword[NF_KEY_BLACK].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "BLACK" ) ); + sKeyword[NF_KEY_BLUE].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "BLUE" ) ); + sKeyword[NF_KEY_GREEN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GREEN" ) ); + sKeyword[NF_KEY_CYAN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "CYAN" ) ); + sKeyword[NF_KEY_RED].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "RED" ) ); + sKeyword[NF_KEY_MAGENTA].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "MAGENTA" ) ); + sKeyword[NF_KEY_BROWN].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "BROWN" ) ); + sKeyword[NF_KEY_GREY].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "GREY" ) ); + sKeyword[NF_KEY_YELLOW].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "YELLOW" ) ); + sKeyword[NF_KEY_WHITE].AssignAscii( RTL_CONSTASCII_STRINGPARAM( "WHITE" ) ); + } + break; + } + + // boolean keyords + InitSpecialKeyword( NF_KEY_TRUE ); + InitSpecialKeyword( NF_KEY_FALSE ); + + // compatibility currency strings + InitCompatCur(); +} + + +void ImpSvNumberformatScan::ChangeNullDate(USHORT nDay, USHORT nMonth, USHORT nYear) +{ + if ( pNullDate ) + *pNullDate = Date(nDay, nMonth, nYear); + else + pNullDate = new Date(nDay, nMonth, nYear); +} + +void ImpSvNumberformatScan::ChangeStandardPrec(short nPrec) +{ + nStandardPrec = nPrec; +} + +Color* ImpSvNumberformatScan::GetColor(String& sStr) +{ + String sString = pFormatter->GetCharClass()->upper(sStr); + const String* pKeyword = GetKeywords(); + size_t i = 0; + while (i < NF_MAX_DEFAULT_COLORS && + sString != pKeyword[NF_KEY_FIRSTCOLOR+i] ) + i++; + if ( i >= NF_MAX_DEFAULT_COLORS ) + { + const String* pEnglishColors = theEnglishColors::get(); + size_t j = 0; + while ( j < NF_MAX_DEFAULT_COLORS && + sString != pEnglishColors[j] ) + ++j; + if ( j < NF_MAX_DEFAULT_COLORS ) + i = j; + } + + Color* pResult = NULL; + if (i >= NF_MAX_DEFAULT_COLORS) + { + const String& rColorWord = pKeyword[NF_KEY_COLOR]; + xub_StrLen nPos = sString.Match(rColorWord); + if (nPos > 0) + { + sStr.Erase(0, nPos); + sStr.EraseLeadingChars(); + sStr.EraseTrailingChars(); + if (bConvertMode) + { + pFormatter->ChangeIntl(eNewLnge); + sStr.Insert( GetKeywords()[NF_KEY_COLOR], 0 ); // Color -> FARBE + pFormatter->ChangeIntl(eTmpLnge); + } + else + sStr.Insert(rColorWord,0); + sString.Erase(0, nPos); + sString.EraseLeadingChars(); + sString.EraseTrailingChars(); + + if ( CharClass::isAsciiNumeric( sString ) ) + { + long nIndex = sString.ToInt32(); + if (nIndex > 0 && nIndex <= 64) + pResult = pFormatter->GetUserDefColor((USHORT)nIndex-1); + } + } + } + else + { + sStr.Erase(); + if (bConvertMode) + { + pFormatter->ChangeIntl(eNewLnge); + sStr = GetKeywords()[NF_KEY_FIRSTCOLOR+i]; // red -> rot + pFormatter->ChangeIntl(eTmpLnge); + } + else + sStr = pKeyword[NF_KEY_FIRSTCOLOR+i]; + + pResult = &(StandardColor[i]); + } + return pResult; +} + + +short ImpSvNumberformatScan::GetKeyWord( const String& sSymbol, xub_StrLen nPos ) +{ + String sString = pFormatter->GetCharClass()->toUpper( sSymbol, nPos, sSymbol.Len() - nPos ); + const String* pKeyword = GetKeywords(); + // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere + if ( sString.Search( pKeyword[NF_KEY_GENERAL] ) == 0 ) + return NF_KEY_GENERAL; + //! MUST be a reverse search to find longer strings first + short i = NF_KEYWORD_ENTRIES_COUNT-1; + BOOL bFound = FALSE; + for ( ; i > NF_KEY_LASTKEYWORD_SO5; --i ) + { + bFound = sString.Search(pKeyword[i]) == 0; + if ( bFound ) + { + break; + } + } + // new keywords take precedence over old keywords + if ( !bFound ) + { // skip the gap of colors et al between new and old keywords and search on + i = NF_KEY_LASTKEYWORD; + while ( i > 0 && sString.Search(pKeyword[i]) != 0 ) + i--; + if ( i > NF_KEY_LASTOLDKEYWORD && sString != pKeyword[i] ) + { // found something, but maybe it's something else? + // e.g. new NNN is found in NNNN, for NNNN we must search on + short j = i - 1; + while ( j > 0 && sString.Search(pKeyword[j]) != 0 ) + j--; + if ( j && pKeyword[j].Len() > pKeyword[i].Len() ) + return j; + } + } + // The Thai T NatNum modifier during Xcl import. + if (i == 0 && bConvertMode && sString.GetChar(0) == 'T' && eTmpLnge == + LANGUAGE_ENGLISH_US && MsLangId::getRealLanguage( eNewLnge) == + LANGUAGE_THAI) + i = NF_KEY_THAI_T; + return i; // 0 => not found +} + +//--------------------------------------------------------------------------- +// Next_Symbol +//--------------------------------------------------------------------------- +// Zerlegt die Eingabe in Symbole fuer die weitere +// Verarbeitung (Turing-Maschine). +//--------------------------------------------------------------------------- +// Ausgangs Zustand = SsStart +//---------------+-------------------+-----------------------+--------------- +// Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand +//---------------+-------------------+-----------------------+--------------- +// SsStart | Buchstabe | Symbol=Zeichen | SsGetWord +// | " | Typ = String | SsGetString +// | \ | Typ = String | SsGetChar +// | * | Typ = Star | SsGetStar +// | _ | Typ = Blank | SsGetBlank +// | @ # 0 ? / . , % [ | Symbol = Zeichen; | +// | ] ' Blank | Typ = Steuerzeichen | SsStop +// | $ - + ( ) : | Typ = String; | +// | | | Typ = Comment | SsStop +// | Sonst | Symbol = Zeichen | SsStop +//---------------|-------------------+-----------------------+--------------- +// SsGetChar | Sonst | Symbol=Zeichen | SsStop +//---------------+-------------------+-----------------------+--------------- +// GetString | " | | SsStop +// | Sonst | Symbol+=Zeichen | GetString +//---------------+-------------------+-----------------------+--------------- +// SsGetWord | Buchstabe | Symbol += Zeichen | +// | + - (E+ E-)| Symbol += Zeichen | SsStop +// | / (AM/PM)| Symbol += Zeichen | +// | Sonst | Pos--, if Key Typ=Word| SsStop +//---------------+-------------------+-----------------------+--------------- +// SsGetStar | Sonst | Symbol+=Zeichen | SsStop +// | | markiere Sonderfall * | +//---------------+-------------------+-----------------------+--------------- +// SsGetBlank | Sonst | Symbol+=Zeichen | SsStop +// | | markiere Sonderfall _ | +//---------------+-------------------+-----------------------+--------------- +// Wurde im State SsGetWord ein Schluesselwort erkannt (auch als +// Anfangsteilwort des Symbols) +// so werden die restlichen Buchstaben zurueckgeschrieben !! + +enum ScanState +{ + SsStop = 0, + SsStart = 1, + SsGetChar = 2, + SsGetString = 3, + SsGetWord = 4, + SsGetStar = 5, + SsGetBlank = 6 +}; + +short ImpSvNumberformatScan::Next_Symbol( const String& rStr, + xub_StrLen& nPos, String& sSymbol ) +{ + if ( bKeywordsNeedInit ) + InitKeywords(); + const CharClass* pChrCls = pFormatter->GetCharClass(); + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + const xub_StrLen nStart = nPos; + short eType = 0; + ScanState eState = SsStart; + sSymbol.Erase(); + while ( nPos < rStr.Len() && eState != SsStop ) + { + sal_Unicode cToken = rStr.GetChar( 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 != STRING_NOTFOUND && sCurString.Len() > 1 && + nPos-1 + sCurString.Len() <= rStr.Len() && + !(nPos > 1 && rStr.GetChar( nPos-2 ) == '[') ) + { + String aTest( rStr.Copy( nPos-1, sCurString.Len() ) ); + pChrCls->toUpper( aTest ); + if ( aTest == sCurString ) + { + sSymbol = rStr.Copy( --nPos, sCurString.Len() ); + nPos = nPos + sSymbol.Len(); + eState = SsStop; + 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; + sSymbol += cToken; + eState = SsStop; + } + break; + case '*': + { + eType = NF_SYMBOLTYPE_STAR; + sSymbol += cToken; + eState = SsGetStar; + } + break; + case '_': + { + eType = NF_SYMBOLTYPE_BLANK; + sSymbol += cToken; + eState = SsGetBlank; + } + break; +#if NF_COMMENT_IN_FORMATSTRING + case '{': + eType = NF_SYMBOLTYPE_COMMENT; + eState = SsStop; + sSymbol.Append( rStr.GetBuffer() + (nPos-1), rStr.Len() - (nPos-1) ); + nPos = rStr.Len(); + break; +#endif + case '"': + eType = NF_SYMBOLTYPE_STRING; + eState = SsGetString; + sSymbol += cToken; + break; + case '\\': + eType = NF_SYMBOLTYPE_STRING; + eState = SsGetChar; + sSymbol += cToken; + break; + case '$': + case '+': + case '(': + case ')': + eType = NF_SYMBOLTYPE_STRING; + eState = SsStop; + sSymbol += cToken; + break; + default : + { + if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cToken) || + StringEqualsChar( pFormatter->GetNumThousandSep(), cToken) || + StringEqualsChar( pFormatter->GetDateSep(), cToken) || + StringEqualsChar( pLoc->getTimeSep(), cToken) || + StringEqualsChar( pLoc->getTime100SecSep(), cToken)) + { + // Another separator than pre-known ASCII + eType = NF_SYMBOLTYPE_DEL; + sSymbol += cToken; + eState = SsStop; + } + else if ( pChrCls->isLetter( rStr, nPos-1 ) ) + { + short nTmpType = GetKeyWord( rStr, nPos-1 ); + if ( nTmpType ) + { + BOOL bCurrency = FALSE; + // "Automatic" currency may start with keyword, + // like "R" (Rand) and 'R' (era) + if ( nCurrPos != STRING_NOTFOUND && + nPos-1 + sCurString.Len() <= rStr.Len() && + sCurString.Search( sKeyword[nTmpType] ) == 0 ) + { + String aTest( rStr.Copy( nPos-1, sCurString.Len() ) ); + pChrCls->toUpper( aTest ); + if ( aTest == sCurString ) + bCurrency = TRUE; + } + if ( bCurrency ) + { + eState = SsGetWord; + sSymbol += cToken; + } + else + { + eType = nTmpType; + xub_StrLen nLen = sKeyword[eType].Len(); + sSymbol = rStr.Copy( nPos-1, nLen ); + if ( eType == NF_KEY_E || IsAmbiguousE( eType ) ) + { + sal_Unicode cNext = rStr.GetChar(nPos); + switch ( cNext ) + { + case '+' : + case '-' : // E+ E- combine to one symbol + sSymbol += 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; + sSymbol += cToken; + } + } + else + { + eType = NF_SYMBOLTYPE_STRING; + eState = SsStop; + sSymbol += cToken; + } + } + break; + } + } + break; + case SsGetChar: + { + sSymbol += cToken; + eState = SsStop; + } + break; + case SsGetString: + { + if (cToken == '"') + eState = SsStop; + sSymbol += cToken; + } + break; + case SsGetWord: + { + if ( pChrCls->isLetter( rStr, nPos-1 ) ) + { + short nTmpType = GetKeyWord( rStr, nPos-1 ); + if ( nTmpType ) + { // beginning of keyword, stop scan and put back + eType = NF_SYMBOLTYPE_STRING; + eState = SsStop; + nPos--; + } + else + sSymbol += cToken; + } + else + { + BOOL bDontStop = FALSE; + switch (cToken) + { + case '/': // AM/PM, A/P + { + sal_Unicode cNext = rStr.GetChar(nPos); + if ( cNext == 'P' || cNext == 'p' ) + { + xub_StrLen nLen = sSymbol.Len(); + if ( 1 <= nLen + && (sSymbol.GetChar(0) == 'A' || sSymbol.GetChar(0) == 'a') + && (nLen == 1 || (nLen == 2 + && (sSymbol.GetChar(1) == 'M' || sSymbol.GetChar(1) == 'm') + && (rStr.GetChar(nPos+1) == 'M' || rStr.GetChar(nPos+1) == 'm'))) ) + { + sSymbol += cToken; + bDontStop = TRUE; + } + } + } + break; + } + // anything not recognized will stop the scan + if ( eState != SsStop && !bDontStop ) + { + eState = SsStop; + nPos--; + eType = NF_SYMBOLTYPE_STRING; + } + } + } + break; + case SsGetStar: + { + eState = SsStop; + sSymbol += cToken; + nRepPos = (nPos - nStart) - 1; // everytime > 0!! + } + break; + case SsGetBlank: + { + eState = SsStop; + sSymbol += cToken; + } + break; + default: + break; + } // of switch + } // of while + if (eState == SsGetWord) + eType = NF_SYMBOLTYPE_STRING; + return eType; +} + +xub_StrLen ImpSvNumberformatScan::Symbol_Division(const String& rString) +{ + nCurrPos = STRING_NOTFOUND; + // Ist Waehrung im Spiel? + String sString = pFormatter->GetCharClass()->upper(rString); + xub_StrLen nCPos = 0; + while (nCPos != STRING_NOTFOUND) + { + nCPos = sString.Search(GetCurString(),nCPos); + if (nCPos != STRING_NOTFOUND) + { + // in Quotes? + xub_StrLen nQ = SvNumberformat::GetQuoteEnd( sString, nCPos ); + if ( nQ == STRING_NOTFOUND ) + { + sal_Unicode c; + if ( nCPos == 0 || + ((c = sString.GetChar(xub_StrLen(nCPos-1))) != '"' + && c != '\\') ) // dm kann durch "dm + { // \d geschuetzt werden + nCurrPos = nCPos; + nCPos = STRING_NOTFOUND; // Abbruch + } + else + nCPos++; // weitersuchen + } + else + nCPos = nQ + 1; // weitersuchen + } + } + nAnzStrings = 0; + BOOL bStar = FALSE; // wird bei '*'Detektion gesetzt + Reset(); + + xub_StrLen nPos = 0; + const xub_StrLen nLen = rString.Len(); + while (nPos < nLen && nAnzStrings < NF_MAX_FORMAT_SYMBOLS) + { + nTypeArray[nAnzStrings] = Next_Symbol(rString, nPos, sStrArray[nAnzStrings]); + if (nTypeArray[nAnzStrings] == NF_SYMBOLTYPE_STAR) + { // Ueberwachung des '*' + if (bStar) + return nPos; // Fehler: doppelter '*' + else + bStar = TRUE; + } + nAnzStrings++; + } + + return 0; // 0 => ok +} + +void ImpSvNumberformatScan::SkipStrings(USHORT& i, xub_StrLen& nPos) +{ + while (i < nAnzStrings && ( nTypeArray[i] == NF_SYMBOLTYPE_STRING + || nTypeArray[i] == NF_SYMBOLTYPE_BLANK + || nTypeArray[i] == NF_SYMBOLTYPE_STAR) ) + { + nPos = nPos + sStrArray[i].Len(); + i++; + } +} + + +USHORT ImpSvNumberformatScan::PreviousKeyword(USHORT i) +{ + short res = 0; + if (i > 0 && i < nAnzStrings) + { + i--; + while (i > 0 && nTypeArray[i] <= 0) + i--; + if (nTypeArray[i] > 0) + res = nTypeArray[i]; + } + return res; +} + +USHORT ImpSvNumberformatScan::NextKeyword(USHORT i) +{ + short res = 0; + if (i < nAnzStrings-1) + { + i++; + while (i < nAnzStrings-1 && nTypeArray[i] <= 0) + i++; + if (nTypeArray[i] > 0) + res = nTypeArray[i]; + } + return res; +} + +short ImpSvNumberformatScan::PreviousType( USHORT i ) +{ + if ( i > 0 && i < nAnzStrings ) + { + do + { + i--; + } while ( i > 0 && nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ); + return nTypeArray[i]; + } + return 0; +} + +sal_Unicode ImpSvNumberformatScan::PreviousChar(USHORT i) +{ + sal_Unicode res = ' '; + if (i > 0 && i < nAnzStrings) + { + 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].Len() > 0) + res = sStrArray[i].GetChar(xub_StrLen(sStrArray[i].Len()-1)); + } + return res; +} + +sal_Unicode ImpSvNumberformatScan::NextChar(USHORT i) +{ + sal_Unicode res = ' '; + if (i < nAnzStrings-1) + { + i++; + while (i < nAnzStrings-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].Len() > 0) + res = sStrArray[i].GetChar(0); + } + return res; +} + +BOOL ImpSvNumberformatScan::IsLastBlankBeforeFrac(USHORT i) +{ + BOOL res = TRUE; + if (i < nAnzStrings-1) + { + BOOL bStop = FALSE; + i++; + while (i < nAnzStrings-1 && !bStop) + { + i++; + if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL && + sStrArray[i].GetChar(0) == '/') + bStop = TRUE; + else if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL && + sStrArray[i].GetChar(0) == ' ') + res = FALSE; + } + if (!bStop) // kein '/' + res = FALSE; + } + else + res = FALSE; // kein '/' mehr + + return res; +} + +void ImpSvNumberformatScan::Reset() +{ + nAnzStrings = 0; + nAnzResStrings = 0; +#if 0 +// ER 20.06.97 14:05 nicht noetig, wenn nAnzStrings beachtet wird + for (size_t i = 0; i < NF_MAX_FORMAT_SYMBOLS; i++) + { + sStrArray[i].Erase(); + nTypeArray[i] = 0; + } +#endif + eScannedType = NUMBERFORMAT_UNDEFINED; + nRepPos = 0; + bExp = FALSE; + bThousand = FALSE; + nThousand = 0; + bDecSep = FALSE; + nDecPos = -1; + nExpPos = (USHORT) -1; + nBlankPos = (USHORT) -1; + nCntPre = 0; + nCntPost = 0; + nCntExp = 0; + bFrac = FALSE; + bBlank = FALSE; + nNatNumModifier = 0; +} + + +BOOL ImpSvNumberformatScan::Is100SecZero( USHORT i, BOOL bHadDecSep ) +{ + USHORT nIndexPre = PreviousKeyword( i ); + return (nIndexPre == NF_KEY_S || nIndexPre == NF_KEY_SS) + && (bHadDecSep // S, SS ',' + || (i>0 && nTypeArray[i-1] == NF_SYMBOLTYPE_STRING)); + // SS"any"00 take "any" as a valid decimal separator +} + + +xub_StrLen ImpSvNumberformatScan::ScanType(const String&) +{ + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + + xub_StrLen nPos = 0; + USHORT i = 0; + short eNewType; + BOOL bMatchBracket = FALSE; + bool bHaveGeneral = false; // if General/Standard encountered + + SkipStrings(i, nPos); + while (i < nAnzStrings) + { + if (nTypeArray[i] > 0) + { // keyword + switch (nTypeArray[i]) + { + case NF_KEY_E: // E + eNewType = NUMBERFORMAT_SCIENTIFIC; + break; + case NF_KEY_AMPM: // AM,A,PM,P + case NF_KEY_AP: + case NF_KEY_H: // H + case NF_KEY_HH: // HH + case NF_KEY_S: // S + case NF_KEY_SS: // SS + eNewType = NUMBERFORMAT_TIME; + break; + case NF_KEY_M: // M + case NF_KEY_MM: // MM + { // minute or month + USHORT nIndexPre = PreviousKeyword(i); + USHORT nIndexNex = NextKeyword(i); + sal_Unicode cChar = PreviousChar(i); + if (nIndexPre == NF_KEY_H || // H + nIndexPre == NF_KEY_HH || // HH + nIndexNex == NF_KEY_S || // S + nIndexNex == NF_KEY_SS || // SS + cChar == '[' ) // [M + { + eNewType = NUMBERFORMAT_TIME; + nTypeArray[i] -= 2; // 6 -> 4, 7 -> 5 + } + else + eNewType = NUMBERFORMAT_DATE; + } + 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 = NUMBERFORMAT_DATE; + break; + case NF_KEY_CCC: // CCC + eNewType = NUMBERFORMAT_CURRENCY; + break; + case NF_KEY_GENERAL: // Standard + eNewType = NUMBERFORMAT_NUMBER; + bHaveGeneral = true; + break; + default: + eNewType = NUMBERFORMAT_UNDEFINED; + break; + } + } + else + { // control character + switch ( sStrArray[i].GetChar(0) ) + { + case '#': + case '?': + eNewType = NUMBERFORMAT_NUMBER; + break; + case '0': + { + if ( (eScannedType & NUMBERFORMAT_TIME) == NUMBERFORMAT_TIME ) + { + if ( Is100SecZero( i, bDecSep ) ) + { + bDecSep = TRUE; // subsequent 0's + eNewType = NUMBERFORMAT_TIME; + } + else + return nPos; // Error + } + else + eNewType = NUMBERFORMAT_NUMBER; + } + break; + case '%': + eNewType = NUMBERFORMAT_PERCENT; + break; + case '/': + eNewType = NUMBERFORMAT_FRACTION; + break; + case '[': + { + if ( i < nAnzStrings-1 && + nTypeArray[i+1] == NF_SYMBOLTYPE_STRING && + sStrArray[i+1].GetChar(0) == '$' ) + { // as of SV_NUMBERFORMATTER_VERSION_NEW_CURR + eNewType = NUMBERFORMAT_CURRENCY; + bMatchBracket = TRUE; + } + else if ( i < nAnzStrings-1 && + nTypeArray[i+1] == NF_SYMBOLTYPE_STRING && + sStrArray[i+1].GetChar(0) == '~' ) + { // as of SV_NUMBERFORMATTER_VERSION_CALENDAR + eNewType = NUMBERFORMAT_DATE; + bMatchBracket = TRUE; + } + else + { + USHORT 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 = NUMBERFORMAT_TIME; + else + return nPos; // Error + } + } + break; + case '@': + eNewType = NUMBERFORMAT_TEXT; + break; + default: + if ( sStrArray[i] == pLoc->getTime100SecSep() ) + bDecSep = TRUE; // for SS,0 + eNewType = NUMBERFORMAT_UNDEFINED; + break; + } + } + if (eScannedType == NUMBERFORMAT_UNDEFINED) + eScannedType = eNewType; + else if (eScannedType == NUMBERFORMAT_TEXT || eNewType == NUMBERFORMAT_TEXT) + eScannedType = NUMBERFORMAT_TEXT; // Text bleibt immer Text + else if (eNewType == NUMBERFORMAT_UNDEFINED) + { // bleibt wie bisher + } + else if (eScannedType != eNewType) + { + switch (eScannedType) + { + case NUMBERFORMAT_DATE: + { + switch (eNewType) + { + case NUMBERFORMAT_TIME: + eScannedType = NUMBERFORMAT_DATETIME; + break; + case NUMBERFORMAT_FRACTION: // DD/MM + break; + default: + { + if (nCurrPos != STRING_NOTFOUND) + eScannedType = NUMBERFORMAT_UNDEFINED; + else if ( sStrArray[i] != pFormatter->GetDateSep() ) + return nPos; + } + } + } + break; + case NUMBERFORMAT_TIME: + { + switch (eNewType) + { + case NUMBERFORMAT_DATE: + eScannedType = NUMBERFORMAT_DATETIME; + break; + case NUMBERFORMAT_FRACTION: // MM/SS + break; + default: + { + if (nCurrPos != STRING_NOTFOUND) + eScannedType = NUMBERFORMAT_UNDEFINED; + else if ( sStrArray[i] != pLoc->getTimeSep() ) + return nPos; + } + } + } + break; + case NUMBERFORMAT_DATETIME: + { + switch (eNewType) + { + case NUMBERFORMAT_TIME: + case NUMBERFORMAT_DATE: + break; + case NUMBERFORMAT_FRACTION: // DD/MM + break; + default: + { + if (nCurrPos != STRING_NOTFOUND) + eScannedType = NUMBERFORMAT_UNDEFINED; + else if ( sStrArray[i] != pFormatter->GetDateSep() + && sStrArray[i] != pLoc->getTimeSep() ) + return nPos; + } + } + } + break; + case NUMBERFORMAT_PERCENT: + { + switch (eNewType) + { + case NUMBERFORMAT_NUMBER: // nur Zahl nach Prozent + break; + default: + return nPos; + } + } + break; + case NUMBERFORMAT_SCIENTIFIC: + { + switch (eNewType) + { + case NUMBERFORMAT_NUMBER: // nur Zahl nach E + break; + default: + return nPos; + } + } + break; + case NUMBERFORMAT_NUMBER: + { + switch (eNewType) + { + case NUMBERFORMAT_SCIENTIFIC: + case NUMBERFORMAT_PERCENT: + case NUMBERFORMAT_FRACTION: + case NUMBERFORMAT_CURRENCY: + eScannedType = eNewType; + break; + default: + if (nCurrPos != STRING_NOTFOUND) + eScannedType = NUMBERFORMAT_UNDEFINED; + else + return nPos; + } + } + break; + case NUMBERFORMAT_FRACTION: + { + switch (eNewType) + { + case NUMBERFORMAT_NUMBER: // nur Zahl nach Bruch + break; + default: + return nPos; + } + } + break; + default: + break; + } + } + nPos = nPos + sStrArray[i].Len(); // Korrekturposition + i++; + if ( bMatchBracket ) + { // no type detection inside of matching brackets if [$...], [~...] + while ( bMatchBracket && i < nAnzStrings ) + { + if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL + && sStrArray[i].GetChar(0) == ']' ) + bMatchBracket = FALSE; + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + if ( bMatchBracket ) + return nPos; // missing closing bracket at end of code + } + SkipStrings(i, nPos); + } + + if ((eScannedType == NUMBERFORMAT_NUMBER || eScannedType == NUMBERFORMAT_UNDEFINED) + && nCurrPos != STRING_NOTFOUND && !bHaveGeneral) + eScannedType = NUMBERFORMAT_CURRENCY; // old "automatic" currency + if (eScannedType == NUMBERFORMAT_UNDEFINED) + eScannedType = NUMBERFORMAT_DEFINED; + return 0; // Alles ok +} + + +bool ImpSvNumberformatScan::InsertSymbol( USHORT & nPos, svt::NfSymbolType eType, const String& rStr ) +{ + if (nAnzStrings >= NF_MAX_FORMAT_SYMBOLS || nPos > nAnzStrings) + return false; + ++nAnzResStrings; + if (nPos > 0 && nTypeArray[nPos-1] == NF_SYMBOLTYPE_EMPTY) + --nPos; // reuse position + else + { + ++nAnzStrings; + for (size_t i = nAnzStrings; i > nPos; --i) + { + nTypeArray[i] = nTypeArray[i-1]; + sStrArray[i] = sStrArray[i-1]; + } + } + nTypeArray[nPos] = static_cast<short>(eType); + sStrArray[nPos] = rStr; + return true; +} + + +int ImpSvNumberformatScan::FinalScanGetCalendar( xub_StrLen& nPos, USHORT& i, + USHORT& rAnzResStrings ) +{ + if ( sStrArray[i].GetChar(0) == '[' && + i < nAnzStrings-1 && + nTypeArray[i+1] == NF_SYMBOLTYPE_STRING && + sStrArray[i+1].GetChar(0) == '~' ) + { // [~calendarID] + // as of SV_NUMBERFORMATTER_VERSION_CALENDAR + nPos = nPos + sStrArray[i].Len(); // [ + nTypeArray[i] = NF_SYMBOLTYPE_CALDEL; + nPos = nPos + sStrArray[++i].Len(); // ~ + sStrArray[i-1] += sStrArray[i]; // [~ + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + rAnzResStrings--; + if ( ++i >= nAnzStrings ) + return -1; // error + nPos = nPos + sStrArray[i].Len(); // calendarID + String& rStr = sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_CALENDAR; // convert + i++; + while ( i < nAnzStrings && + sStrArray[i].GetChar(0) != ']' ) + { + nPos = nPos + sStrArray[i].Len(); + rStr += sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + rAnzResStrings--; + i++; + } + if ( rStr.Len() && i < nAnzStrings && + sStrArray[i].GetChar(0) == ']' ) + { + nTypeArray[i] = NF_SYMBOLTYPE_CALDEL; + nPos = nPos + sStrArray[i].Len(); + i++; + } + else + return -1; // error + return 1; + } + return 0; +} + +xub_StrLen ImpSvNumberformatScan::FinalScan( String& rString, String& rComment ) +{ + const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); + + // save values for convert mode + String sOldDecSep = pFormatter->GetNumDecimalSep(); + String sOldThousandSep = pFormatter->GetNumThousandSep(); + String sOldDateSep = pFormatter->GetDateSep(); + String sOldTimeSep = pLoc->getTimeSep(); + String sOldTime100SecSep= pLoc->getTime100SecSep(); + String sOldCurSymbol = GetCurSymbol(); + String sOldCurString = GetCurString(); + sal_Unicode cOldKeyH = sKeyword[NF_KEY_H].GetChar(0); + sal_Unicode cOldKeyMI = sKeyword[NF_KEY_MI].GetChar(0); + sal_Unicode cOldKeyS = sKeyword[NF_KEY_S].GetChar(0); + + // If the group separator is a Non-Breaking Space (French) continue with a + // normal space instead so queries on space work correctly. + // The format string is adjusted to allow both. + // For output of the format code string the LocaleData characters are used. + if ( sOldThousandSep.GetChar(0) == cNonBreakingSpace && sOldThousandSep.Len() == 1 ) + sOldThousandSep = ' '; + + // change locale data et al + if (bConvertMode) + { + pFormatter->ChangeIntl(eNewLnge); + //! pointer may have changed + pLoc = pFormatter->GetLocaleData(); + //! init new keywords + InitKeywords(); + } + const CharClass* pChrCls = pFormatter->GetCharClass(); + + xub_StrLen nPos = 0; // error correction position + USHORT i = 0; // symbol loop counter + USHORT nCounter = 0; // counts digits + nAnzResStrings = nAnzStrings; // counts remaining symbols + bDecSep = FALSE; // reset in case already used in TypeCheck + bool bThaiT = false; // Thai T NatNum modifier present + + switch (eScannedType) + { + case NUMBERFORMAT_TEXT: + case NUMBERFORMAT_DEFINED: + { + while (i < nAnzStrings) + { + switch (nTypeArray[i]) + { + case NF_SYMBOLTYPE_BLANK: + case NF_SYMBOLTYPE_STAR: + break; + case NF_SYMBOLTYPE_COMMENT: + { + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + SvNumberformat::EraseCommentBraces( rStr ); + rComment += rStr; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + break; + case NF_KEY_GENERAL : // #77026# "General" is the same as "@" + break; + default: + { + if ( nTypeArray[i] != NF_SYMBOLTYPE_DEL || + sStrArray[i].GetChar(0) != '@' ) + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + } + break; + } + nPos = nPos + sStrArray[i].Len(); + i++; + } // of while + } + break; + case NUMBERFORMAT_NUMBER: + case NUMBERFORMAT_PERCENT: + case NUMBERFORMAT_CURRENCY: + case NUMBERFORMAT_SCIENTIFIC: + case NUMBERFORMAT_FRACTION: + { + sal_Unicode cThousandFill = ' '; + while (i < nAnzStrings) + { + if (eScannedType == NUMBERFORMAT_FRACTION && // special case + nTypeArray[i] == NF_SYMBOLTYPE_DEL && // # ### #/# + StringEqualsChar( sOldThousandSep, ' ' ) && // e.g. France or Sweden + StringEqualsChar( sStrArray[i], ' ' ) && + !bFrac && + IsLastBlankBeforeFrac(i) ) + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; // del->string + } // kein Taus.p. + + + if (nTypeArray[i] == NF_SYMBOLTYPE_BLANK || + nTypeArray[i] == NF_SYMBOLTYPE_STAR || + nTypeArray[i] == NF_KEY_CCC || // CCC + nTypeArray[i] == NF_KEY_GENERAL ) // Standard + { + if (nTypeArray[i] == NF_KEY_GENERAL) + { + nThousand = FLAG_STANDARD_IN_FORMAT; + if ( bConvertMode ) + sStrArray[i] = sNameStandardFormat; + } + nPos = nPos + sStrArray[i].Len(); + i++; + } + else if (nTypeArray[i] == NF_SYMBOLTYPE_STRING || // Strings oder + nTypeArray[i] > 0) // Keywords + { + if (eScannedType == NUMBERFORMAT_SCIENTIFIC && + nTypeArray[i] == NF_KEY_E) // E+ + { + if (bExp) // doppelt + return nPos; + bExp = TRUE; + nExpPos = i; + if (bDecSep) + nCntPost = nCounter; + else + nCntPre = nCounter; + nCounter = 0; + nTypeArray[i] = NF_SYMBOLTYPE_EXP; + } + else if (eScannedType == NUMBERFORMAT_FRACTION && + sStrArray[i].GetChar(0) == ' ') + { + if (!bBlank && !bFrac) // nicht doppelt oder hinter / + { + if (bDecSep && nCounter > 0) // Nachkommastellen + return nPos; // Fehler + bBlank = TRUE; + nBlankPos = i; + nCntPre = nCounter; + nCounter = 0; + } + nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK; + } + else if (nTypeArray[i] == NF_KEY_THAI_T) + { + bThaiT = true; + sStrArray[i] = sKeyword[nTypeArray[i]]; + } + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + else if (nTypeArray[i] == NF_SYMBOLTYPE_DEL) + { + sal_Unicode cHere = sStrArray[i].GetChar(0); + // Handle not pre-known separators in switch. + sal_Unicode cSimplified; + if (StringEqualsChar( pFormatter->GetNumThousandSep(), cHere)) + cSimplified = ','; + else if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cHere)) + cSimplified = '.'; + else + cSimplified = cHere; + switch ( cSimplified ) + { + case '#': + case '0': + case '?': + { + if (nThousand > 0) // #... # + return nPos; // Fehler + else if (bFrac && cHere == '0') + return nPos; // 0 im Nenner + nTypeArray[i] = NF_SYMBOLTYPE_DIGIT; + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + i++; + nCounter++; + while (i < nAnzStrings && + (sStrArray[i].GetChar(0) == '#' || + sStrArray[i].GetChar(0) == '0' || + sStrArray[i].GetChar(0) == '?') + ) + { + nTypeArray[i] = NF_SYMBOLTYPE_DIGIT; + nPos = nPos + sStrArray[i].Len(); + nCounter++; + i++; + } + } + break; + case '-': + { + if ( bDecSep && nDecPos+1 == i && + nTypeArray[nDecPos] == NF_SYMBOLTYPE_DECSEP ) + { // "0.--" + nTypeArray[i] = NF_SYMBOLTYPE_DIGIT; + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + i++; + nCounter++; + while (i < nAnzStrings && + (sStrArray[i].GetChar(0) == '-') ) + { + // If more than two dashes are present in + // currency formats the last dash will be + // interpreted literally as a minus sign. + // Has to be this ugly. Period. + if ( eScannedType == NUMBERFORMAT_CURRENCY + && rStr.Len() >= 2 && + (i == nAnzStrings-1 || + sStrArray[i+1].GetChar(0) != '-') ) + break; + rStr += sStrArray[i]; + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + nCounter++; + i++; + } + } + else + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + } + break; + case '.': + case ',': + case '\'': + case ' ': + { + sal_Unicode cSep = cHere; // remember + if ( StringEqualsChar( sOldThousandSep, cSep ) ) + { + // previous char with skip empty + sal_Unicode cPre = PreviousChar(i); + sal_Unicode cNext; + if (bExp || bBlank || bFrac) + { // after E, / or ' ' + if ( !StringEqualsChar( sOldThousandSep, ' ' ) ) + { + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; // eat it + } + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + } + else if (i > 0 && i < nAnzStrings-1 && + (cPre == '#' || cPre == '0') && + ((cNext = NextChar(i)) == '#' || cNext == '0') + ) // #,# + { + nPos = nPos + sStrArray[i].Len(); + if (!bThousand) // only once + { + bThousand = TRUE; + cThousandFill = sStrArray[i+1].GetChar(0); + } + // Eat it, will be reinserted at proper + // grouping positions further down. + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + else if (i > 0 && (cPre == '#' || cPre == '0') + && PreviousType(i) == NF_SYMBOLTYPE_DIGIT + && nThousand < FLAG_STANDARD_IN_FORMAT ) + { // #,,,, + if ( StringEqualsChar( sOldThousandSep, ' ' ) ) + { // strange, those French.. + BOOL bFirst = TRUE; + String& rStr = sStrArray[i]; + // set a hard Non-Breaking Space or ConvertMode + const String& rSepF = pFormatter->GetNumThousandSep(); + while ( i < nAnzStrings + && sStrArray[i] == sOldThousandSep + && StringEqualsChar( sOldThousandSep, NextChar(i) ) ) + { // last was a space or another space + // is following => separator + nPos = nPos + sStrArray[i].Len(); + if ( bFirst ) + { + bFirst = FALSE; + rStr = rSepF; + nTypeArray[i] = NF_SYMBOLTYPE_THSEP; + } + else + { + rStr += rSepF; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + nThousand++; + i++; + } + if ( i < nAnzStrings-1 + && sStrArray[i] == sOldThousandSep ) + { // something following last space + // => space if currency contained, + // else separator + nPos = nPos + sStrArray[i].Len(); + if ( (nPos <= nCurrPos && + nCurrPos < nPos + sStrArray[i+1].Len()) + || nTypeArray[i+1] == NF_KEY_CCC + || (i < nAnzStrings-2 && + sStrArray[i+1].GetChar(0) == '[' && + sStrArray[i+2].GetChar(0) == '$') ) + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + } + else + { + if ( bFirst ) + { + bFirst = FALSE; + rStr = rSepF; + nTypeArray[i] = NF_SYMBOLTYPE_THSEP; + } + else + { + rStr += rSepF; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + nThousand++; + } + i++; + } + } + else + { + do + { + nThousand++; + nTypeArray[i] = NF_SYMBOLTYPE_THSEP; + nPos = nPos + sStrArray[i].Len(); + sStrArray[i] = pFormatter->GetNumThousandSep(); + i++; + } while (i < nAnzStrings && + sStrArray[i] == sOldThousandSep); + } + } + else // any grsep + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + i++; + while ( i < nAnzStrings && + sStrArray[i] == sOldThousandSep ) + { + rStr += sStrArray[i]; + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + } + } + else if ( StringEqualsChar( sOldDecSep, cSep ) ) + { + if (bBlank || bFrac) // . behind / or ' ' + return nPos; // error + else if (bExp) // behind E + { + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; // eat it + } + else if (bDecSep) // any . + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + i++; + while ( i < nAnzStrings && + sStrArray[i] == sOldDecSep ) + { + rStr += sStrArray[i]; + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + } + else + { + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_DECSEP; + sStrArray[i] = pFormatter->GetNumDecimalSep(); + bDecSep = TRUE; + nDecPos = i; + nCntPre = nCounter; + nCounter = 0; + + i++; + } + } // of else = DecSep + else // . without meaning + { + if (cSep == ' ' && + eScannedType == NUMBERFORMAT_FRACTION && + StringEqualsChar( sStrArray[i], ' ' ) ) + { + if (!bBlank && !bFrac) // no dups + { // or behind / + if (bDecSep && nCounter > 0)// dec. + return nPos; // error + bBlank = TRUE; + nBlankPos = i; + nCntPre = nCounter; + nCounter = 0; + } + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + } + else + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + i++; + while (i < nAnzStrings && + StringEqualsChar( sStrArray[i], cSep ) ) + { + rStr += sStrArray[i]; + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + } + } + } + break; + case '/': + { + if (eScannedType == NUMBERFORMAT_FRACTION) + { + if ( i == 0 || + (nTypeArray[i-1] != NF_SYMBOLTYPE_DIGIT && + nTypeArray[i-1] != NF_SYMBOLTYPE_EMPTY) ) + return nPos ? nPos : 1; // /? not allowed + else if (!bFrac || (bDecSep && nCounter > 0)) + { + bFrac = TRUE; + nCntPost = nCounter; + nCounter = 0; + nTypeArray[i] = NF_SYMBOLTYPE_FRAC; + nPos = nPos + sStrArray[i].Len(); + i++; + } + else // / doppelt od. , imZaehl + return nPos; // Fehler + } + else + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + } + break; + case '[' : + { + if ( eScannedType == NUMBERFORMAT_CURRENCY && + i < nAnzStrings-1 && + nTypeArray[i+1] == NF_SYMBOLTYPE_STRING && + sStrArray[i+1].GetChar(0) == '$' ) + { // [$DM-xxx] + // ab SV_NUMBERFORMATTER_VERSION_NEW_CURR + nPos = nPos + sStrArray[i].Len(); // [ + nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL; + nPos = nPos + sStrArray[++i].Len(); // $ + sStrArray[i-1] += sStrArray[i]; // [$ + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + if ( ++i >= nAnzStrings ) + return nPos; // Fehler + nPos = nPos + sStrArray[i].Len(); // DM + String& rStr = sStrArray[i]; + String* pStr = &sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_CURRENCY; // wandeln + BOOL bHadDash = FALSE; + i++; + while ( i < nAnzStrings && + sStrArray[i].GetChar(0) != ']' ) + { + nPos = nPos + sStrArray[i].Len(); + if ( bHadDash ) + { + *pStr += sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + else + { + if ( sStrArray[i].GetChar(0) == '-' ) + { + bHadDash = TRUE; + pStr = &sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_CURREXT; + } + else + { + *pStr += sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + } + i++; + } + if ( rStr.Len() && i < nAnzStrings && + sStrArray[i].GetChar(0) == ']' ) + { + nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL; + nPos = nPos + sStrArray[i].Len(); + i++; + } + else + return nPos; // Fehler + } + else + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + } + break; + default: // andere Dels + { + if (eScannedType == NUMBERFORMAT_PERCENT && + cHere == '%') + nTypeArray[i] = NF_SYMBOLTYPE_PERCENT; + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + } // of switch (Del) + } // of else Del + else if ( nTypeArray[i] == NF_SYMBOLTYPE_COMMENT ) + { + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + SvNumberformat::EraseCommentBraces( rStr ); + rComment += rStr; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + else + { + DBG_ERRORFILE( "unknown NF_SYMBOLTYPE_..." ); + nPos = nPos + sStrArray[i].Len(); + i++; + } + } // of while + if (eScannedType == NUMBERFORMAT_FRACTION) + { + if (bFrac) + nCntExp = nCounter; + else if (bBlank) + nCntPost = nCounter; + else + nCntPre = nCounter; + } + else + { + if (bExp) + nCntExp = nCounter; + else if (bDecSep) + nCntPost = nCounter; + else + nCntPre = nCounter; + } + if (bThousand) // Expansion of grouping separators + { + USHORT nMaxPos; + if (bFrac) + { + if (bBlank) + nMaxPos = nBlankPos; + else + nMaxPos = 0; // no grouping + } + else if (bDecSep) // decimal separator present + nMaxPos = nDecPos; + else if (bExp) // 'E' exponent present + nMaxPos = nExpPos; + else // up to end + nMaxPos = i; + // Insert separators at proper positions. + xub_StrLen nCount = 0; + utl::DigitGroupingIterator aGrouping( pLoc->getDigitGrouping()); + size_t nFirstDigitSymbol = nMaxPos; + size_t nFirstGroupingSymbol = nMaxPos; + i = nMaxPos; + while (i-- > 0) + { + if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT) + { + nFirstDigitSymbol = i; + nCount = nCount + sStrArray[i].Len(); // MSC converts += to int and then warns, so ... + // Insert separator only if not leftmost symbol. + if (i > 0 && nCount >= aGrouping.getPos()) + { + DBG_ASSERT( sStrArray[i].Len() == 1, + "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion"); + if (!InsertSymbol( i, NF_SYMBOLTYPE_THSEP, + pFormatter->GetNumThousandSep())) + // nPos isn't correct here, but signals error + return nPos; + // i may have been decremented by 1 + nFirstDigitSymbol = i + 1; + nFirstGroupingSymbol = i; + aGrouping.advance(); + } + } + } + // Generated something like "string",000; remove separator again. + if (nFirstGroupingSymbol < nFirstDigitSymbol) + { + nTypeArray[nFirstGroupingSymbol] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + } + // Combine digits into groups to save memory (Info will be copied + // later, taking only non-empty symbols). + for (i = 0; i < nAnzStrings; ++i) + { + if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT) + { + String& rStr = sStrArray[i]; + while (++i < nAnzStrings && + nTypeArray[i] == NF_SYMBOLTYPE_DIGIT) + { + rStr += sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + } + } + } + break; // of NUMBERFORMAT_NUMBER + case NUMBERFORMAT_DATE: + { + while (i < nAnzStrings) + { + switch (nTypeArray[i]) + { + case NF_SYMBOLTYPE_BLANK: + case NF_SYMBOLTYPE_STAR: + case NF_SYMBOLTYPE_STRING: + nPos = nPos + sStrArray[i].Len(); + i++; + break; + case NF_SYMBOLTYPE_COMMENT: + { + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + SvNumberformat::EraseCommentBraces( rStr ); + rComment += rStr; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + break; + case NF_SYMBOLTYPE_DEL: + { + int nCalRet; + if (sStrArray[i] == sOldDateSep) + { + nTypeArray[i] = NF_SYMBOLTYPE_DATESEP; + nPos = nPos + sStrArray[i].Len(); + if (bConvertMode) + sStrArray[i] = pFormatter->GetDateSep(); + i++; + } + else if ( (nCalRet = FinalScanGetCalendar( nPos, i, nAnzResStrings )) != 0 ) + { + if ( nCalRet < 0 ) + return nPos; // error + } + else + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + } + break; + case NF_KEY_THAI_T : + bThaiT = true; + // fall thru + case NF_KEY_M: // M + case NF_KEY_MM: // MM + case NF_KEY_MMM: // MMM + case NF_KEY_MMMM: // MMMM + case NF_KEY_MMMMM: // MMMMM + case NF_KEY_Q: // Q + case NF_KEY_QQ: // QQ + case NF_KEY_D: // D + case NF_KEY_DD: // DD + case NF_KEY_DDD: // DDD + case NF_KEY_DDDD: // DDDD + case NF_KEY_YY: // YY + case NF_KEY_YYYY: // YYYY + case NF_KEY_NN: // NN + case NF_KEY_NNN: // NNN + case NF_KEY_NNNN: // NNNN + case NF_KEY_WW : // WW + case NF_KEY_AAA : // AAA + case NF_KEY_AAAA : // AAAA + case NF_KEY_EC : // E + case NF_KEY_EEC : // EE + case NF_KEY_G : // G + case NF_KEY_GG : // GG + case NF_KEY_GGG : // GGG + case NF_KEY_R : // R + case NF_KEY_RR : // RR + sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT + nPos = nPos + sStrArray[i].Len(); + i++; + break; + default: // andere Keywords + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + break; + } + } // of while + } + break; // of NUMBERFORMAT_DATE + case NUMBERFORMAT_TIME: + { + while (i < nAnzStrings) + { + switch (nTypeArray[i]) + { + case NF_SYMBOLTYPE_BLANK: + case NF_SYMBOLTYPE_STAR: + { + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + case NF_SYMBOLTYPE_DEL: + { + switch( sStrArray[i].GetChar(0) ) + { + case '0': + { + if ( Is100SecZero( i, bDecSep ) ) + { + bDecSep = TRUE; + nTypeArray[i] = NF_SYMBOLTYPE_DIGIT; + String& rStr = sStrArray[i]; + i++; + nPos = nPos + sStrArray[i].Len(); + nCounter++; + while (i < nAnzStrings && + sStrArray[i].GetChar(0) == '0') + { + rStr += sStrArray[i]; + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + nCounter++; + i++; + } + } + else + return nPos; + } + break; + case '#': + case '?': + return nPos; + case '[': + { + if (bThousand) // doppelt + return nPos; + bThousand = TRUE; // bei Time frei + sal_Unicode cChar = pChrCls->upper( NextChar(i) ).GetChar(0); + if ( cChar == cOldKeyH ) + nThousand = 1; // H + else if ( cChar == cOldKeyMI ) + nThousand = 2; // M + else if ( cChar == cOldKeyS ) + nThousand = 3; // S + else + return nPos; + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + case ']': + { + if (!bThousand) // kein [ vorher + return nPos; + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + default: + { + nPos = nPos + sStrArray[i].Len(); + if ( sStrArray[i] == sOldTimeSep ) + { + nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP; + if ( bConvertMode ) + sStrArray[i] = pLoc->getTimeSep(); + } + else if ( sStrArray[i] == sOldTime100SecSep ) + { + bDecSep = TRUE; + nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP; + if ( bConvertMode ) + sStrArray[i] = pLoc->getTime100SecSep(); + } + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + i++; + } + break; + } + } + break; + case NF_SYMBOLTYPE_STRING: + { + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + case NF_SYMBOLTYPE_COMMENT: + { + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + SvNumberformat::EraseCommentBraces( rStr ); + rComment += rStr; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + break; + case NF_KEY_AMPM: // AM/PM + case NF_KEY_AP: // A/P + { + bExp = TRUE; // missbraucht fuer A/P + sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + case NF_KEY_THAI_T : + bThaiT = true; + // fall thru + case NF_KEY_MI: // M + case NF_KEY_MMI: // MM + case NF_KEY_H: // H + case NF_KEY_HH: // HH + case NF_KEY_S: // S + case NF_KEY_SS: // SS + { + sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + default: // andere Keywords + { + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + } + } // of while + nCntPost = nCounter; // Zaehler der Nullen + if (bExp) + nCntExp = 1; // merkt AM/PM + } + break; // of NUMBERFORMAT_TIME + case NUMBERFORMAT_DATETIME: + { + BOOL bTimePart = FALSE; + while (i < nAnzStrings) + { + switch (nTypeArray[i]) + { + case NF_SYMBOLTYPE_BLANK: + case NF_SYMBOLTYPE_STAR: + case NF_SYMBOLTYPE_STRING: + nPos = nPos + sStrArray[i].Len(); + i++; + break; + case NF_SYMBOLTYPE_COMMENT: + { + String& rStr = sStrArray[i]; + nPos = nPos + rStr.Len(); + SvNumberformat::EraseCommentBraces( rStr ); + rComment += rStr; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + i++; + } + break; + case NF_SYMBOLTYPE_DEL: + { + int nCalRet; + if ( (nCalRet = FinalScanGetCalendar( nPos, i, nAnzResStrings )) != 0 ) + { + if ( nCalRet < 0 ) + return nPos; // error + } + else + { + switch( sStrArray[i].GetChar(0) ) + { + case '0': + { + if ( bTimePart && Is100SecZero( i, bDecSep ) ) + { + bDecSep = TRUE; + nTypeArray[i] = NF_SYMBOLTYPE_DIGIT; + String& rStr = sStrArray[i]; + i++; + nPos = nPos + sStrArray[i].Len(); + nCounter++; + while (i < nAnzStrings && + sStrArray[i].GetChar(0) == '0') + { + rStr += sStrArray[i]; + nPos = nPos + sStrArray[i].Len(); + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + nCounter++; + i++; + } + } + else + return nPos; + } + break; + case '#': + case '?': + return nPos; + default: + { + nPos = nPos + sStrArray[i].Len(); + if (bTimePart) + { + if ( sStrArray[i] == sOldTimeSep ) + { + nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP; + if ( bConvertMode ) + sStrArray[i] = pLoc->getTimeSep(); + } + else if ( sStrArray[i] == sOldTime100SecSep ) + { + bDecSep = TRUE; + nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP; + if ( bConvertMode ) + sStrArray[i] = pLoc->getTime100SecSep(); + } + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + } + else + { + if ( sStrArray[i] == sOldDateSep ) + { + nTypeArray[i] = NF_SYMBOLTYPE_DATESEP; + if (bConvertMode) + sStrArray[i] = pFormatter->GetDateSep(); + } + else + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + } + i++; + } + } + } + } + break; + case NF_KEY_AMPM: // AM/PM + case NF_KEY_AP: // A/P + { + bTimePart = TRUE; + bExp = TRUE; // missbraucht fuer A/P + sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT + nPos = nPos + sStrArray[i].Len(); + i++; + } + break; + case NF_KEY_MI: // M + case NF_KEY_MMI: // MM + case NF_KEY_H: // H + case NF_KEY_HH: // HH + case NF_KEY_S: // S + case NF_KEY_SS: // SS + bTimePart = TRUE; + sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT + nPos = nPos + sStrArray[i].Len(); + i++; + break; + case NF_KEY_M: // M + case NF_KEY_MM: // MM + case NF_KEY_MMM: // MMM + case NF_KEY_MMMM: // MMMM + case NF_KEY_MMMMM: // MMMMM + case NF_KEY_Q: // Q + case NF_KEY_QQ: // QQ + case NF_KEY_D: // D + case NF_KEY_DD: // DD + case NF_KEY_DDD: // DDD + case NF_KEY_DDDD: // DDDD + case NF_KEY_YY: // YY + case NF_KEY_YYYY: // YYYY + case NF_KEY_NN: // NN + case NF_KEY_NNN: // NNN + case NF_KEY_NNNN: // NNNN + case NF_KEY_WW : // WW + case NF_KEY_AAA : // AAA + case NF_KEY_AAAA : // AAAA + case NF_KEY_EC : // E + case NF_KEY_EEC : // EE + case NF_KEY_G : // G + case NF_KEY_GG : // GG + case NF_KEY_GGG : // GGG + case NF_KEY_R : // R + case NF_KEY_RR : // RR + bTimePart = FALSE; + sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT + nPos = nPos + sStrArray[i].Len(); + i++; + break; + case NF_KEY_THAI_T : + bThaiT = true; + sStrArray[i] = sKeyword[nTypeArray[i]]; + nPos = nPos + sStrArray[i].Len(); + i++; + break; + default: // andere Keywords + nTypeArray[i] = NF_SYMBOLTYPE_STRING; + nPos = nPos + sStrArray[i].Len(); + i++; + break; + } + } // of while + nCntPost = nCounter; // decimals (100th seconds) + if (bExp) + nCntExp = 1; // merkt AM/PM + } + break; // of NUMBERFORMAT_DATETIME + default: + break; + } + if (eScannedType == NUMBERFORMAT_SCIENTIFIC && + (nCntPre + nCntPost == 0 || nCntExp == 0)) + return nPos; + else if (eScannedType == NUMBERFORMAT_FRACTION && (nCntExp > 8 || nCntExp == 0)) + return nPos; + + if (bThaiT && !GetNatNumModifier()) + SetNatNumModifier(1); + + if ( bConvertMode ) + { // strings containing keywords of the target locale must be quoted, so + // the user sees the difference and is able to edit the format string + for ( i=0; i < nAnzStrings; i++ ) + { + if ( nTypeArray[i] == NF_SYMBOLTYPE_STRING && + sStrArray[i].GetChar(0) != '\"' ) + { + if ( bConvertSystemToSystem && eScannedType == NUMBERFORMAT_CURRENCY ) + { // don't stringize automatic currency, will be converted + if ( sStrArray[i] == sOldCurSymbol ) + continue; // for + // DM might be splitted into D and M + if ( sStrArray[i].Len() < sOldCurSymbol.Len() && + pChrCls->toUpper( sStrArray[i], 0, 1 ).GetChar(0) == + sOldCurString.GetChar(0) ) + { + String aTmp( sStrArray[i] ); + USHORT j = i + 1; + while ( aTmp.Len() < sOldCurSymbol.Len() && + j < nAnzStrings && + nTypeArray[j] == NF_SYMBOLTYPE_STRING ) + { + aTmp += sStrArray[j++]; + } + if ( pChrCls->upper( aTmp ) == sOldCurString ) + { + sStrArray[i++] = aTmp; + for ( ; i<j; i++ ) + { + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + i = j - 1; + continue; // for + } + } + } + String& rStr = sStrArray[i]; + xub_StrLen nLen = rStr.Len(); + for ( xub_StrLen j=0; j<nLen; j++ ) + { + if ( (j == 0 || rStr.GetChar(j-1) != '\\') && GetKeyWord( rStr, j ) ) + { + rStr.Insert( '\"', 0 ); + rStr += '\"'; + break; // for + } + } + } + } + } + // concatenate strings, remove quotes for output, and rebuild the format string + rString.Erase(); + i = 0; + while (i < nAnzStrings) + { + switch ( nTypeArray[i] ) + { + case NF_SYMBOLTYPE_STRING : + { + xub_StrLen nStringPos = rString.Len(); + xub_StrLen nArrPos = 0; + USHORT iPos = i; + do + { + if (sStrArray[i].Len() == 2 && + sStrArray[i].GetChar(0) == '\\') + { + // Unescape some simple forms of symbols even in the UI + // visible string to prevent duplicates that differ + // only in notation, originating from import. + // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical, + // but 0\ 000 0 and 0 000 0 in a French locale are not. + sal_Unicode c = sStrArray[i].GetChar(1); + switch (c) + { + case '+': + case '-': + rString += c; + break; + case ' ': + case '.': + case '/': + if (((eScannedType & NUMBERFORMAT_DATE) == 0) + && (StringEqualsChar( + pFormatter->GetNumThousandSep(), + c) || StringEqualsChar( + pFormatter->GetNumDecimalSep(), + c) || (c == ' ' && + StringEqualsChar( + pFormatter->GetNumThousandSep(), + cNonBreakingSpace)))) + rString += sStrArray[i]; + else if ((eScannedType & NUMBERFORMAT_DATE) && + StringEqualsChar( + pFormatter->GetDateSep(), c)) + rString += sStrArray[i]; + else if ((eScannedType & NUMBERFORMAT_TIME) && + (StringEqualsChar( pLoc->getTimeSep(), + c) || + StringEqualsChar( + pLoc->getTime100SecSep(), c))) + rString += sStrArray[i]; + else if (eScannedType & NUMBERFORMAT_FRACTION) + rString += sStrArray[i]; + else + rString += c; + break; + default: + rString += sStrArray[i]; + } + } + else + rString += sStrArray[i]; + if ( RemoveQuotes( sStrArray[i] ) > 0 ) + { // update currency up to quoted string + if ( eScannedType == NUMBERFORMAT_CURRENCY ) + { // dM -> DM or DM -> $ in old automatic + // currency formats, oh my ..., why did we ever + // introduce them? + String aTmp( pChrCls->toUpper( + sStrArray[iPos], nArrPos, + sStrArray[iPos].Len()-nArrPos ) ); + xub_StrLen nCPos = aTmp.Search( sOldCurString ); + if ( nCPos != STRING_NOTFOUND ) + { + const String& rCur = + bConvertMode && bConvertSystemToSystem ? + GetCurSymbol() : sOldCurSymbol; + sStrArray[iPos].Replace( nArrPos+nCPos, + sOldCurString.Len(), rCur ); + rString.Replace( nStringPos+nCPos, + sOldCurString.Len(), rCur ); + } + nStringPos = rString.Len(); + if ( iPos == i ) + nArrPos = sStrArray[iPos].Len(); + else + nArrPos = sStrArray[iPos].Len() + sStrArray[i].Len(); + } + } + if ( iPos != i ) + { + sStrArray[iPos] += sStrArray[i]; + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + i++; + } while ( i < nAnzStrings && nTypeArray[i] == NF_SYMBOLTYPE_STRING ); + if ( i < nAnzStrings ) + i--; // enter switch on next symbol again + if ( eScannedType == NUMBERFORMAT_CURRENCY && nStringPos < rString.Len() ) + { // same as above, since last RemoveQuotes + String aTmp( pChrCls->toUpper( + sStrArray[iPos], nArrPos, + sStrArray[iPos].Len()-nArrPos ) ); + xub_StrLen nCPos = aTmp.Search( sOldCurString ); + if ( nCPos != STRING_NOTFOUND ) + { + const String& rCur = + bConvertMode && bConvertSystemToSystem ? + GetCurSymbol() : sOldCurSymbol; + sStrArray[iPos].Replace( nArrPos+nCPos, + sOldCurString.Len(), rCur ); + rString.Replace( nStringPos+nCPos, + sOldCurString.Len(), rCur ); + } + } + } + break; + case NF_SYMBOLTYPE_CURRENCY : + { + rString += sStrArray[i]; + RemoveQuotes( sStrArray[i] ); + } + break; + case NF_KEY_THAI_T: + if (bThaiT && GetNatNumModifier() == 1) + { // Remove T from format code, will be replaced with a [NatNum1] prefix. + nTypeArray[i] = NF_SYMBOLTYPE_EMPTY; + nAnzResStrings--; + } + else + rString += sStrArray[i]; + break; + case NF_SYMBOLTYPE_EMPTY : + // nothing + break; + default: + rString += sStrArray[i]; + } + i++; + } + return 0; +} + + +xub_StrLen ImpSvNumberformatScan::RemoveQuotes( String& rStr ) +{ + if ( rStr.Len() > 1 ) + { + sal_Unicode c = rStr.GetChar(0); + xub_StrLen n; + if ( c == '"' && rStr.GetChar( (n = xub_StrLen(rStr.Len()-1)) ) == '"' ) + { + rStr.Erase(n,1); + rStr.Erase(0,1); + return 2; + } + else if ( c == '\\' ) + { + rStr.Erase(0,1); + return 1; + } + } + return 0; +} + + +xub_StrLen ImpSvNumberformatScan::ScanFormat( String& rString, String& rComment ) +{ + xub_StrLen res = Symbol_Division(rString); //lexikalische Analyse + if (!res) + res = ScanType(rString); // Erkennung des Formattyps + if (!res) + res = FinalScan( rString, rComment ); // Typabhaengige Endanalyse + return res; // res = Kontrollposition + // res = 0 => Format ok +} + +void ImpSvNumberformatScan::CopyInfo(ImpSvNumberformatInfo* pInfo, USHORT nAnz) +{ + size_t i,j; + j = 0; + i = 0; + while (i < nAnz && j < NF_MAX_FORMAT_SYMBOLS) + { + if (nTypeArray[j] != NF_SYMBOLTYPE_EMPTY) + { + pInfo->sStrArray[i] = sStrArray[j]; + pInfo->nTypeArray[i] = nTypeArray[j]; + i++; + } + j++; + } + pInfo->eScannedType = eScannedType; + pInfo->bThousand = bThousand; + pInfo->nThousand = nThousand; + pInfo->nCntPre = nCntPre; + pInfo->nCntPost = nCntPost; + pInfo->nCntExp = nCntExp; +} + + diff --git a/svl/source/numbers/zforscan.hxx b/svl/source/numbers/zforscan.hxx new file mode 100644 index 000000000000..300715dfeaa5 --- /dev/null +++ b/svl/source/numbers/zforscan.hxx @@ -0,0 +1,278 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zforscan.hxx,v $ + * $Revision: 1.24.136.1 $ + * + * 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. + * + ************************************************************************/ +#ifndef _ZFORSCAN_HXX +#define _ZFORSCAN_HXX + +#include <tools/string.hxx> +#include <tools/date.hxx> +#include <i18npool/lang.h> +#include <tools/color.hxx> +#include <svl/nfkeytab.hxx> +#include "nfsymbol.hxx" + +class SvNumberFormatter; +struct ImpSvNumberformatInfo; + + +const size_t NF_MAX_FORMAT_SYMBOLS = 100; +const size_t NF_MAX_DEFAULT_COLORS = 10; + +// Hack: nThousand==1000 => "Default" occurs in format string +const USHORT FLAG_STANDARD_IN_FORMAT = 1000; + +class ImpSvNumberformatScan +{ +public: + + ImpSvNumberformatScan( SvNumberFormatter* pFormatter ); + ~ImpSvNumberformatScan(); + void ChangeIntl(); // tauscht Keywords aus + + void ChangeNullDate(USHORT nDay, USHORT nMonth, USHORT nYear); + // tauscht Referenzdatum aus + void ChangeStandardPrec(short nPrec); // tauscht Standardprecision aus + + xub_StrLen ScanFormat( String& rString, String& rComment ); // Aufruf der Scan-Analyse + + void CopyInfo(ImpSvNumberformatInfo* pInfo, + USHORT nAnz); // Kopiert die FormatInfo + USHORT GetAnzResStrings() const { return nAnzResStrings; } + + const CharClass& GetChrCls() const { return *pFormatter->GetCharClass(); } + const LocaleDataWrapper& GetLoc() const { return *pFormatter->GetLocaleData(); } + CalendarWrapper& GetCal() const { return *pFormatter->GetCalendar(); } + + const String* GetKeywords() const + { + if ( bKeywordsNeedInit ) + InitKeywords(); + return sKeyword; + } + // Keywords used in output like TRUE and FALSE + const String& GetSpecialKeyword( NfKeywordIndex eIdx ) const + { + if ( !sKeyword[eIdx].Len() ) + InitSpecialKeyword( eIdx ); + return sKeyword[eIdx]; + } + const String& GetTrueString() const { return GetSpecialKeyword( NF_KEY_TRUE ); } + const String& GetFalseString() const { return GetSpecialKeyword( NF_KEY_FALSE ); } + const String& GetColorString() const { return GetKeywords()[NF_KEY_COLOR]; } + const String& GetRedString() const { return GetKeywords()[NF_KEY_RED]; } + const String& GetBooleanString() const { return GetKeywords()[NF_KEY_BOOLEAN]; } + const String& GetErrorString() const { return sErrStr; } + + Date* GetNullDate() const { return pNullDate; } + const String& GetStandardName() const + { + if ( bKeywordsNeedInit ) + InitKeywords(); + return sNameStandardFormat; + } + short GetStandardPrec() const { return nStandardPrec; } + const Color& GetRedColor() const { return StandardColor[4]; } + Color* GetColor(String& sStr); // Setzt Hauptfarben oder + // definierte Farben + + // the compatibility currency symbol for old automatic currency formats + const String& GetCurSymbol() const + { + if ( bCompatCurNeedInit ) + InitCompatCur(); + return sCurSymbol; + } + + // the compatibility currency abbreviation for CCC format code + const String& GetCurAbbrev() const + { + if ( bCompatCurNeedInit ) + InitCompatCur(); + return sCurAbbrev; + } + + // the compatibility currency symbol upper case for old automatic currency formats + const String& GetCurString() const + { + if ( bCompatCurNeedInit ) + InitCompatCur(); + return sCurString; + } + + void SetConvertMode(LanguageType eTmpLge, LanguageType eNewLge, + BOOL bSystemToSystem = FALSE ) + { + bConvertMode = TRUE; + eNewLnge = eNewLge; + eTmpLnge = eTmpLge; + bConvertSystemToSystem = bSystemToSystem; + } + void SetConvertMode(BOOL bMode) { bConvertMode = bMode; } + // Veraendert nur die Bool-Variable + // (zum temporaeren Unterbrechen des + // Convert-Modus) + BOOL GetConvertMode() const { return bConvertMode; } + LanguageType GetNewLnge() const { return eNewLnge; } + // Lesezugriff auf ConvertMode + // und Konvertierungsland/Spr. + LanguageType GetTmpLnge() const { return eTmpLnge; } + // Lesezugriff auf + // und Ausgangsland/Spr. + + /// get Thai T speciality + BYTE GetNatNumModifier() const { return nNatNumModifier; } + /// set Thai T speciality + void SetNatNumModifier( BYTE n ) { nNatNumModifier = n; } + + SvNumberFormatter* GetNumberformatter() { return pFormatter; } + // Zugriff auf Formatierer + // (fuer zformat.cxx) + + +private: // ---- privater Teil + NfKeywordTable sKeyword; // Schluesselworte der Syntax + Color StandardColor[NF_MAX_DEFAULT_COLORS]; + // Array der Standardfarben + Date* pNullDate; // 30Dec1899 + String sNameStandardFormat; // "Standard" + short nStandardPrec; // default Precision fuer Standardformat (2) + SvNumberFormatter* pFormatter; // Pointer auf die Formatliste + + String sStrArray[NF_MAX_FORMAT_SYMBOLS]; // Array der Symbole + short nTypeArray[NF_MAX_FORMAT_SYMBOLS]; // Array der Infos + // externe Infos: + USHORT nAnzResStrings; // Anzahl der Ergebnissymbole +#if !(defined SOLARIS && defined X86) + short eScannedType; // Typ gemaess Scan +#else + int eScannedType; // wg. Optimierung +#endif + BOOL bThousand; // Mit Tausenderpunkt + USHORT nThousand; // Zaehlt ....-Folgen + USHORT nCntPre; // Zaehlt Vorkommastellen + USHORT nCntPost; // Zaehlt Nachkommastellen + USHORT nCntExp; // Zaehlt Exp.Stellen, AM/PM + // interne Infos: + USHORT nAnzStrings; // Anzahl der Symbole + USHORT nRepPos; // Position eines '*' + USHORT nExpPos; // interne Position des E + USHORT nBlankPos; // interne Position des Blank + short nDecPos; // interne Pos. des , + BOOL bExp; // wird bei Lesen des E gesetzt + BOOL bFrac; // wird bei Lesen des / gesetzt + BOOL bBlank; // wird bei ' '(Fraction) ges. + BOOL bDecSep; // Wird beim ersten , gesetzt + mutable BOOL bKeywordsNeedInit; // Locale dependent keywords need to be initialized + mutable BOOL bCompatCurNeedInit; // Locale dependent compatibility currency need to be initialized + String sCurSymbol; // Currency symbol for compatibility format codes + String sCurString; // Currency symbol in upper case + String sCurAbbrev; // Currency abbreviation + String sErrStr; // String fuer Fehlerausgaben + + BOOL bConvertMode; // Wird im Convert-Mode gesetzt + // Land/Sprache, in die der + LanguageType eNewLnge; // gescannte String konvertiert + // wird (fuer Excel Filter) + // Land/Sprache, aus der der + LanguageType eTmpLnge; // gescannte String konvertiert + // wird (fuer Excel Filter) + BOOL bConvertSystemToSystem; // Whether the conversion is + // from one system locale to + // another system locale (in + // this case the automatic + // currency symbol is converted + // too). + + xub_StrLen nCurrPos; // Position des Waehrungssymbols + + BYTE nNatNumModifier; // Thai T speciality + + void InitKeywords() const; + void InitSpecialKeyword( NfKeywordIndex eIdx ) const; + void InitCompatCur() const; + +#ifdef _ZFORSCAN_CXX // ----- private Methoden ----- + void SetDependentKeywords(); + // Setzt die Sprachabh. Keyw. + void SkipStrings(USHORT& i,xub_StrLen& nPos);// Ueberspringt StringSymbole + USHORT PreviousKeyword(USHORT i); // Gibt Index des vorangeh. + // Schluesselworts oder 0 + USHORT NextKeyword(USHORT i); // Gibt Index des naechsten + // Schluesselworts oder 0 + sal_Unicode PreviousChar(USHORT i); // Gibt letzten Buchstaben + // vor der Position, + // skipt EMPTY, STRING, STAR, BLANK + sal_Unicode NextChar(USHORT i); // Gibt ersten Buchst. danach + short PreviousType( USHORT i ); // Gibt Typ vor Position, + // skipt EMPTY + BOOL IsLastBlankBeforeFrac(USHORT i); // True <=> es kommt kein ' ' + // mehr bis zum '/' + void Reset(); // Reset aller Variablen + // vor Analysestart + short GetKeyWord( const String& sSymbol, // determine keyword at nPos + xub_StrLen nPos ); // return 0 <=> not found + + inline BOOL IsAmbiguousE( short nKey ) // whether nKey is ambiguous E of NF_KEY_E/NF_KEY_EC + { + return (nKey == NF_KEY_EC || nKey == NF_KEY_E) && + (GetKeywords()[NF_KEY_EC] == GetKeywords()[NF_KEY_E]); + } + + // if 0 at strArray[i] is of S,00 or SS,00 or SS"any"00 in ScanType() or FinalScan() + BOOL Is100SecZero( USHORT i, BOOL bHadDecSep ); + + short Next_Symbol(const String& rStr, + xub_StrLen& nPos, + String& sSymbol); // Naechstes Symbol + xub_StrLen Symbol_Division(const String& rString);// lexikalische Voranalyse + xub_StrLen ScanType(const String& rString); // Analyse des Formattyps + xub_StrLen FinalScan( String& rString, String& rComment ); // Endanalyse mit Vorgabe + // des Typs + // -1:= error, return nPos in FinalScan; 0:= no calendar, 1:= calendar found + int FinalScanGetCalendar( xub_StrLen& nPos, USHORT& i, USHORT& nAnzResStrings ); + + /** Insert symbol into nTypeArray and sStrArray, e.g. grouping separator. + If at nPos-1 a symbol type NF_SYMBOLTYPE_EMPTY is present, that is + reused instead of shifting all one up and nPos is decremented! */ + bool InsertSymbol( USHORT & nPos, svt::NfSymbolType eType, const String& rStr ); + + static inline BOOL StringEqualsChar( const String& rStr, sal_Unicode ch ) + { return rStr.GetChar(0) == ch && rStr.Len() == 1; } + // Yes, for efficiency get the character first and then compare length + // because in most places where this is used the string is one char. + + // remove "..." and \... quotes from rStr, return how many chars removed + static xub_StrLen RemoveQuotes( String& rStr ); + +#endif //_ZFORSCAN_CXX +}; + + + +#endif // _ZFORSCAN_HXX |