xref: /AOO41X/main/forms/source/xforms/convert.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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