update dev300-m58
[ooovba.git] / forms / source / xforms / convert.cxx
blob1ebbafb3a06082fdc19306af82ae6dea2b355437
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: convert.cxx,v $
10 * $Revision: 1.8 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_forms.hxx"
34 #include "convert.hxx"
36 #include "unohelper.hxx"
37 #include <memory>
38 #include <algorithm>
39 #include <functional>
40 #include <rtl/math.hxx>
41 #include <rtl/ustrbuf.hxx>
42 #include <tools/date.hxx>
43 #include <com/sun/star/uno/Type.hxx>
44 #include <com/sun/star/xsd/WhiteSpaceTreatment.hpp>
45 #include <com/sun/star/util/Date.hpp>
46 #include <com/sun/star/util/DateTime.hpp>
47 #include <com/sun/star/util/Time.hpp>
49 using xforms::Convert;
50 using ::rtl::OUString;
51 using ::rtl::OUStringBuffer;
52 using com::sun::star::uno::Any;
53 using com::sun::star::uno::makeAny;
54 using com::sun::star::util::Time;
55 using namespace std;
57 typedef com::sun::star::util::Date UNODate;
58 typedef com::sun::star::util::Time UNOTime;
59 typedef com::sun::star::util::DateTime UNODateTime;
61 Convert::Convert()
62 : maMap()
64 init();
67 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE )
69 namespace
71 // ========================================================================
72 struct StringToken
74 private:
75 ::rtl::OUString m_sString;
76 sal_Int32 m_nTokenStart;
77 sal_Int32 m_nTokenEnd;
79 public:
80 StringToken() : m_sString(), m_nTokenStart( 0 ), m_nTokenEnd( 0 ) { }
81 StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd );
82 StringToken( const StringToken& );
83 StringToken& operator=( const StringToken& );
85 inline bool isEmpty() const { return m_nTokenEnd <= m_nTokenStart; }
86 inline sal_Int32 getLength() const { return isEmpty() ? 0 : m_nTokenEnd - m_nTokenStart - 1; }
87 inline const sal_Unicode* begin() const { return m_sString.getStr() + m_nTokenStart; }
88 inline const sal_Unicode* end() const { return m_sString.getStr() + m_nTokenEnd; }
90 bool toInt32( sal_Int32& _rValue ) const;
93 // ------------------------------------------------------------------------
94 StringToken::StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd )
95 :m_sString( _rString )
96 ,m_nTokenStart( _nTokenStart )
97 ,m_nTokenEnd( _nTokenEnd )
99 OSL_ENSURE( ( m_nTokenStart >= 0 ) && ( m_nTokenStart <= m_sString.getLength() ), "StringToken::StringToken: invalid token start!" );
100 OSL_ENSURE( ( m_nTokenEnd >= 0 ) && ( m_nTokenEnd <= m_sString.getLength() ), "StringToken::StringToken: invalid token end!" );
103 // ------------------------------------------------------------------------
104 StringToken::StringToken( const StringToken& _rRHS )
106 *this = _rRHS;
109 // ------------------------------------------------------------------------
110 StringToken& StringToken::operator=( const StringToken& _rRHS )
112 if ( this == &_rRHS )
113 return *this;
115 m_sString = _rRHS.m_sString;
116 m_nTokenStart = _rRHS.m_nTokenStart;
117 m_nTokenEnd = _rRHS.m_nTokenEnd;
119 return *this;
122 // ------------------------------------------------------------------------
123 bool StringToken::toInt32( sal_Int32& _rValue ) const
125 if ( isEmpty() )
126 return false;
128 _rValue = 0;
129 const sal_Unicode* pStr = begin();
130 while ( pStr < end() )
132 if ( ( *pStr < '0' ) || ( *pStr > '9' ) )
133 return false;
135 _rValue *= 10;
136 _rValue += ( *pStr - '0' );
138 ++pStr;
141 return true;
144 // ========================================================================
145 class StringTokenizer
147 private:
148 ::rtl::OUString m_sString;
149 const sal_Unicode m_nTokenSeparator;
150 sal_Int32 m_nTokenStart;
152 public:
153 /** constructs a tokenizer
154 @param _rString the string to tokenize
155 @param _nTokenSeparator the token value. May be 0, in this case the tokenizer
156 will recognize exactly one token, being the whole string.
157 This may make sense if you want to apply <type>StringToken</type>
158 methods to a whole string.
160 StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator = ';' );
162 /// resets the tokenizer to the beginning of the string
163 void reset();
165 /// determines whether there is a next token
166 bool hasNextToken() const;
168 /// retrieves the next token
169 StringToken
170 getNextToken();
173 // ------------------------------------------------------------------------
174 StringTokenizer::StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator )
175 :m_sString( _rString )
176 ,m_nTokenSeparator( _nTokenSeparator )
178 reset();
181 // ------------------------------------------------------------------------
182 void StringTokenizer::reset()
184 m_nTokenStart = 0;
187 // ------------------------------------------------------------------------
188 bool StringTokenizer::hasNextToken() const
190 return ( m_nTokenStart < m_sString.getLength() );
193 // ------------------------------------------------------------------------
194 StringToken StringTokenizer::getNextToken()
196 OSL_PRECOND( hasNextToken(), "StringTokenizer::getNextToken: there is no next token!" );
197 if ( !hasNextToken() )
198 return StringToken();
200 // determine the end of the current token
201 sal_Int32 nTokenEnd = m_nTokenSeparator ? m_sString.indexOf( m_nTokenSeparator, m_nTokenStart ) : m_sString.getLength();
202 bool bLastToken = !m_nTokenSeparator || ( nTokenEnd == -1 );
204 // construct a new token
205 StringToken aToken( m_sString, m_nTokenStart, bLastToken ? m_sString.getLength() : nTokenEnd );
206 // advance
207 m_nTokenStart = bLastToken ? m_sString.getLength() : nTokenEnd + 1;
208 // outta here
209 return aToken;
212 // ========================================================================
213 // ------------------------------------------------------------------------
214 OUString lcl_toXSD_OUString( const Any& rAny )
215 { OUString sStr; rAny >>= sStr; return sStr; }
217 // ------------------------------------------------------------------------
218 Any lcl_toAny_OUString( const OUString& rStr )
219 { Any aAny; aAny <<= rStr; return aAny; }
221 // ------------------------------------------------------------------------
222 OUString lcl_toXSD_bool( const Any& rAny )
223 { bool b = false; rAny >>= b; return b ? OUSTRING("true") : OUSTRING("false"); }
225 // ------------------------------------------------------------------------
226 Any lcl_toAny_bool( const OUString& rStr )
228 bool b = ( rStr == OUSTRING("true") || rStr == OUSTRING("1") );
229 return makeAny( b );
232 // ------------------------------------------------------------------------
233 OUString lcl_toXSD_double( const Any& rAny )
235 double f = 0.0;
236 rAny >>= f;
238 return rtl::math::isFinite( f )
239 ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic,
240 rtl_math_DecimalPlaces_Max, '.',
241 sal_True )
242 : OUString();
245 // ------------------------------------------------------------------------
246 Any lcl_toAny_double( const OUString& rString )
248 rtl_math_ConversionStatus eStatus;
249 double f = rtl::math::stringToDouble(
250 rString, sal_Unicode('.'), sal_Unicode(','), &eStatus, NULL );
251 return ( eStatus == rtl_math_ConversionStatus_Ok ) ? makeAny( f ) : Any();
254 // ------------------------------------------------------------------------
255 void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, ::rtl::OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits )
257 if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) )
258 _rBuffer.append( (sal_Unicode)'0' );
259 if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) )
260 _rBuffer.append( (sal_Unicode)'0' );
261 if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) )
262 _rBuffer.append( (sal_Unicode)'0' );
263 _rBuffer.append( _nValue );
266 // ------------------------------------------------------------------------
267 OUString lcl_toXSD_UNODate_typed( const UNODate& rDate )
270 ::rtl::OUStringBuffer sInfo;
271 lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 );
272 sInfo.appendAscii( "-" );
273 lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 );
274 sInfo.appendAscii( "-" );
275 lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 );
277 return sInfo.makeStringAndClear();
280 // ------------------------------------------------------------------------
281 OUString lcl_toXSD_UNODate( const Any& rAny )
283 UNODate aDate;
284 OSL_VERIFY( rAny >>= aDate );
285 return lcl_toXSD_UNODate_typed( aDate );
288 // ------------------------------------------------------------------------
289 UNODate lcl_toUNODate( const OUString& rString )
291 bool bWellformed = true;
293 UNODate aDate( 1, 1, 1900 );
295 sal_Int32 nToken = 0;
296 StringTokenizer aTokenizer( rString, '-' );
297 while ( aTokenizer.hasNextToken() )
299 sal_Int32 nTokenValue = 0;
300 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) )
302 bWellformed = false;
303 break;
306 if ( nToken == 0 )
307 aDate.Year = (sal_uInt16)nTokenValue;
308 else if ( nToken == 1 )
309 aDate.Month = (sal_uInt16)nTokenValue;
310 else if ( nToken == 2 )
311 aDate.Day = (sal_uInt16)nTokenValue;
312 else
314 bWellformed = false;
315 break;
317 ++nToken;
320 // sanity checks
321 if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) )
322 bWellformed = false;
323 else
325 ::Date aDateCheck( 1, aDate.Month, aDate.Year );
326 if ( aDate.Day > aDateCheck.GetDaysInMonth() )
327 bWellformed = false;
330 // all okay?
331 if ( !bWellformed )
332 return UNODate( 1, 1, 1900 );
334 return aDate;
337 // ------------------------------------------------------------------------
338 Any lcl_toAny_UNODate( const OUString& rString )
340 return makeAny( lcl_toUNODate( rString ) );
343 // ------------------------------------------------------------------------
344 OUString lcl_toXSD_UNOTime_typed( const UNOTime& rTime )
347 ::rtl::OUStringBuffer sInfo;
348 lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 );
349 sInfo.appendAscii( ":" );
350 lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 );
351 sInfo.appendAscii( ":" );
352 lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 );
353 if ( rTime.HundredthSeconds )
355 sInfo.appendAscii( "." );
356 lcl_appendInt32ToBuffer( rTime.HundredthSeconds, sInfo, 2 );
359 return sInfo.makeStringAndClear();
362 // ------------------------------------------------------------------------
363 OUString lcl_toXSD_UNOTime( const Any& rAny )
365 UNOTime aTime;
366 OSL_VERIFY( rAny >>= aTime );
367 return lcl_toXSD_UNOTime_typed( aTime );
370 // ------------------------------------------------------------------------
371 UNOTime lcl_toUNOTime( const OUString& rString )
373 bool bWellformed = true;
375 UNOTime aTime( 0, 0, 0, 0 );
377 ::rtl::OUString sString( rString );
378 // see if there's a decimal separator for the seconds,
379 // and if so, handle it separately
380 sal_Int32 nDecimalSepPos = rString.indexOf( '.' );
381 if ( nDecimalSepPos == -1 )
382 // ISO 8601 allows for both a comma and a dot
383 nDecimalSepPos = rString.indexOf( ',' );
384 if ( nDecimalSepPos != -1 )
386 // handle fractional seconds
387 ::rtl::OUString sFractional = sString.copy( nDecimalSepPos + 1 );
388 if ( sFractional.getLength() > 2 )
389 // our precision is HundrethSeconds - it's all a css.util.Time can hold
390 sFractional = sFractional.copy( 0, 2 );
391 sal_Int32 nFractional = 0;
392 if ( sFractional.getLength() )
394 if ( StringTokenizer( sFractional, 0 ).getNextToken().toInt32( nFractional ) )
396 aTime.HundredthSeconds = (sal_uInt16)nFractional;
397 if ( nFractional < 10 )
398 aTime.HundredthSeconds *= 10;
400 else
401 bWellformed = false;
404 // strip the fraction before further processing
405 sString = sString.copy( 0, nDecimalSepPos );
408 // split into the tokens which are separated by colon
409 sal_Int32 nToken = 0;
410 StringTokenizer aTokenizer( sString, ':' );
411 while ( aTokenizer.hasNextToken() )
413 sal_Int32 nTokenValue = 0;
414 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) )
416 bWellformed = false;
417 break;
420 if ( nToken == 0 )
421 aTime.Hours = (sal_uInt16)nTokenValue;
422 else if ( nToken == 1 )
423 aTime.Minutes = (sal_uInt16)nTokenValue;
424 else if ( nToken == 2 )
425 aTime.Seconds = (sal_uInt16)nTokenValue;
426 else
428 bWellformed = false;
429 break;
431 ++nToken;
434 // sanity checks
435 // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere,
436 // but we accept them all the time for simplicity reasons
437 if ( ( aTime.Hours > 24 )
438 || ( aTime.Minutes > 59 )
439 || ( aTime.Seconds > 60 )
441 bWellformed = false;
443 if ( bWellformed
444 && ( aTime.Hours == 24 )
445 && ( ( aTime.Minutes != 0 )
446 || ( aTime.Seconds != 0 )
447 || ( aTime.HundredthSeconds != 0 )
450 bWellformed = false;
452 // all okay?
453 if ( !bWellformed )
454 return UNOTime( 0, 0, 0, 0 );
456 return aTime;
459 // ------------------------------------------------------------------------
460 Any lcl_toAny_UNOTime( const OUString& rString )
462 return makeAny( lcl_toUNOTime( rString ) );
465 // ------------------------------------------------------------------------
466 OUString lcl_toXSD_UNODateTime( const Any& rAny )
468 UNODateTime aDateTime;
469 OSL_VERIFY( rAny >>= aDateTime );
471 UNODate aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year );
472 ::rtl::OUString sDate = lcl_toXSD_UNODate_typed( aDate );
474 UNOTime aTime( aDateTime.HundredthSeconds, aDateTime.Seconds, aDateTime.Minutes, aDateTime.Hours );
475 ::rtl::OUString sTime = lcl_toXSD_UNOTime_typed( aTime );
477 ::rtl::OUStringBuffer sInfo;
478 sInfo.append( sDate );
479 sInfo.append( (sal_Unicode) 'T' );
480 sInfo.append( sTime );
481 return sInfo.makeStringAndClear();
484 // ------------------------------------------------------------------------
485 Any lcl_toAny_UNODateTime( const OUString& rString )
487 // separate the date from the time part
488 sal_Int32 nDateTimeSep = rString.indexOf( 'T' );
489 if ( nDateTimeSep == -1 )
490 nDateTimeSep = rString.indexOf( 't' );
492 UNODate aDate;
493 UNOTime aTime;
494 if ( nDateTimeSep == -1 )
495 { // no time part
496 aDate = lcl_toUNODate( rString );
497 aTime = UNOTime( 0, 0, 0, 0 );
499 else
501 aDate = lcl_toUNODate( rString.copy( 0, nDateTimeSep ) );
502 aTime = lcl_toUNOTime( rString.copy( nDateTimeSep + 1 ) );
504 UNODateTime aDateTime(
505 aTime.HundredthSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
506 aDate.Day, aDate.Month, aDate.Year
508 return makeAny( aDateTime );
512 // ============================================================================
513 void Convert::init()
515 ADD_ENTRY( this, OUString );
516 ADD_ENTRY( this, bool );
517 ADD_ENTRY( this, double );
518 ADD_ENTRY( this, UNODate );
519 ADD_ENTRY( this, UNOTime );
520 ADD_ENTRY( this, UNODateTime );
524 Convert& Convert::get()
526 // create our Singleton instance on demand
527 static Convert* pConvert = NULL;
528 if( pConvert == NULL )
529 pConvert = new Convert();
531 OSL_ENSURE( pConvert != NULL, "no converter?" );
532 return *pConvert;
535 bool Convert::hasType( const Type_t& rType )
537 return maMap.find( rType ) != maMap.end();
540 Convert::Types_t Convert::getTypes()
542 Types_t aTypes( maMap.size() );
543 transform( maMap.begin(), maMap.end(), aTypes.getArray(),
544 select1st<Map_t::value_type>() );
545 return aTypes;
548 rtl::OUString Convert::toXSD( const Any_t& rAny )
550 Map_t::iterator aIter = maMap.find( rAny.getValueType() );
551 return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString();
554 Convert::Any_t Convert::toAny( const rtl::OUString& rValue,
555 const Type_t& rType )
557 Map_t::iterator aIter = maMap.find( rType );
558 return aIter != maMap.end() ? aIter->second.second( rValue ) : Any_t();
561 //------------------------------------------------------------------------
562 ::rtl::OUString Convert::convertWhitespace( const ::rtl::OUString& _rString, sal_Int16 _nWhitespaceTreatment )
564 ::rtl::OUString sConverted;
565 switch( _nWhitespaceTreatment )
567 default:
568 OSL_ENSURE( sal_False, "Convert::convertWhitespace: invalid whitespace treatment constant!" );
569 // NO break
570 case com::sun::star::xsd::WhiteSpaceTreatment::Preserve:
571 sConverted = _rString;
572 break;
573 case com::sun::star::xsd::WhiteSpaceTreatment::Replace:
574 sConverted = replaceWhitespace( _rString );
575 break;
576 case com::sun::star::xsd::WhiteSpaceTreatment::Collapse:
577 sConverted = collapseWhitespace( _rString );
578 break;
580 return sConverted;
583 //------------------------------------------------------------------------
584 ::rtl::OUString Convert::replaceWhitespace( const ::rtl::OUString& _rString )
586 OUStringBuffer aBuffer( _rString );
587 sal_Int32 nLength = aBuffer.getLength();
588 const sal_Unicode* pBuffer = aBuffer.getStr();
589 for( sal_Int32 i = 0; i < nLength; i++ )
591 sal_Unicode c = pBuffer[i];
592 if( c == sal_Unicode(0x08) ||
593 c == sal_Unicode(0x0A) ||
594 c == sal_Unicode(0x0D) )
595 aBuffer.setCharAt( i, sal_Unicode(0x20) );
597 return aBuffer.makeStringAndClear();
600 //------------------------------------------------------------------------
601 ::rtl::OUString Convert::collapseWhitespace( const ::rtl::OUString& _rString )
603 sal_Int32 nLength = _rString.getLength();
604 OUStringBuffer aBuffer( nLength );
605 const sal_Unicode* pStr = _rString.getStr();
606 bool bStrip = true;
607 for( sal_Int32 i = 0; i < nLength; i++ )
609 sal_Unicode c = pStr[i];
610 if( c == sal_Unicode(0x08) ||
611 c == sal_Unicode(0x0A) ||
612 c == sal_Unicode(0x0D) ||
613 c == sal_Unicode(0x20) )
615 if( ! bStrip )
617 aBuffer.append( sal_Unicode(0x20) );
618 bStrip = true;
621 else
623 bStrip = false;
624 aBuffer.append( c );
627 if( aBuffer[ aBuffer.getLength() - 1 ] == sal_Unicode( 0x20 ) )
628 aBuffer.setLength( aBuffer.getLength() - 1 );
629 return aBuffer.makeStringAndClear();