| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_forms.hxx" |
| |
| #include "convert.hxx" |
| |
| #include "unohelper.hxx" |
| #include <memory> |
| #include <algorithm> |
| #include <functional> |
| #include <rtl/math.hxx> |
| #include <rtl/ustrbuf.hxx> |
| #include <tools/date.hxx> |
| #include <com/sun/star/uno/Type.hxx> |
| #include <com/sun/star/xsd/WhiteSpaceTreatment.hpp> |
| #include <com/sun/star/util/Date.hpp> |
| #include <com/sun/star/util/DateTime.hpp> |
| #include <com/sun/star/util/Time.hpp> |
| |
| using xforms::Convert; |
| using ::rtl::OUString; |
| using ::rtl::OUStringBuffer; |
| using com::sun::star::uno::Any; |
| using com::sun::star::uno::makeAny; |
| using com::sun::star::util::Time; |
| using namespace std; |
| |
| typedef com::sun::star::util::Date UNODate; |
| typedef com::sun::star::util::Time UNOTime; |
| typedef com::sun::star::util::DateTime UNODateTime; |
| |
| Convert::Convert() |
| : maMap() |
| { |
| init(); |
| } |
| |
| #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE ) |
| |
| namespace |
| { |
| // ======================================================================== |
| struct StringToken |
| { |
| private: |
| ::rtl::OUString m_sString; |
| sal_Int32 m_nTokenStart; |
| sal_Int32 m_nTokenEnd; |
| |
| public: |
| StringToken() : m_sString(), m_nTokenStart( 0 ), m_nTokenEnd( 0 ) { } |
| StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd ); |
| StringToken( const StringToken& ); |
| StringToken& operator=( const StringToken& ); |
| |
| inline bool isEmpty() const { return m_nTokenEnd <= m_nTokenStart; } |
| inline sal_Int32 getLength() const { return isEmpty() ? 0 : m_nTokenEnd - m_nTokenStart - 1; } |
| inline const sal_Unicode* begin() const { return m_sString.getStr() + m_nTokenStart; } |
| inline const sal_Unicode* end() const { return m_sString.getStr() + m_nTokenEnd; } |
| |
| bool toInt32( sal_Int32& _rValue ) const; |
| }; |
| |
| // ------------------------------------------------------------------------ |
| StringToken::StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd ) |
| :m_sString( _rString ) |
| ,m_nTokenStart( _nTokenStart ) |
| ,m_nTokenEnd( _nTokenEnd ) |
| { |
| OSL_ENSURE( ( m_nTokenStart >= 0 ) && ( m_nTokenStart <= m_sString.getLength() ), "StringToken::StringToken: invalid token start!" ); |
| OSL_ENSURE( ( m_nTokenEnd >= 0 ) && ( m_nTokenEnd <= m_sString.getLength() ), "StringToken::StringToken: invalid token end!" ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| StringToken::StringToken( const StringToken& _rRHS ) |
| { |
| *this = _rRHS; |
| } |
| |
| // ------------------------------------------------------------------------ |
| StringToken& StringToken::operator=( const StringToken& _rRHS ) |
| { |
| if ( this == &_rRHS ) |
| return *this; |
| |
| m_sString = _rRHS.m_sString; |
| m_nTokenStart = _rRHS.m_nTokenStart; |
| m_nTokenEnd = _rRHS.m_nTokenEnd; |
| |
| return *this; |
| } |
| |
| // ------------------------------------------------------------------------ |
| bool StringToken::toInt32( sal_Int32& _rValue ) const |
| { |
| if ( isEmpty() ) |
| return false; |
| |
| _rValue = 0; |
| const sal_Unicode* pStr = begin(); |
| while ( pStr < end() ) |
| { |
| if ( ( *pStr < '0' ) || ( *pStr > '9' ) ) |
| return false; |
| |
| _rValue *= 10; |
| _rValue += ( *pStr - '0' ); |
| |
| ++pStr; |
| } |
| |
| return true; |
| } |
| |
| // ======================================================================== |
| class StringTokenizer |
| { |
| private: |
| ::rtl::OUString m_sString; |
| const sal_Unicode m_nTokenSeparator; |
| sal_Int32 m_nTokenStart; |
| |
| public: |
| /** constructs a tokenizer |
| @param _rString the string to tokenize |
| @param _nTokenSeparator the token value. May be 0, in this case the tokenizer |
| will recognize exactly one token, being the whole string. |
| This may make sense if you want to apply <type>StringToken</type> |
| methods to a whole string. |
| */ |
| StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator = ';' ); |
| |
| /// resets the tokenizer to the beginning of the string |
| void reset(); |
| |
| /// determines whether there is a next token |
| bool hasNextToken() const; |
| |
| /// retrieves the next token |
| StringToken |
| getNextToken(); |
| }; |
| |
| // ------------------------------------------------------------------------ |
| StringTokenizer::StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator ) |
| :m_sString( _rString ) |
| ,m_nTokenSeparator( _nTokenSeparator ) |
| { |
| reset(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| void StringTokenizer::reset() |
| { |
| m_nTokenStart = 0; |
| } |
| |
| // ------------------------------------------------------------------------ |
| bool StringTokenizer::hasNextToken() const |
| { |
| return ( m_nTokenStart < m_sString.getLength() ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| StringToken StringTokenizer::getNextToken() |
| { |
| OSL_PRECOND( hasNextToken(), "StringTokenizer::getNextToken: there is no next token!" ); |
| if ( !hasNextToken() ) |
| return StringToken(); |
| |
| // determine the end of the current token |
| sal_Int32 nTokenEnd = m_nTokenSeparator ? m_sString.indexOf( m_nTokenSeparator, m_nTokenStart ) : m_sString.getLength(); |
| bool bLastToken = !m_nTokenSeparator || ( nTokenEnd == -1 ); |
| |
| // construct a new token |
| StringToken aToken( m_sString, m_nTokenStart, bLastToken ? m_sString.getLength() : nTokenEnd ); |
| // advance |
| m_nTokenStart = bLastToken ? m_sString.getLength() : nTokenEnd + 1; |
| // outta here |
| return aToken; |
| } |
| |
| // ======================================================================== |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_OUString( const Any& rAny ) |
| { OUString sStr; rAny >>= sStr; return sStr; } |
| |
| // ------------------------------------------------------------------------ |
| Any lcl_toAny_OUString( const OUString& rStr ) |
| { Any aAny; aAny <<= rStr; return aAny; } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_bool( const Any& rAny ) |
| { bool b = false; rAny >>= b; return b ? OUSTRING("true") : OUSTRING("false"); } |
| |
| // ------------------------------------------------------------------------ |
| Any lcl_toAny_bool( const OUString& rStr ) |
| { |
| bool b = ( rStr == OUSTRING("true") || rStr == OUSTRING("1") ); |
| return makeAny( b ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_double( const Any& rAny ) |
| { |
| double f = 0.0; |
| rAny >>= f; |
| |
| return rtl::math::isFinite( f ) |
| ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic, |
| rtl_math_DecimalPlaces_Max, '.', |
| sal_True ) |
| : OUString(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| Any lcl_toAny_double( const OUString& rString ) |
| { |
| rtl_math_ConversionStatus eStatus; |
| double f = rtl::math::stringToDouble( |
| rString, sal_Unicode('.'), sal_Unicode(','), &eStatus, NULL ); |
| return ( eStatus == rtl_math_ConversionStatus_Ok ) ? makeAny( f ) : Any(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, ::rtl::OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits ) |
| { |
| if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) ) |
| _rBuffer.append( (sal_Unicode)'0' ); |
| if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) ) |
| _rBuffer.append( (sal_Unicode)'0' ); |
| if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) ) |
| _rBuffer.append( (sal_Unicode)'0' ); |
| _rBuffer.append( _nValue ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_UNODate_typed( const UNODate& rDate ) |
| { |
| |
| ::rtl::OUStringBuffer sInfo; |
| lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 ); |
| sInfo.appendAscii( "-" ); |
| lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 ); |
| sInfo.appendAscii( "-" ); |
| lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 ); |
| |
| return sInfo.makeStringAndClear(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_UNODate( const Any& rAny ) |
| { |
| UNODate aDate; |
| OSL_VERIFY( rAny >>= aDate ); |
| return lcl_toXSD_UNODate_typed( aDate ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| UNODate lcl_toUNODate( const OUString& rString ) |
| { |
| bool bWellformed = true; |
| |
| UNODate aDate( 1, 1, 1900 ); |
| |
| sal_Int32 nToken = 0; |
| StringTokenizer aTokenizer( rString, '-' ); |
| while ( aTokenizer.hasNextToken() ) |
| { |
| sal_Int32 nTokenValue = 0; |
| if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) ) |
| { |
| bWellformed = false; |
| break; |
| } |
| |
| if ( nToken == 0 ) |
| aDate.Year = (sal_uInt16)nTokenValue; |
| else if ( nToken == 1 ) |
| aDate.Month = (sal_uInt16)nTokenValue; |
| else if ( nToken == 2 ) |
| aDate.Day = (sal_uInt16)nTokenValue; |
| else |
| { |
| bWellformed = false; |
| break; |
| } |
| ++nToken; |
| } |
| |
| // sanity checks |
| if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) ) |
| bWellformed = false; |
| else |
| { |
| ::Date aDateCheck( 1, aDate.Month, aDate.Year ); |
| if ( aDate.Day > aDateCheck.GetDaysInMonth() ) |
| bWellformed = false; |
| } |
| |
| // all okay? |
| if ( !bWellformed ) |
| return UNODate( 1, 1, 1900 ); |
| |
| return aDate; |
| } |
| |
| // ------------------------------------------------------------------------ |
| Any lcl_toAny_UNODate( const OUString& rString ) |
| { |
| return makeAny( lcl_toUNODate( rString ) ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_UNOTime_typed( const UNOTime& rTime ) |
| { |
| |
| ::rtl::OUStringBuffer sInfo; |
| lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 ); |
| sInfo.appendAscii( ":" ); |
| lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 ); |
| sInfo.appendAscii( ":" ); |
| lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 ); |
| if ( rTime.HundredthSeconds ) |
| { |
| sInfo.appendAscii( "." ); |
| lcl_appendInt32ToBuffer( rTime.HundredthSeconds, sInfo, 2 ); |
| } |
| |
| return sInfo.makeStringAndClear(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_UNOTime( const Any& rAny ) |
| { |
| UNOTime aTime; |
| OSL_VERIFY( rAny >>= aTime ); |
| return lcl_toXSD_UNOTime_typed( aTime ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| UNOTime lcl_toUNOTime( const OUString& rString ) |
| { |
| bool bWellformed = true; |
| |
| UNOTime aTime( 0, 0, 0, 0 ); |
| |
| ::rtl::OUString sString( rString ); |
| // see if there's a decimal separator for the seconds, |
| // and if so, handle it separately |
| sal_Int32 nDecimalSepPos = rString.indexOf( '.' ); |
| if ( nDecimalSepPos == -1 ) |
| // ISO 8601 allows for both a comma and a dot |
| nDecimalSepPos = rString.indexOf( ',' ); |
| if ( nDecimalSepPos != -1 ) |
| { |
| // handle fractional seconds |
| ::rtl::OUString sFractional = sString.copy( nDecimalSepPos + 1 ); |
| if ( sFractional.getLength() > 2 ) |
| // our precision is HundrethSeconds - it's all a css.util.Time can hold |
| sFractional = sFractional.copy( 0, 2 ); |
| sal_Int32 nFractional = 0; |
| if ( sFractional.getLength() ) |
| { |
| if ( StringTokenizer( sFractional, 0 ).getNextToken().toInt32( nFractional ) ) |
| { |
| aTime.HundredthSeconds = (sal_uInt16)nFractional; |
| if ( nFractional < 10 ) |
| aTime.HundredthSeconds *= 10; |
| } |
| else |
| bWellformed = false; |
| } |
| |
| // strip the fraction before further processing |
| sString = sString.copy( 0, nDecimalSepPos ); |
| } |
| |
| // split into the tokens which are separated by colon |
| sal_Int32 nToken = 0; |
| StringTokenizer aTokenizer( sString, ':' ); |
| while ( aTokenizer.hasNextToken() ) |
| { |
| sal_Int32 nTokenValue = 0; |
| if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) ) |
| { |
| bWellformed = false; |
| break; |
| } |
| |
| if ( nToken == 0 ) |
| aTime.Hours = (sal_uInt16)nTokenValue; |
| else if ( nToken == 1 ) |
| aTime.Minutes = (sal_uInt16)nTokenValue; |
| else if ( nToken == 2 ) |
| aTime.Seconds = (sal_uInt16)nTokenValue; |
| else |
| { |
| bWellformed = false; |
| break; |
| } |
| ++nToken; |
| } |
| |
| // sanity checks |
| // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere, |
| // but we accept them all the time for simplicity reasons |
| if ( ( aTime.Hours > 24 ) |
| || ( aTime.Minutes > 59 ) |
| || ( aTime.Seconds > 60 ) |
| ) |
| bWellformed = false; |
| |
| if ( bWellformed |
| && ( aTime.Hours == 24 ) |
| && ( ( aTime.Minutes != 0 ) |
| || ( aTime.Seconds != 0 ) |
| || ( aTime.HundredthSeconds != 0 ) |
| ) |
| ) |
| bWellformed = false; |
| |
| // all okay? |
| if ( !bWellformed ) |
| return UNOTime( 0, 0, 0, 0 ); |
| |
| return aTime; |
| } |
| |
| // ------------------------------------------------------------------------ |
| Any lcl_toAny_UNOTime( const OUString& rString ) |
| { |
| return makeAny( lcl_toUNOTime( rString ) ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| OUString lcl_toXSD_UNODateTime( const Any& rAny ) |
| { |
| UNODateTime aDateTime; |
| OSL_VERIFY( rAny >>= aDateTime ); |
| |
| UNODate aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year ); |
| ::rtl::OUString sDate = lcl_toXSD_UNODate_typed( aDate ); |
| |
| UNOTime aTime( aDateTime.HundredthSeconds, aDateTime.Seconds, aDateTime.Minutes, aDateTime.Hours ); |
| ::rtl::OUString sTime = lcl_toXSD_UNOTime_typed( aTime ); |
| |
| ::rtl::OUStringBuffer sInfo; |
| sInfo.append( sDate ); |
| sInfo.append( (sal_Unicode) 'T' ); |
| sInfo.append( sTime ); |
| return sInfo.makeStringAndClear(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| Any lcl_toAny_UNODateTime( const OUString& rString ) |
| { |
| // separate the date from the time part |
| sal_Int32 nDateTimeSep = rString.indexOf( 'T' ); |
| if ( nDateTimeSep == -1 ) |
| nDateTimeSep = rString.indexOf( 't' ); |
| |
| UNODate aDate; |
| UNOTime aTime; |
| if ( nDateTimeSep == -1 ) |
| { // no time part |
| aDate = lcl_toUNODate( rString ); |
| aTime = UNOTime( 0, 0, 0, 0 ); |
| } |
| else |
| { |
| aDate = lcl_toUNODate( rString.copy( 0, nDateTimeSep ) ); |
| aTime = lcl_toUNOTime( rString.copy( nDateTimeSep + 1 ) ); |
| } |
| UNODateTime aDateTime( |
| aTime.HundredthSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours, |
| aDate.Day, aDate.Month, aDate.Year |
| ); |
| return makeAny( aDateTime ); |
| } |
| } |
| |
| // ============================================================================ |
| void Convert::init() |
| { |
| ADD_ENTRY( this, OUString ); |
| ADD_ENTRY( this, bool ); |
| ADD_ENTRY( this, double ); |
| ADD_ENTRY( this, UNODate ); |
| ADD_ENTRY( this, UNOTime ); |
| ADD_ENTRY( this, UNODateTime ); |
| } |
| |
| |
| Convert& Convert::get() |
| { |
| // create our Singleton instance on demand |
| static Convert* pConvert = NULL; |
| if( pConvert == NULL ) |
| pConvert = new Convert(); |
| |
| OSL_ENSURE( pConvert != NULL, "no converter?" ); |
| return *pConvert; |
| } |
| |
| bool Convert::hasType( const Type_t& rType ) |
| { |
| return maMap.find( rType ) != maMap.end(); |
| } |
| |
| Convert::Types_t Convert::getTypes() |
| { |
| Types_t aTypes( maMap.size() ); |
| transform( maMap.begin(), maMap.end(), aTypes.getArray(), |
| select1st<Map_t::value_type>() ); |
| return aTypes; |
| } |
| |
| rtl::OUString Convert::toXSD( const Any_t& rAny ) |
| { |
| Map_t::iterator aIter = maMap.find( rAny.getValueType() ); |
| return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString(); |
| } |
| |
| Convert::Any_t Convert::toAny( const rtl::OUString& rValue, |
| const Type_t& rType ) |
| { |
| Map_t::iterator aIter = maMap.find( rType ); |
| return aIter != maMap.end() ? aIter->second.second( rValue ) : Any_t(); |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString Convert::convertWhitespace( const ::rtl::OUString& _rString, sal_Int16 _nWhitespaceTreatment ) |
| { |
| ::rtl::OUString sConverted; |
| switch( _nWhitespaceTreatment ) |
| { |
| default: |
| OSL_ENSURE( sal_False, "Convert::convertWhitespace: invalid whitespace treatment constant!" ); |
| // NO break |
| case com::sun::star::xsd::WhiteSpaceTreatment::Preserve: |
| sConverted = _rString; |
| break; |
| case com::sun::star::xsd::WhiteSpaceTreatment::Replace: |
| sConverted = replaceWhitespace( _rString ); |
| break; |
| case com::sun::star::xsd::WhiteSpaceTreatment::Collapse: |
| sConverted = collapseWhitespace( _rString ); |
| break; |
| } |
| return sConverted; |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString Convert::replaceWhitespace( const ::rtl::OUString& _rString ) |
| { |
| OUStringBuffer aBuffer( _rString ); |
| sal_Int32 nLength = aBuffer.getLength(); |
| const sal_Unicode* pBuffer = aBuffer.getStr(); |
| for( sal_Int32 i = 0; i < nLength; i++ ) |
| { |
| sal_Unicode c = pBuffer[i]; |
| if( c == sal_Unicode(0x08) || |
| c == sal_Unicode(0x0A) || |
| c == sal_Unicode(0x0D) ) |
| aBuffer.setCharAt( i, sal_Unicode(0x20) ); |
| } |
| return aBuffer.makeStringAndClear(); |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString Convert::collapseWhitespace( const ::rtl::OUString& _rString ) |
| { |
| sal_Int32 nLength = _rString.getLength(); |
| OUStringBuffer aBuffer( nLength ); |
| const sal_Unicode* pStr = _rString.getStr(); |
| bool bStrip = true; |
| for( sal_Int32 i = 0; i < nLength; i++ ) |
| { |
| sal_Unicode c = pStr[i]; |
| if( c == sal_Unicode(0x08) || |
| c == sal_Unicode(0x0A) || |
| c == sal_Unicode(0x0D) || |
| c == sal_Unicode(0x20) ) |
| { |
| if( ! bStrip ) |
| { |
| aBuffer.append( sal_Unicode(0x20) ); |
| bStrip = true; |
| } |
| } |
| else |
| { |
| bStrip = false; |
| aBuffer.append( c ); |
| } |
| } |
| if( aBuffer[ aBuffer.getLength() - 1 ] == sal_Unicode( 0x20 ) ) |
| aBuffer.setLength( aBuffer.getLength() - 1 ); |
| return aBuffer.makeStringAndClear(); |
| } |