1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_forms.hxx" 26 27 #include "convert.hxx" 28 29 #include "unohelper.hxx" 30 #include <memory> 31 #include <algorithm> 32 #include <functional> 33 #include <rtl/math.hxx> 34 #include <rtl/ustrbuf.hxx> 35 #include <tools/date.hxx> 36 #include <com/sun/star/uno/Type.hxx> 37 #include <com/sun/star/xsd/WhiteSpaceTreatment.hpp> 38 #include <com/sun/star/util/Date.hpp> 39 #include <com/sun/star/util/DateTime.hpp> 40 #include <com/sun/star/util/Time.hpp> 41 42 using xforms::Convert; 43 using ::rtl::OUString; 44 using ::rtl::OUStringBuffer; 45 using com::sun::star::uno::Any; 46 using com::sun::star::uno::makeAny; 47 using com::sun::star::util::Time; 48 using namespace std; 49 50 typedef com::sun::star::util::Date UNODate; 51 typedef com::sun::star::util::Time UNOTime; 52 typedef com::sun::star::util::DateTime UNODateTime; 53 54 Convert::Convert() 55 : maMap() 56 { 57 init(); 58 } 59 60 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ getCppuType( static_cast<TYPE*>( NULL ) ) ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE ) 61 62 namespace 63 { 64 // ======================================================================== 65 struct StringToken 66 { 67 private: 68 ::rtl::OUString m_sString; 69 sal_Int32 m_nTokenStart; 70 sal_Int32 m_nTokenEnd; 71 72 public: 73 StringToken() : m_sString(), m_nTokenStart( 0 ), m_nTokenEnd( 0 ) { } 74 StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd ); 75 StringToken( const StringToken& ); 76 StringToken& operator=( const StringToken& ); 77 78 inline bool isEmpty() const { return m_nTokenEnd <= m_nTokenStart; } 79 inline sal_Int32 getLength() const { return isEmpty() ? 0 : m_nTokenEnd - m_nTokenStart - 1; } 80 inline const sal_Unicode* begin() const { return m_sString.getStr() + m_nTokenStart; } 81 inline const sal_Unicode* end() const { return m_sString.getStr() + m_nTokenEnd; } 82 83 bool toInt32( sal_Int32& _rValue ) const; 84 }; 85 86 // ------------------------------------------------------------------------ 87 StringToken::StringToken( const ::rtl::OUString& _rString, sal_Int32 _nTokenStart, sal_Int32 _nTokenEnd ) 88 :m_sString( _rString ) 89 ,m_nTokenStart( _nTokenStart ) 90 ,m_nTokenEnd( _nTokenEnd ) 91 { 92 OSL_ENSURE( ( m_nTokenStart >= 0 ) && ( m_nTokenStart <= m_sString.getLength() ), "StringToken::StringToken: invalid token start!" ); 93 OSL_ENSURE( ( m_nTokenEnd >= 0 ) && ( m_nTokenEnd <= m_sString.getLength() ), "StringToken::StringToken: invalid token end!" ); 94 } 95 96 // ------------------------------------------------------------------------ 97 StringToken::StringToken( const StringToken& _rRHS ) 98 { 99 *this = _rRHS; 100 } 101 102 // ------------------------------------------------------------------------ 103 StringToken& StringToken::operator=( const StringToken& _rRHS ) 104 { 105 if ( this == &_rRHS ) 106 return *this; 107 108 m_sString = _rRHS.m_sString; 109 m_nTokenStart = _rRHS.m_nTokenStart; 110 m_nTokenEnd = _rRHS.m_nTokenEnd; 111 112 return *this; 113 } 114 115 // ------------------------------------------------------------------------ 116 bool StringToken::toInt32( sal_Int32& _rValue ) const 117 { 118 if ( isEmpty() ) 119 return false; 120 121 _rValue = 0; 122 const sal_Unicode* pStr = begin(); 123 while ( pStr < end() ) 124 { 125 if ( ( *pStr < '0' ) || ( *pStr > '9' ) ) 126 return false; 127 128 _rValue *= 10; 129 _rValue += ( *pStr - '0' ); 130 131 ++pStr; 132 } 133 134 return true; 135 } 136 137 // ======================================================================== 138 class StringTokenizer 139 { 140 private: 141 ::rtl::OUString m_sString; 142 const sal_Unicode m_nTokenSeparator; 143 sal_Int32 m_nTokenStart; 144 145 public: 146 /** constructs a tokenizer 147 @param _rString the string to tokenize 148 @param _nTokenSeparator the token value. May be 0, in this case the tokenizer 149 will recognize exactly one token, being the whole string. 150 This may make sense if you want to apply <type>StringToken</type> 151 methods to a whole string. 152 */ 153 StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator = ';' ); 154 155 /// resets the tokenizer to the beginning of the string 156 void reset(); 157 158 /// determines whether there is a next token 159 bool hasNextToken() const; 160 161 /// retrieves the next token 162 StringToken 163 getNextToken(); 164 }; 165 166 // ------------------------------------------------------------------------ 167 StringTokenizer::StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator ) 168 :m_sString( _rString ) 169 ,m_nTokenSeparator( _nTokenSeparator ) 170 { 171 reset(); 172 } 173 174 // ------------------------------------------------------------------------ 175 void StringTokenizer::reset() 176 { 177 m_nTokenStart = 0; 178 } 179 180 // ------------------------------------------------------------------------ 181 bool StringTokenizer::hasNextToken() const 182 { 183 return ( m_nTokenStart < m_sString.getLength() ); 184 } 185 186 // ------------------------------------------------------------------------ 187 StringToken StringTokenizer::getNextToken() 188 { 189 OSL_PRECOND( hasNextToken(), "StringTokenizer::getNextToken: there is no next token!" ); 190 if ( !hasNextToken() ) 191 return StringToken(); 192 193 // determine the end of the current token 194 sal_Int32 nTokenEnd = m_nTokenSeparator ? m_sString.indexOf( m_nTokenSeparator, m_nTokenStart ) : m_sString.getLength(); 195 bool bLastToken = !m_nTokenSeparator || ( nTokenEnd == -1 ); 196 197 // construct a new token 198 StringToken aToken( m_sString, m_nTokenStart, bLastToken ? m_sString.getLength() : nTokenEnd ); 199 // advance 200 m_nTokenStart = bLastToken ? m_sString.getLength() : nTokenEnd + 1; 201 // outta here 202 return aToken; 203 } 204 205 // ======================================================================== 206 // ------------------------------------------------------------------------ 207 OUString lcl_toXSD_OUString( const Any& rAny ) 208 { OUString sStr; rAny >>= sStr; return sStr; } 209 210 // ------------------------------------------------------------------------ 211 Any lcl_toAny_OUString( const OUString& rStr ) 212 { Any aAny; aAny <<= rStr; return aAny; } 213 214 // ------------------------------------------------------------------------ 215 OUString lcl_toXSD_bool( const Any& rAny ) 216 { bool b = false; rAny >>= b; return b ? OUSTRING("true") : OUSTRING("false"); } 217 218 // ------------------------------------------------------------------------ 219 Any lcl_toAny_bool( const OUString& rStr ) 220 { 221 bool b = ( rStr == OUSTRING("true") || rStr == OUSTRING("1") ); 222 return makeAny( b ); 223 } 224 225 // ------------------------------------------------------------------------ 226 OUString lcl_toXSD_double( const Any& rAny ) 227 { 228 double f = 0.0; 229 rAny >>= f; 230 231 return rtl::math::isFinite( f ) 232 ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic, 233 rtl_math_DecimalPlaces_Max, '.', 234 sal_True ) 235 : OUString(); 236 } 237 238 // ------------------------------------------------------------------------ 239 Any lcl_toAny_double( const OUString& rString ) 240 { 241 rtl_math_ConversionStatus eStatus; 242 double f = rtl::math::stringToDouble( 243 rString, sal_Unicode('.'), sal_Unicode(','), &eStatus, NULL ); 244 return ( eStatus == rtl_math_ConversionStatus_Ok ) ? makeAny( f ) : Any(); 245 } 246 247 // ------------------------------------------------------------------------ 248 void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, ::rtl::OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits ) 249 { 250 if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) ) 251 _rBuffer.append( (sal_Unicode)'0' ); 252 if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) ) 253 _rBuffer.append( (sal_Unicode)'0' ); 254 if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) ) 255 _rBuffer.append( (sal_Unicode)'0' ); 256 _rBuffer.append( _nValue ); 257 } 258 259 // ------------------------------------------------------------------------ 260 OUString lcl_toXSD_UNODate_typed( const UNODate& rDate ) 261 { 262 263 ::rtl::OUStringBuffer sInfo; 264 lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 ); 265 sInfo.appendAscii( "-" ); 266 lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 ); 267 sInfo.appendAscii( "-" ); 268 lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 ); 269 270 return sInfo.makeStringAndClear(); 271 } 272 273 // ------------------------------------------------------------------------ 274 OUString lcl_toXSD_UNODate( const Any& rAny ) 275 { 276 UNODate aDate; 277 OSL_VERIFY( rAny >>= aDate ); 278 return lcl_toXSD_UNODate_typed( aDate ); 279 } 280 281 // ------------------------------------------------------------------------ 282 UNODate lcl_toUNODate( const OUString& rString ) 283 { 284 bool bWellformed = true; 285 286 UNODate aDate( 1, 1, 1900 ); 287 288 sal_Int32 nToken = 0; 289 StringTokenizer aTokenizer( rString, '-' ); 290 while ( aTokenizer.hasNextToken() ) 291 { 292 sal_Int32 nTokenValue = 0; 293 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) ) 294 { 295 bWellformed = false; 296 break; 297 } 298 299 if ( nToken == 0 ) 300 aDate.Year = (sal_uInt16)nTokenValue; 301 else if ( nToken == 1 ) 302 aDate.Month = (sal_uInt16)nTokenValue; 303 else if ( nToken == 2 ) 304 aDate.Day = (sal_uInt16)nTokenValue; 305 else 306 { 307 bWellformed = false; 308 break; 309 } 310 ++nToken; 311 } 312 313 // sanity checks 314 if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) ) 315 bWellformed = false; 316 else 317 { 318 ::Date aDateCheck( 1, aDate.Month, aDate.Year ); 319 if ( aDate.Day > aDateCheck.GetDaysInMonth() ) 320 bWellformed = false; 321 } 322 323 // all okay? 324 if ( !bWellformed ) 325 return UNODate( 1, 1, 1900 ); 326 327 return aDate; 328 } 329 330 // ------------------------------------------------------------------------ 331 Any lcl_toAny_UNODate( const OUString& rString ) 332 { 333 return makeAny( lcl_toUNODate( rString ) ); 334 } 335 336 // ------------------------------------------------------------------------ 337 OUString lcl_toXSD_UNOTime_typed( const UNOTime& rTime ) 338 { 339 340 ::rtl::OUStringBuffer sInfo; 341 lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 ); 342 sInfo.appendAscii( ":" ); 343 lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 ); 344 sInfo.appendAscii( ":" ); 345 lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 ); 346 if ( rTime.HundredthSeconds ) 347 { 348 sInfo.appendAscii( "." ); 349 lcl_appendInt32ToBuffer( rTime.HundredthSeconds, sInfo, 2 ); 350 } 351 352 return sInfo.makeStringAndClear(); 353 } 354 355 // ------------------------------------------------------------------------ 356 OUString lcl_toXSD_UNOTime( const Any& rAny ) 357 { 358 UNOTime aTime; 359 OSL_VERIFY( rAny >>= aTime ); 360 return lcl_toXSD_UNOTime_typed( aTime ); 361 } 362 363 // ------------------------------------------------------------------------ 364 UNOTime lcl_toUNOTime( const OUString& rString ) 365 { 366 bool bWellformed = true; 367 368 UNOTime aTime( 0, 0, 0, 0 ); 369 370 ::rtl::OUString sString( rString ); 371 // see if there's a decimal separator for the seconds, 372 // and if so, handle it separately 373 sal_Int32 nDecimalSepPos = rString.indexOf( '.' ); 374 if ( nDecimalSepPos == -1 ) 375 // ISO 8601 allows for both a comma and a dot 376 nDecimalSepPos = rString.indexOf( ',' ); 377 if ( nDecimalSepPos != -1 ) 378 { 379 // handle fractional seconds 380 ::rtl::OUString sFractional = sString.copy( nDecimalSepPos + 1 ); 381 if ( sFractional.getLength() > 2 ) 382 // our precision is HundrethSeconds - it's all a css.util.Time can hold 383 sFractional = sFractional.copy( 0, 2 ); 384 sal_Int32 nFractional = 0; 385 if ( sFractional.getLength() ) 386 { 387 if ( StringTokenizer( sFractional, 0 ).getNextToken().toInt32( nFractional ) ) 388 { 389 aTime.HundredthSeconds = (sal_uInt16)nFractional; 390 if ( nFractional < 10 ) 391 aTime.HundredthSeconds *= 10; 392 } 393 else 394 bWellformed = false; 395 } 396 397 // strip the fraction before further processing 398 sString = sString.copy( 0, nDecimalSepPos ); 399 } 400 401 // split into the tokens which are separated by colon 402 sal_Int32 nToken = 0; 403 StringTokenizer aTokenizer( sString, ':' ); 404 while ( aTokenizer.hasNextToken() ) 405 { 406 sal_Int32 nTokenValue = 0; 407 if ( !aTokenizer.getNextToken().toInt32( nTokenValue ) ) 408 { 409 bWellformed = false; 410 break; 411 } 412 413 if ( nToken == 0 ) 414 aTime.Hours = (sal_uInt16)nTokenValue; 415 else if ( nToken == 1 ) 416 aTime.Minutes = (sal_uInt16)nTokenValue; 417 else if ( nToken == 2 ) 418 aTime.Seconds = (sal_uInt16)nTokenValue; 419 else 420 { 421 bWellformed = false; 422 break; 423 } 424 ++nToken; 425 } 426 427 // sanity checks 428 // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere, 429 // but we accept them all the time for simplicity reasons 430 if ( ( aTime.Hours > 24 ) 431 || ( aTime.Minutes > 59 ) 432 || ( aTime.Seconds > 60 ) 433 ) 434 bWellformed = false; 435 436 if ( bWellformed 437 && ( aTime.Hours == 24 ) 438 && ( ( aTime.Minutes != 0 ) 439 || ( aTime.Seconds != 0 ) 440 || ( aTime.HundredthSeconds != 0 ) 441 ) 442 ) 443 bWellformed = false; 444 445 // all okay? 446 if ( !bWellformed ) 447 return UNOTime( 0, 0, 0, 0 ); 448 449 return aTime; 450 } 451 452 // ------------------------------------------------------------------------ 453 Any lcl_toAny_UNOTime( const OUString& rString ) 454 { 455 return makeAny( lcl_toUNOTime( rString ) ); 456 } 457 458 // ------------------------------------------------------------------------ 459 OUString lcl_toXSD_UNODateTime( const Any& rAny ) 460 { 461 UNODateTime aDateTime; 462 OSL_VERIFY( rAny >>= aDateTime ); 463 464 UNODate aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year ); 465 ::rtl::OUString sDate = lcl_toXSD_UNODate_typed( aDate ); 466 467 UNOTime aTime( aDateTime.HundredthSeconds, aDateTime.Seconds, aDateTime.Minutes, aDateTime.Hours ); 468 ::rtl::OUString sTime = lcl_toXSD_UNOTime_typed( aTime ); 469 470 ::rtl::OUStringBuffer sInfo; 471 sInfo.append( sDate ); 472 sInfo.append( (sal_Unicode) 'T' ); 473 sInfo.append( sTime ); 474 return sInfo.makeStringAndClear(); 475 } 476 477 // ------------------------------------------------------------------------ 478 Any lcl_toAny_UNODateTime( const OUString& rString ) 479 { 480 // separate the date from the time part 481 sal_Int32 nDateTimeSep = rString.indexOf( 'T' ); 482 if ( nDateTimeSep == -1 ) 483 nDateTimeSep = rString.indexOf( 't' ); 484 485 UNODate aDate; 486 UNOTime aTime; 487 if ( nDateTimeSep == -1 ) 488 { // no time part 489 aDate = lcl_toUNODate( rString ); 490 aTime = UNOTime( 0, 0, 0, 0 ); 491 } 492 else 493 { 494 aDate = lcl_toUNODate( rString.copy( 0, nDateTimeSep ) ); 495 aTime = lcl_toUNOTime( rString.copy( nDateTimeSep + 1 ) ); 496 } 497 UNODateTime aDateTime( 498 aTime.HundredthSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours, 499 aDate.Day, aDate.Month, aDate.Year 500 ); 501 return makeAny( aDateTime ); 502 } 503 } 504 505 // ============================================================================ 506 void Convert::init() 507 { 508 ADD_ENTRY( this, OUString ); 509 ADD_ENTRY( this, bool ); 510 ADD_ENTRY( this, double ); 511 ADD_ENTRY( this, UNODate ); 512 ADD_ENTRY( this, UNOTime ); 513 ADD_ENTRY( this, UNODateTime ); 514 } 515 516 517 Convert& Convert::get() 518 { 519 // create our Singleton instance on demand 520 static Convert* pConvert = NULL; 521 if( pConvert == NULL ) 522 pConvert = new Convert(); 523 524 OSL_ENSURE( pConvert != NULL, "no converter?" ); 525 return *pConvert; 526 } 527 528 bool Convert::hasType( const Type_t& rType ) 529 { 530 return maMap.find( rType ) != maMap.end(); 531 } 532 533 Convert::Types_t Convert::getTypes() 534 { 535 Types_t aTypes( maMap.size() ); 536 transform( maMap.begin(), maMap.end(), aTypes.getArray(), 537 select1st<Map_t::value_type>() ); 538 return aTypes; 539 } 540 541 rtl::OUString Convert::toXSD( const Any_t& rAny ) 542 { 543 Map_t::iterator aIter = maMap.find( rAny.getValueType() ); 544 return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString(); 545 } 546 547 Convert::Any_t Convert::toAny( const rtl::OUString& rValue, 548 const Type_t& rType ) 549 { 550 Map_t::iterator aIter = maMap.find( rType ); 551 return aIter != maMap.end() ? aIter->second.second( rValue ) : Any_t(); 552 } 553 554 //------------------------------------------------------------------------ 555 ::rtl::OUString Convert::convertWhitespace( const ::rtl::OUString& _rString, sal_Int16 _nWhitespaceTreatment ) 556 { 557 ::rtl::OUString sConverted; 558 switch( _nWhitespaceTreatment ) 559 { 560 default: 561 OSL_ENSURE( sal_False, "Convert::convertWhitespace: invalid whitespace treatment constant!" ); 562 // NO break 563 case com::sun::star::xsd::WhiteSpaceTreatment::Preserve: 564 sConverted = _rString; 565 break; 566 case com::sun::star::xsd::WhiteSpaceTreatment::Replace: 567 sConverted = replaceWhitespace( _rString ); 568 break; 569 case com::sun::star::xsd::WhiteSpaceTreatment::Collapse: 570 sConverted = collapseWhitespace( _rString ); 571 break; 572 } 573 return sConverted; 574 } 575 576 //------------------------------------------------------------------------ 577 ::rtl::OUString Convert::replaceWhitespace( const ::rtl::OUString& _rString ) 578 { 579 OUStringBuffer aBuffer( _rString ); 580 sal_Int32 nLength = aBuffer.getLength(); 581 const sal_Unicode* pBuffer = aBuffer.getStr(); 582 for( sal_Int32 i = 0; i < nLength; i++ ) 583 { 584 sal_Unicode c = pBuffer[i]; 585 if( c == sal_Unicode(0x08) || 586 c == sal_Unicode(0x0A) || 587 c == sal_Unicode(0x0D) ) 588 aBuffer.setCharAt( i, sal_Unicode(0x20) ); 589 } 590 return aBuffer.makeStringAndClear(); 591 } 592 593 //------------------------------------------------------------------------ 594 ::rtl::OUString Convert::collapseWhitespace( const ::rtl::OUString& _rString ) 595 { 596 sal_Int32 nLength = _rString.getLength(); 597 OUStringBuffer aBuffer( nLength ); 598 const sal_Unicode* pStr = _rString.getStr(); 599 bool bStrip = true; 600 for( sal_Int32 i = 0; i < nLength; i++ ) 601 { 602 sal_Unicode c = pStr[i]; 603 if( c == sal_Unicode(0x08) || 604 c == sal_Unicode(0x0A) || 605 c == sal_Unicode(0x0D) || 606 c == sal_Unicode(0x20) ) 607 { 608 if( ! bStrip ) 609 { 610 aBuffer.append( sal_Unicode(0x20) ); 611 bStrip = true; 612 } 613 } 614 else 615 { 616 bStrip = false; 617 aBuffer.append( c ); 618 } 619 } 620 if( aBuffer[ aBuffer.getLength() - 1 ] == sal_Unicode( 0x20 ) ) 621 aBuffer.setLength( aBuffer.getLength() - 1 ); 622 return aBuffer.makeStringAndClear(); 623 } 624