masterfix DEV300: #i10000# build fix
[LibreOffice.git] / forms / source / xforms / convert.cxx
blob7ea6e35a52844953d2e8c04b38dd5b80e0026373
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_forms.hxx"
31 #include "convert.hxx"
33 #include "unohelper.hxx"
34 #include <memory>
35 #include <algorithm>
36 #include <functional>
37 #include <rtl/math.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <tools/date.hxx>
40 #include <com/sun/star/uno/Type.hxx>
41 #include <com/sun/star/xsd/WhiteSpaceTreatment.hpp>
42 #include <com/sun/star/util/Date.hpp>
43 #include <com/sun/star/util/DateTime.hpp>
44 #include <com/sun/star/util/Time.hpp>
46 using xforms::Convert;
47 using ::rtl::OUString;
48 using ::rtl::OUStringBuffer;
49 using com::sun::star::uno::Any;
50 using com::sun::star::uno::makeAny;
51 using com::sun::star::util::Time;
52 using namespace std;
54 typedef com::sun::star::util::Date UNODate;
55 typedef com::sun::star::util::Time UNOTime;
56 typedef com::sun::star::util::DateTime UNODateTime;
58 Convert::Convert()
59 : maMap()
61 init();
64 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE )
66 namespace
68 // ========================================================================
69 struct StringToken
71 private:
72 ::rtl::OUString m_sString;
73 sal_Int32 m_nTokenStart;
74 sal_Int32 m_nTokenEnd;
76 public:
77 StringToken() : m_sString(), m_nTokenStart( 0 ), m_nTokenEnd( 0 ) { }
78 StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd );
79 StringToken( const StringToken& );
80 StringToken& operator=( const StringToken& );
82 inline bool isEmpty() const { return m_nTokenEnd <= m_nTokenStart; }
83 inline sal_Int32 getLength() const { return isEmpty() ? 0 : m_nTokenEnd - m_nTokenStart - 1; }
84 inline const sal_Unicode* begin() const { return m_sString.getStr() + m_nTokenStart; }
85 inline const sal_Unicode* end() const { return m_sString.getStr() + m_nTokenEnd; }
87 bool toInt32( sal_Int32& _rValue ) const;
90 // ------------------------------------------------------------------------
91 StringToken::StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd )
92 :m_sString( _rString )
93 ,m_nTokenStart( _nTokenStart )
94 ,m_nTokenEnd( _nTokenEnd )
96 OSL_ENSURE( ( m_nTokenStart >= 0 ) && ( m_nTokenStart <= m_sString.getLength() ), "StringToken::StringToken: invalid token start!" );
97 OSL_ENSURE( ( m_nTokenEnd >= 0 ) && ( m_nTokenEnd <= m_sString.getLength() ), "StringToken::StringToken: invalid token end!" );
100 // ------------------------------------------------------------------------
101 StringToken::StringToken( const StringToken& _rRHS )
103 *this = _rRHS;
106 // ------------------------------------------------------------------------
107 StringToken& StringToken::operator=( const StringToken& _rRHS )
109 if ( this == &_rRHS )
110 return *this;
112 m_sString = _rRHS.m_sString;
113 m_nTokenStart = _rRHS.m_nTokenStart;
114 m_nTokenEnd = _rRHS.m_nTokenEnd;
116 return *this;
119 // ------------------------------------------------------------------------
120 bool StringToken::toInt32( sal_Int32& _rValue ) const
122 if ( isEmpty() )
123 return false;
125 _rValue = 0;
126 const sal_Unicode* pStr = begin();
127 while ( pStr < end() )
129 if ( ( *pStr < '0' ) || ( *pStr > '9' ) )
130 return false;
132 _rValue *= 10;
133 _rValue += ( *pStr - '0' );
135 ++pStr;
138 return true;
141 // ========================================================================
142 class StringTokenizer
144 private:
145 ::rtl::OUString m_sString;
146 const sal_Unicode m_nTokenSeparator;
147 sal_Int32 m_nTokenStart;
149 public:
150 /** constructs a tokenizer
151 @param _rString the string to tokenize
152 @param _nTokenSeparator the token value. May be 0, in this case the tokenizer
153 will recognize exactly one token, being the whole string.
154 This may make sense if you want to apply <type>StringToken</type>
155 methods to a whole string.
157 StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator = ';' );
159 /// resets the tokenizer to the beginning of the string
160 void reset();
162 /// determines whether there is a next token
163 bool hasNextToken() const;
165 /// retrieves the next token
166 StringToken
167 getNextToken();
170 // ------------------------------------------------------------------------
171 StringTokenizer::StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator )
172 :m_sString( _rString )
173 ,m_nTokenSeparator( _nTokenSeparator )
175 reset();
178 // ------------------------------------------------------------------------
179 void StringTokenizer::reset()
181 m_nTokenStart = 0;
184 // ------------------------------------------------------------------------
185 bool StringTokenizer::hasNextToken() const
187 return ( m_nTokenStart < m_sString.getLength() );
190 // ------------------------------------------------------------------------
191 StringToken StringTokenizer::getNextToken()
193 OSL_PRECOND( hasNextToken(), "StringTokenizer::getNextToken: there is no next token!" );
194 if ( !hasNextToken() )
195 return StringToken();
197 // determine the end of the current token
198 sal_Int32 nTokenEnd = m_nTokenSeparator ? m_sString.indexOf( m_nTokenSeparator, m_nTokenStart ) : m_sString.getLength();
199 bool bLastToken = !m_nTokenSeparator || ( nTokenEnd == -1 );
201 // construct a new token
202 StringToken aToken( m_sString, m_nTokenStart, bLastToken ? m_sString.getLength() : nTokenEnd );
203 // advance
204 m_nTokenStart = bLastToken ? m_sString.getLength() : nTokenEnd + 1;
205 // outta here
206 return aToken;
209 // ========================================================================
210 // ------------------------------------------------------------------------
211 OUString lcl_toXSD_OUString( const Any& rAny )
212 { OUString sStr; rAny >>= sStr; return sStr; }
214 // ------------------------------------------------------------------------
215 Any lcl_toAny_OUString( const OUString& rStr )
216 { Any aAny; aAny <<= rStr; return aAny; }
218 // ------------------------------------------------------------------------
219 OUString lcl_toXSD_bool( const Any& rAny )
220 { bool b = false; rAny >>= b; return b ? OUSTRING("true") : OUSTRING("false"); }
222 // ------------------------------------------------------------------------
223 Any lcl_toAny_bool( const OUString& rStr )
225 bool b = ( rStr == OUSTRING("true") || rStr == OUSTRING("1") );
226 return makeAny( b );
229 // ------------------------------------------------------------------------
230 OUString lcl_toXSD_double( const Any& rAny )
232 double f = 0.0;
233 rAny >>= f;
235 return rtl::math::isFinite( f )
236 ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic,
237 rtl_math_DecimalPlaces_Max, '.',
238 sal_True )
239 : OUString();
242 // ------------------------------------------------------------------------
243 Any lcl_toAny_double( const OUString& rString )
245 rtl_math_ConversionStatus eStatus;
246 double f = rtl::math::stringToDouble(
247 rString, sal_Unicode('.'), sal_Unicode(','), &eStatus, NULL );
248 return ( eStatus == rtl_math_ConversionStatus_Ok ) ? makeAny( f ) : Any();
251 // ------------------------------------------------------------------------
252 void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, ::rtl::OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits )
254 if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) )
255 _rBuffer.append( (sal_Unicode)'0' );
256 if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) )
257 _rBuffer.append( (sal_Unicode)'0' );
258 if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) )
259 _rBuffer.append( (sal_Unicode)'0' );
260 _rBuffer.append( _nValue );
263 // ------------------------------------------------------------------------
264 OUString lcl_toXSD_UNODate_typed( const UNODate& rDate )
267 ::rtl::OUStringBuffer sInfo;
268 lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 );
269 sInfo.appendAscii( "-" );
270 lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 );
271 sInfo.appendAscii( "-" );
272 lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 );
274 return sInfo.makeStringAndClear();
277 // ------------------------------------------------------------------------
278 OUString lcl_toXSD_UNODate( const Any& rAny )
280 UNODate aDate;
281 OSL_VERIFY( rAny >>= aDate );
282 return lcl_toXSD_UNODate_typed( aDate );
285 // ------------------------------------------------------------------------
286 UNODate lcl_toUNODate( const OUString& rString )
288 bool bWellformed = true;
290 UNODate aDate( 1, 1, 1900 );
292 sal_Int32 nToken = 0;
293 StringTokenizer aTokenizer( rString, '-' );
294 while ( aTokenizer.hasNextToken() )
296 sal_Int32 nTokenValue = 0;
297 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) )
299 bWellformed = false;
300 break;
303 if ( nToken == 0 )
304 aDate.Year = (sal_uInt16)nTokenValue;
305 else if ( nToken == 1 )
306 aDate.Month = (sal_uInt16)nTokenValue;
307 else if ( nToken == 2 )
308 aDate.Day = (sal_uInt16)nTokenValue;
309 else
311 bWellformed = false;
312 break;
314 ++nToken;
317 // sanity checks
318 if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) )
319 bWellformed = false;
320 else
322 ::Date aDateCheck( 1, aDate.Month, aDate.Year );
323 if ( aDate.Day > aDateCheck.GetDaysInMonth() )
324 bWellformed = false;
327 // all okay?
328 if ( !bWellformed )
329 return UNODate( 1, 1, 1900 );
331 return aDate;
334 // ------------------------------------------------------------------------
335 Any lcl_toAny_UNODate( const OUString& rString )
337 return makeAny( lcl_toUNODate( rString ) );
340 // ------------------------------------------------------------------------
341 OUString lcl_toXSD_UNOTime_typed( const UNOTime& rTime )
344 ::rtl::OUStringBuffer sInfo;
345 lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 );
346 sInfo.appendAscii( ":" );
347 lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 );
348 sInfo.appendAscii( ":" );
349 lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 );
350 if ( rTime.HundredthSeconds )
352 sInfo.appendAscii( "." );
353 lcl_appendInt32ToBuffer( rTime.HundredthSeconds, sInfo, 2 );
356 return sInfo.makeStringAndClear();
359 // ------------------------------------------------------------------------
360 OUString lcl_toXSD_UNOTime( const Any& rAny )
362 UNOTime aTime;
363 OSL_VERIFY( rAny >>= aTime );
364 return lcl_toXSD_UNOTime_typed( aTime );
367 // ------------------------------------------------------------------------
368 UNOTime lcl_toUNOTime( const OUString& rString )
370 bool bWellformed = true;
372 UNOTime aTime( 0, 0, 0, 0 );
374 ::rtl::OUString sString( rString );
375 // see if there's a decimal separator for the seconds,
376 // and if so, handle it separately
377 sal_Int32 nDecimalSepPos = rString.indexOf( '.' );
378 if ( nDecimalSepPos == -1 )
379 // ISO 8601 allows for both a comma and a dot
380 nDecimalSepPos = rString.indexOf( ',' );
381 if ( nDecimalSepPos != -1 )
383 // handle fractional seconds
384 ::rtl::OUString sFractional = sString.copy( nDecimalSepPos + 1 );
385 if ( sFractional.getLength() > 2 )
386 // our precision is HundrethSeconds - it's all a css.util.Time can hold
387 sFractional = sFractional.copy( 0, 2 );
388 sal_Int32 nFractional = 0;
389 if ( sFractional.getLength() )
391 if ( StringTokenizer( sFractional, 0 ).getNextToken().toInt32( nFractional ) )
393 aTime.HundredthSeconds = (sal_uInt16)nFractional;
394 if ( nFractional < 10 )
395 aTime.HundredthSeconds *= 10;
397 else
398 bWellformed = false;
401 // strip the fraction before further processing
402 sString = sString.copy( 0, nDecimalSepPos );
405 // split into the tokens which are separated by colon
406 sal_Int32 nToken = 0;
407 StringTokenizer aTokenizer( sString, ':' );
408 while ( aTokenizer.hasNextToken() )
410 sal_Int32 nTokenValue = 0;
411 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) )
413 bWellformed = false;
414 break;
417 if ( nToken == 0 )
418 aTime.Hours = (sal_uInt16)nTokenValue;
419 else if ( nToken == 1 )
420 aTime.Minutes = (sal_uInt16)nTokenValue;
421 else if ( nToken == 2 )
422 aTime.Seconds = (sal_uInt16)nTokenValue;
423 else
425 bWellformed = false;
426 break;
428 ++nToken;
431 // sanity checks
432 // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere,
433 // but we accept them all the time for simplicity reasons
434 if ( ( aTime.Hours > 24 )
435 || ( aTime.Minutes > 59 )
436 || ( aTime.Seconds > 60 )
438 bWellformed = false;
440 if ( bWellformed
441 && ( aTime.Hours == 24 )
442 && ( ( aTime.Minutes != 0 )
443 || ( aTime.Seconds != 0 )
444 || ( aTime.HundredthSeconds != 0 )
447 bWellformed = false;
449 // all okay?
450 if ( !bWellformed )
451 return UNOTime( 0, 0, 0, 0 );
453 return aTime;
456 // ------------------------------------------------------------------------
457 Any lcl_toAny_UNOTime( const OUString& rString )
459 return makeAny( lcl_toUNOTime( rString ) );
462 // ------------------------------------------------------------------------
463 OUString lcl_toXSD_UNODateTime( const Any& rAny )
465 UNODateTime aDateTime;
466 OSL_VERIFY( rAny >>= aDateTime );
468 UNODate aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year );
469 ::rtl::OUString sDate = lcl_toXSD_UNODate_typed( aDate );
471 UNOTime aTime( aDateTime.HundredthSeconds, aDateTime.Seconds, aDateTime.Minutes, aDateTime.Hours );
472 ::rtl::OUString sTime = lcl_toXSD_UNOTime_typed( aTime );
474 ::rtl::OUStringBuffer sInfo;
475 sInfo.append( sDate );
476 sInfo.append( (sal_Unicode) 'T' );
477 sInfo.append( sTime );
478 return sInfo.makeStringAndClear();
481 // ------------------------------------------------------------------------
482 Any lcl_toAny_UNODateTime( const OUString& rString )
484 // separate the date from the time part
485 sal_Int32 nDateTimeSep = rString.indexOf( 'T' );
486 if ( nDateTimeSep == -1 )
487 nDateTimeSep = rString.indexOf( 't' );
489 UNODate aDate;
490 UNOTime aTime;
491 if ( nDateTimeSep == -1 )
492 { // no time part
493 aDate = lcl_toUNODate( rString );
494 aTime = UNOTime( 0, 0, 0, 0 );
496 else
498 aDate = lcl_toUNODate( rString.copy( 0, nDateTimeSep ) );
499 aTime = lcl_toUNOTime( rString.copy( nDateTimeSep + 1 ) );
501 UNODateTime aDateTime(
502 aTime.HundredthSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
503 aDate.Day, aDate.Month, aDate.Year
505 return makeAny( aDateTime );
509 // ============================================================================
510 void Convert::init()
512 ADD_ENTRY( this, OUString );
513 ADD_ENTRY( this, bool );
514 ADD_ENTRY( this, double );
515 ADD_ENTRY( this, UNODate );
516 ADD_ENTRY( this, UNOTime );
517 ADD_ENTRY( this, UNODateTime );
521 Convert& Convert::get()
523 // create our Singleton instance on demand
524 static Convert* pConvert = NULL;
525 if( pConvert == NULL )
526 pConvert = new Convert();
528 OSL_ENSURE( pConvert != NULL, "no converter?" );
529 return *pConvert;
532 bool Convert::hasType( const Type_t& rType )
534 return maMap.find( rType ) != maMap.end();
537 Convert::Types_t Convert::getTypes()
539 Types_t aTypes( maMap.size() );
540 transform( maMap.begin(), maMap.end(), aTypes.getArray(),
541 select1st<Map_t::value_type>() );
542 return aTypes;
545 rtl::OUString Convert::toXSD( const Any_t& rAny )
547 Map_t::iterator aIter = maMap.find( rAny.getValueType() );
548 return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString();
551 Convert::Any_t Convert::toAny( const rtl::OUString& rValue,
552 const Type_t& rType )
554 Map_t::iterator aIter = maMap.find( rType );
555 return aIter != maMap.end() ? aIter->second.second( rValue ) : Any_t();
558 //------------------------------------------------------------------------
559 ::rtl::OUString Convert::convertWhitespace( const ::rtl::OUString& _rString, sal_Int16 _nWhitespaceTreatment )
561 ::rtl::OUString sConverted;
562 switch( _nWhitespaceTreatment )
564 default:
565 OSL_ENSURE( sal_False, "Convert::convertWhitespace: invalid whitespace treatment constant!" );
566 // NO break
567 case com::sun::star::xsd::WhiteSpaceTreatment::Preserve:
568 sConverted = _rString;
569 break;
570 case com::sun::star::xsd::WhiteSpaceTreatment::Replace:
571 sConverted = replaceWhitespace( _rString );
572 break;
573 case com::sun::star::xsd::WhiteSpaceTreatment::Collapse:
574 sConverted = collapseWhitespace( _rString );
575 break;
577 return sConverted;
580 //------------------------------------------------------------------------
581 ::rtl::OUString Convert::replaceWhitespace( const ::rtl::OUString& _rString )
583 OUStringBuffer aBuffer( _rString );
584 sal_Int32 nLength = aBuffer.getLength();
585 const sal_Unicode* pBuffer = aBuffer.getStr();
586 for( sal_Int32 i = 0; i < nLength; i++ )
588 sal_Unicode c = pBuffer[i];
589 if( c == sal_Unicode(0x08) ||
590 c == sal_Unicode(0x0A) ||
591 c == sal_Unicode(0x0D) )
592 aBuffer.setCharAt( i, sal_Unicode(0x20) );
594 return aBuffer.makeStringAndClear();
597 //------------------------------------------------------------------------
598 ::rtl::OUString Convert::collapseWhitespace( const ::rtl::OUString& _rString )
600 sal_Int32 nLength = _rString.getLength();
601 OUStringBuffer aBuffer( nLength );
602 const sal_Unicode* pStr = _rString.getStr();
603 bool bStrip = true;
604 for( sal_Int32 i = 0; i < nLength; i++ )
606 sal_Unicode c = pStr[i];
607 if( c == sal_Unicode(0x08) ||
608 c == sal_Unicode(0x0A) ||
609 c == sal_Unicode(0x0D) ||
610 c == sal_Unicode(0x20) )
612 if( ! bStrip )
614 aBuffer.append( sal_Unicode(0x20) );
615 bStrip = true;
618 else
620 bStrip = false;
621 aBuffer.append( c );
624 if( aBuffer[ aBuffer.getLength() - 1 ] == sal_Unicode( 0x20 ) )
625 aBuffer.setLength( aBuffer.getLength() - 1 );
626 return aBuffer.makeStringAndClear();