1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 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. 14 * 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). 20 * 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. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_forms.hxx" 30 31 #include "convert.hxx" 32 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> 45 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; 53 54 typedef com::sun::star::util::Date UNODate; 55 typedef com::sun::star::util::Time UNOTime; 56 typedef com::sun::star::util::DateTime UNODateTime; 57 58 Convert::Convert() 59 : maMap() 60 { 61 init(); 62 } 63 64 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE ) 65 66 namespace 67 { 68 // ======================================================================== 69 struct StringToken 70 { 71 private: 72 ::rtl::OUString m_sString; 73 sal_Int32 m_nTokenStart; 74 sal_Int32 m_nTokenEnd; 75 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& ); 81 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; } 86 87 bool toInt32( sal_Int32& _rValue ) const; 88 }; 89 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 ) 95 { 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!" ); 98 } 99 100 // ------------------------------------------------------------------------ 101 StringToken::StringToken( const StringToken& _rRHS ) 102 { 103 *this = _rRHS; 104 } 105 106 // ------------------------------------------------------------------------ 107 StringToken& StringToken::operator=( const StringToken& _rRHS ) 108 { 109 if ( this == &_rRHS ) 110 return *this; 111 112 m_sString = _rRHS.m_sString; 113 m_nTokenStart = _rRHS.m_nTokenStart; 114 m_nTokenEnd = _rRHS.m_nTokenEnd; 115 116 return *this; 117 } 118 119 // ------------------------------------------------------------------------ 120 bool StringToken::toInt32( sal_Int32& _rValue ) const 121 { 122 if ( isEmpty() ) 123 return false; 124 125 _rValue = 0; 126 const sal_Unicode* pStr = begin(); 127 while ( pStr < end() ) 128 { 129 if ( ( *pStr < '0' ) || ( *pStr > '9' ) ) 130 return false; 131 132 _rValue *= 10; 133 _rValue += ( *pStr - '0' ); 134 135 ++pStr; 136 } 137 138 return true; 139 } 140 141 // ======================================================================== 142 class StringTokenizer 143 { 144 private: 145 ::rtl::OUString m_sString; 146 const sal_Unicode m_nTokenSeparator; 147 sal_Int32 m_nTokenStart; 148 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. 156 */ 157 StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator = ';' ); 158 159 /// resets the tokenizer to the beginning of the string 160 void reset(); 161 162 /// determines whether there is a next token 163 bool hasNextToken() const; 164 165 /// retrieves the next token 166 StringToken 167 getNextToken(); 168 }; 169 170 // ------------------------------------------------------------------------ 171 StringTokenizer::StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator ) 172 :m_sString( _rString ) 173 ,m_nTokenSeparator( _nTokenSeparator ) 174 { 175 reset(); 176 } 177 178 // ------------------------------------------------------------------------ 179 void StringTokenizer::reset() 180 { 181 m_nTokenStart = 0; 182 } 183 184 // ------------------------------------------------------------------------ 185 bool StringTokenizer::hasNextToken() const 186 { 187 return ( m_nTokenStart < m_sString.getLength() ); 188 } 189 190 // ------------------------------------------------------------------------ 191 StringToken StringTokenizer::getNextToken() 192 { 193 OSL_PRECOND( hasNextToken(), "StringTokenizer::getNextToken: there is no next token!" ); 194 if ( !hasNextToken() ) 195 return StringToken(); 196 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 ); 200 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; 207 } 208 209 // ======================================================================== 210 // ------------------------------------------------------------------------ 211 OUString lcl_toXSD_OUString( const Any& rAny ) 212 { OUString sStr; rAny >>= sStr; return sStr; } 213 214 // ------------------------------------------------------------------------ 215 Any lcl_toAny_OUString( const OUString& rStr ) 216 { Any aAny; aAny <<= rStr; return aAny; } 217 218 // ------------------------------------------------------------------------ 219 OUString lcl_toXSD_bool( const Any& rAny ) 220 { bool b = false; rAny >>= b; return b ? OUSTRING("true") : OUSTRING("false"); } 221 222 // ------------------------------------------------------------------------ 223 Any lcl_toAny_bool( const OUString& rStr ) 224 { 225 bool b = ( rStr == OUSTRING("true") || rStr == OUSTRING("1") ); 226 return makeAny( b ); 227 } 228 229 // ------------------------------------------------------------------------ 230 OUString lcl_toXSD_double( const Any& rAny ) 231 { 232 double f = 0.0; 233 rAny >>= f; 234 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(); 240 } 241 242 // ------------------------------------------------------------------------ 243 Any lcl_toAny_double( const OUString& rString ) 244 { 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(); 249 } 250 251 // ------------------------------------------------------------------------ 252 void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, ::rtl::OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits ) 253 { 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 ); 261 } 262 263 // ------------------------------------------------------------------------ 264 OUString lcl_toXSD_UNODate_typed( const UNODate& rDate ) 265 { 266 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 ); 273 274 return sInfo.makeStringAndClear(); 275 } 276 277 // ------------------------------------------------------------------------ 278 OUString lcl_toXSD_UNODate( const Any& rAny ) 279 { 280 UNODate aDate; 281 OSL_VERIFY( rAny >>= aDate ); 282 return lcl_toXSD_UNODate_typed( aDate ); 283 } 284 285 // ------------------------------------------------------------------------ 286 UNODate lcl_toUNODate( const OUString& rString ) 287 { 288 bool bWellformed = true; 289 290 UNODate aDate( 1, 1, 1900 ); 291 292 sal_Int32 nToken = 0; 293 StringTokenizer aTokenizer( rString, '-' ); 294 while ( aTokenizer.hasNextToken() ) 295 { 296 sal_Int32 nTokenValue = 0; 297 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) ) 298 { 299 bWellformed = false; 300 break; 301 } 302 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 310 { 311 bWellformed = false; 312 break; 313 } 314 ++nToken; 315 } 316 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 321 { 322 ::Date aDateCheck( 1, aDate.Month, aDate.Year ); 323 if ( aDate.Day > aDateCheck.GetDaysInMonth() ) 324 bWellformed = false; 325 } 326 327 // all okay? 328 if ( !bWellformed ) 329 return UNODate( 1, 1, 1900 ); 330 331 return aDate; 332 } 333 334 // ------------------------------------------------------------------------ 335 Any lcl_toAny_UNODate( const OUString& rString ) 336 { 337 return makeAny( lcl_toUNODate( rString ) ); 338 } 339 340 // ------------------------------------------------------------------------ 341 OUString lcl_toXSD_UNOTime_typed( const UNOTime& rTime ) 342 { 343 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 ) 351 { 352 sInfo.appendAscii( "." ); 353 lcl_appendInt32ToBuffer( rTime.HundredthSeconds, sInfo, 2 ); 354 } 355 356 return sInfo.makeStringAndClear(); 357 } 358 359 // ------------------------------------------------------------------------ 360 OUString lcl_toXSD_UNOTime( const Any& rAny ) 361 { 362 UNOTime aTime; 363 OSL_VERIFY( rAny >>= aTime ); 364 return lcl_toXSD_UNOTime_typed( aTime ); 365 } 366 367 // ------------------------------------------------------------------------ 368 UNOTime lcl_toUNOTime( const OUString& rString ) 369 { 370 bool bWellformed = true; 371 372 UNOTime aTime( 0, 0, 0, 0 ); 373 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 ) 382 { 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() ) 390 { 391 if ( StringTokenizer( sFractional, 0 ).getNextToken().toInt32( nFractional ) ) 392 { 393 aTime.HundredthSeconds = (sal_uInt16)nFractional; 394 if ( nFractional < 10 ) 395 aTime.HundredthSeconds *= 10; 396 } 397 else 398 bWellformed = false; 399 } 400 401 // strip the fraction before further processing 402 sString = sString.copy( 0, nDecimalSepPos ); 403 } 404 405 // split into the tokens which are separated by colon 406 sal_Int32 nToken = 0; 407 StringTokenizer aTokenizer( sString, ':' ); 408 while ( aTokenizer.hasNextToken() ) 409 { 410 sal_Int32 nTokenValue = 0; 411 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) ) 412 { 413 bWellformed = false; 414 break; 415 } 416 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 424 { 425 bWellformed = false; 426 break; 427 } 428 ++nToken; 429 } 430 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 ) 437 ) 438 bWellformed = false; 439 440 if ( bWellformed 441 && ( aTime.Hours == 24 ) 442 && ( ( aTime.Minutes != 0 ) 443 || ( aTime.Seconds != 0 ) 444 || ( aTime.HundredthSeconds != 0 ) 445 ) 446 ) 447 bWellformed = false; 448 449 // all okay? 450 if ( !bWellformed ) 451 return UNOTime( 0, 0, 0, 0 ); 452 453 return aTime; 454 } 455 456 // ------------------------------------------------------------------------ 457 Any lcl_toAny_UNOTime( const OUString& rString ) 458 { 459 return makeAny( lcl_toUNOTime( rString ) ); 460 } 461 462 // ------------------------------------------------------------------------ 463 OUString lcl_toXSD_UNODateTime( const Any& rAny ) 464 { 465 UNODateTime aDateTime; 466 OSL_VERIFY( rAny >>= aDateTime ); 467 468 UNODate aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year ); 469 ::rtl::OUString sDate = lcl_toXSD_UNODate_typed( aDate ); 470 471 UNOTime aTime( aDateTime.HundredthSeconds, aDateTime.Seconds, aDateTime.Minutes, aDateTime.Hours ); 472 ::rtl::OUString sTime = lcl_toXSD_UNOTime_typed( aTime ); 473 474 ::rtl::OUStringBuffer sInfo; 475 sInfo.append( sDate ); 476 sInfo.append( (sal_Unicode) 'T' ); 477 sInfo.append( sTime ); 478 return sInfo.makeStringAndClear(); 479 } 480 481 // ------------------------------------------------------------------------ 482 Any lcl_toAny_UNODateTime( const OUString& rString ) 483 { 484 // separate the date from the time part 485 sal_Int32 nDateTimeSep = rString.indexOf( 'T' ); 486 if ( nDateTimeSep == -1 ) 487 nDateTimeSep = rString.indexOf( 't' ); 488 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 ); 495 } 496 else 497 { 498 aDate = lcl_toUNODate( rString.copy( 0, nDateTimeSep ) ); 499 aTime = lcl_toUNOTime( rString.copy( nDateTimeSep + 1 ) ); 500 } 501 UNODateTime aDateTime( 502 aTime.HundredthSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours, 503 aDate.Day, aDate.Month, aDate.Year 504 ); 505 return makeAny( aDateTime ); 506 } 507 } 508 509 // ============================================================================ 510 void Convert::init() 511 { 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 ); 518 } 519 520 521 Convert& Convert::get() 522 { 523 // create our Singleton instance on demand 524 static Convert* pConvert = NULL; 525 if( pConvert == NULL ) 526 pConvert = new Convert(); 527 528 OSL_ENSURE( pConvert != NULL, "no converter?" ); 529 return *pConvert; 530 } 531 532 bool Convert::hasType( const Type_t& rType ) 533 { 534 return maMap.find( rType ) != maMap.end(); 535 } 536 537 Convert::Types_t Convert::getTypes() 538 { 539 Types_t aTypes( maMap.size() ); 540 transform( maMap.begin(), maMap.end(), aTypes.getArray(), 541 select1st<Map_t::value_type>() ); 542 return aTypes; 543 } 544 545 rtl::OUString Convert::toXSD( const Any_t& rAny ) 546 { 547 Map_t::iterator aIter = maMap.find( rAny.getValueType() ); 548 return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString(); 549 } 550 551 Convert::Any_t Convert::toAny( const rtl::OUString& rValue, 552 const Type_t& rType ) 553 { 554 Map_t::iterator aIter = maMap.find( rType ); 555 return aIter != maMap.end() ? aIter->second.second( rValue ) : Any_t(); 556 } 557 558 //------------------------------------------------------------------------ 559 ::rtl::OUString Convert::convertWhitespace( const ::rtl::OUString& _rString, sal_Int16 _nWhitespaceTreatment ) 560 { 561 ::rtl::OUString sConverted; 562 switch( _nWhitespaceTreatment ) 563 { 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; 576 } 577 return sConverted; 578 } 579 580 //------------------------------------------------------------------------ 581 ::rtl::OUString Convert::replaceWhitespace( const ::rtl::OUString& _rString ) 582 { 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++ ) 587 { 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) ); 593 } 594 return aBuffer.makeStringAndClear(); 595 } 596 597 //------------------------------------------------------------------------ 598 ::rtl::OUString Convert::collapseWhitespace( const ::rtl::OUString& _rString ) 599 { 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++ ) 605 { 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) ) 611 { 612 if( ! bStrip ) 613 { 614 aBuffer.append( sal_Unicode(0x20) ); 615 bStrip = true; 616 } 617 } 618 else 619 { 620 bStrip = false; 621 aBuffer.append( c ); 622 } 623 } 624 if( aBuffer[ aBuffer.getLength() - 1 ] == sal_Unicode( 0x20 ) ) 625 aBuffer.setLength( aBuffer.getLength() - 1 ); 626 return aBuffer.makeStringAndClear(); 627 } 628