1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: convert.cxx,v $
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"
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
;
57 typedef com::sun::star::util::Date UNODate
;
58 typedef com::sun::star::util::Time UNOTime
;
59 typedef com::sun::star::util::DateTime UNODateTime
;
67 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE )
71 // ========================================================================
75 ::rtl::OUString m_sString
;
76 sal_Int32 m_nTokenStart
;
77 sal_Int32 m_nTokenEnd
;
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
)
109 // ------------------------------------------------------------------------
110 StringToken
& StringToken::operator=( const StringToken
& _rRHS
)
112 if ( this == &_rRHS
)
115 m_sString
= _rRHS
.m_sString
;
116 m_nTokenStart
= _rRHS
.m_nTokenStart
;
117 m_nTokenEnd
= _rRHS
.m_nTokenEnd
;
122 // ------------------------------------------------------------------------
123 bool StringToken::toInt32( sal_Int32
& _rValue
) const
129 const sal_Unicode
* pStr
= begin();
130 while ( pStr
< end() )
132 if ( ( *pStr
< '0' ) || ( *pStr
> '9' ) )
136 _rValue
+= ( *pStr
- '0' );
144 // ========================================================================
145 class StringTokenizer
148 ::rtl::OUString m_sString
;
149 const sal_Unicode m_nTokenSeparator
;
150 sal_Int32 m_nTokenStart
;
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
165 /// determines whether there is a next token
166 bool hasNextToken() const;
168 /// retrieves the next token
173 // ------------------------------------------------------------------------
174 StringTokenizer::StringTokenizer( const ::rtl::OUString
& _rString
, sal_Unicode _nTokenSeparator
)
175 :m_sString( _rString
)
176 ,m_nTokenSeparator( _nTokenSeparator
)
181 // ------------------------------------------------------------------------
182 void StringTokenizer::reset()
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
);
207 m_nTokenStart
= bLastToken
? m_sString
.getLength() : nTokenEnd
+ 1;
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") );
232 // ------------------------------------------------------------------------
233 OUString
lcl_toXSD_double( const Any
& rAny
)
238 return rtl::math::isFinite( f
)
239 ? rtl::math::doubleToUString( f
, rtl_math_StringFormat_Automatic
,
240 rtl_math_DecimalPlaces_Max
, '.',
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
)
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
) )
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
;
321 if ( ( aDate
.Year
> 9999 ) || ( aDate
.Month
< 1 ) || ( aDate
.Month
> 12 ) || ( aDate
.Day
< 1 ) || ( aDate
.Day
> 31 ) )
325 ::Date
aDateCheck( 1, aDate
.Month
, aDate
.Year
);
326 if ( aDate
.Day
> aDateCheck
.GetDaysInMonth() )
332 return UNODate( 1, 1, 1900 );
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
)
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;
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
) )
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
;
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 )
444 && ( aTime
.Hours
== 24 )
445 && ( ( aTime
.Minutes
!= 0 )
446 || ( aTime
.Seconds
!= 0 )
447 || ( aTime
.HundredthSeconds
!= 0 )
454 return UNOTime( 0, 0, 0, 0 );
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' );
494 if ( nDateTimeSep
== -1 )
496 aDate
= lcl_toUNODate( rString
);
497 aTime
= UNOTime( 0, 0, 0, 0 );
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 // ============================================================================
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?" );
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
>() );
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
)
568 OSL_ENSURE( sal_False
, "Convert::convertWhitespace: invalid whitespace treatment constant!" );
570 case com::sun::star::xsd::WhiteSpaceTreatment::Preserve
:
571 sConverted
= _rString
;
573 case com::sun::star::xsd::WhiteSpaceTreatment::Replace
:
574 sConverted
= replaceWhitespace( _rString
);
576 case com::sun::star::xsd::WhiteSpaceTreatment::Collapse
:
577 sConverted
= collapseWhitespace( _rString
);
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();
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) )
617 aBuffer
.append( sal_Unicode(0x20) );
627 if( aBuffer
[ aBuffer
.getLength() - 1 ] == sal_Unicode( 0x20 ) )
628 aBuffer
.setLength( aBuffer
.getLength() - 1 );
629 return aBuffer
.makeStringAndClear();