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"
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
;
54 typedef com::sun::star::util::Date UNODate
;
55 typedef com::sun::star::util::Time UNOTime
;
56 typedef com::sun::star::util::DateTime UNODateTime
;
64 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE )
68 // ========================================================================
72 ::rtl::OUString m_sString
;
73 sal_Int32 m_nTokenStart
;
74 sal_Int32 m_nTokenEnd
;
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
)
106 // ------------------------------------------------------------------------
107 StringToken
& StringToken::operator=( const StringToken
& _rRHS
)
109 if ( this == &_rRHS
)
112 m_sString
= _rRHS
.m_sString
;
113 m_nTokenStart
= _rRHS
.m_nTokenStart
;
114 m_nTokenEnd
= _rRHS
.m_nTokenEnd
;
119 // ------------------------------------------------------------------------
120 bool StringToken::toInt32( sal_Int32
& _rValue
) const
126 const sal_Unicode
* pStr
= begin();
127 while ( pStr
< end() )
129 if ( ( *pStr
< '0' ) || ( *pStr
> '9' ) )
133 _rValue
+= ( *pStr
- '0' );
141 // ========================================================================
142 class StringTokenizer
145 ::rtl::OUString m_sString
;
146 const sal_Unicode m_nTokenSeparator
;
147 sal_Int32 m_nTokenStart
;
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
162 /// determines whether there is a next token
163 bool hasNextToken() const;
165 /// retrieves the next token
170 // ------------------------------------------------------------------------
171 StringTokenizer::StringTokenizer( const ::rtl::OUString
& _rString
, sal_Unicode _nTokenSeparator
)
172 :m_sString( _rString
)
173 ,m_nTokenSeparator( _nTokenSeparator
)
178 // ------------------------------------------------------------------------
179 void StringTokenizer::reset()
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
);
204 m_nTokenStart
= bLastToken
? m_sString
.getLength() : nTokenEnd
+ 1;
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") );
229 // ------------------------------------------------------------------------
230 OUString
lcl_toXSD_double( const Any
& rAny
)
235 return rtl::math::isFinite( f
)
236 ? rtl::math::doubleToUString( f
, rtl_math_StringFormat_Automatic
,
237 rtl_math_DecimalPlaces_Max
, '.',
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
)
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
) )
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
;
318 if ( ( aDate
.Year
> 9999 ) || ( aDate
.Month
< 1 ) || ( aDate
.Month
> 12 ) || ( aDate
.Day
< 1 ) || ( aDate
.Day
> 31 ) )
322 ::Date
aDateCheck( 1, aDate
.Month
, aDate
.Year
);
323 if ( aDate
.Day
> aDateCheck
.GetDaysInMonth() )
329 return UNODate( 1, 1, 1900 );
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
)
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;
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
) )
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
;
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 )
441 && ( aTime
.Hours
== 24 )
442 && ( ( aTime
.Minutes
!= 0 )
443 || ( aTime
.Seconds
!= 0 )
444 || ( aTime
.HundredthSeconds
!= 0 )
451 return UNOTime( 0, 0, 0, 0 );
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' );
491 if ( nDateTimeSep
== -1 )
493 aDate
= lcl_toUNODate( rString
);
494 aTime
= UNOTime( 0, 0, 0, 0 );
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 // ============================================================================
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?" );
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
>() );
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
)
565 OSL_ENSURE( sal_False
, "Convert::convertWhitespace: invalid whitespace treatment constant!" );
567 case com::sun::star::xsd::WhiteSpaceTreatment::Preserve
:
568 sConverted
= _rString
;
570 case com::sun::star::xsd::WhiteSpaceTreatment::Replace
:
571 sConverted
= replaceWhitespace( _rString
);
573 case com::sun::star::xsd::WhiteSpaceTreatment::Collapse
:
574 sConverted
= collapseWhitespace( _rString
);
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();
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) )
614 aBuffer
.append( sal_Unicode(0x20) );
624 if( aBuffer
[ aBuffer
.getLength() - 1 ] == sal_Unicode( 0x20 ) )
625 aBuffer
.setLength( aBuffer
.getLength() - 1 );
626 return aBuffer
.makeStringAndClear();