1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
21 #include "convert.hxx"
22 
23 #include <sstream>
24 #include <rtl/math.hxx>
25 #include <rtl/ustrbuf.hxx>
26 #include <osl/diagnose.h>
27 #include <tools/date.hxx>
28 #include <com/sun/star/uno/Type.hxx>
29 #include <com/sun/star/util/Date.hpp>
30 #include <com/sun/star/util/DateTime.hpp>
31 #include <com/sun/star/util/Time.hpp>
32 #include <comphelper/sequence.hxx>
33 #include <unotools/datetime.hxx>
34 
35 using xforms::Convert;
36 using com::sun::star::uno::Any;
37 using com::sun::star::uno::makeAny;
38 using namespace std;
39 using namespace utl;
40 
Convert()41 Convert::Convert()
42     : maMap()
43 {
44     init();
45 }
46 
47 #define ADD_ENTRY(XCONVERT,TYPE) XCONVERT->maMap[ cppu::UnoType<TYPE>::get() ] = Convert_t( &lcl_toXSD_##TYPE, &lcl_toAny_##TYPE )
48 
49 namespace
50 {
51 
lcl_toXSD_OUString(const Any & rAny)52     OUString lcl_toXSD_OUString( const Any& rAny )
53     { OUString sStr; rAny >>= sStr; return sStr; }
54 
55 
lcl_toAny_OUString(const OUString & rStr)56     Any lcl_toAny_OUString( const OUString& rStr )
57     { return Any(rStr); }
58 
59 
lcl_toXSD_bool(const Any & rAny)60     OUString lcl_toXSD_bool( const Any& rAny )
61     { bool b = false; rAny >>= b; return b ? OUString("true") : OUString("false"); }
62 
63 
lcl_toAny_bool(const OUString & rStr)64     Any lcl_toAny_bool( const OUString& rStr )
65     {
66         bool b = ( rStr == "true"  ||  rStr == "1" );
67         return makeAny( b );
68     }
69 
70 
lcl_toXSD_double(const Any & rAny)71     OUString lcl_toXSD_double( const Any& rAny )
72     {
73         double f = 0.0;
74         rAny >>= f;
75 
76         return std::isfinite( f )
77             ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic,
78                                         rtl_math_DecimalPlaces_Max, '.',
79                                         true )
80             : OUString();
81     }
82 
83 
lcl_toAny_double(const OUString & rString)84     Any lcl_toAny_double( const OUString& rString )
85     {
86         rtl_math_ConversionStatus eStatus;
87         double f = rtl::math::stringToDouble(
88             rString, '.', ',', &eStatus );
89         return ( eStatus == rtl_math_ConversionStatus_Ok ) ? makeAny( f ) : Any();
90     }
91 
92 
lcl_appendInt32ToBuffer(const sal_Int32 _nValue,OUStringBuffer & _rBuffer,sal_Int16 _nMinDigits)93     void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits )
94     {
95         if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) )
96             _rBuffer.append( '0' );
97         if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) )
98             _rBuffer.append( '0' );
99         if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) )
100             _rBuffer.append( '0' );
101         _rBuffer.append( _nValue );
102     }
103 
104 
lcl_toXSD_UNODate_typed(const css::util::Date & rDate)105     OUString lcl_toXSD_UNODate_typed( const css::util::Date& rDate )
106     {
107 
108         OUStringBuffer sInfo;
109         lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 );
110         sInfo.append( "-" );
111         lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 );
112         sInfo.append( "-" );
113         lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 );
114 
115         return sInfo.makeStringAndClear();
116     }
117 
118 
lcl_toXSD_UNODate(const Any & rAny)119     OUString lcl_toXSD_UNODate( const Any& rAny )
120     {
121         css::util::Date aDate;
122         OSL_VERIFY( rAny >>= aDate );
123         return lcl_toXSD_UNODate_typed( aDate );
124     }
125 
126 
lcl_toUNODate(const OUString & rString)127     css::util::Date lcl_toUNODate( const OUString& rString )
128     {
129         css::util::Date aDate( 1, 1, 1900 );
130 
131         bool bWellformed = ISO8601parseDate(rString, aDate);
132 
133         // sanity checks
134         if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) )
135             bWellformed = false;
136         else
137         {
138             ::Date aDateCheck( 1, aDate.Month, aDate.Year );
139             if ( aDate.Day > aDateCheck.GetDaysInMonth() )
140                 bWellformed = false;
141         }
142 
143         // all okay?
144         if ( !bWellformed )
145             return css::util::Date( 1, 1, 1900 );
146 
147         return aDate;
148     }
149 
150 
lcl_toAny_UNODate(const OUString & rString)151     Any lcl_toAny_UNODate( const OUString& rString )
152     {
153         return makeAny( lcl_toUNODate( rString ) );
154     }
155 
156 
lcl_toXSD_UNOTime_typed(const css::util::Time & rTime)157     OUString lcl_toXSD_UNOTime_typed( const css::util::Time& rTime )
158     {
159 
160         OUStringBuffer sInfo;
161         lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 );
162         sInfo.append( ":" );
163         lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 );
164         sInfo.append( ":" );
165         lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 );
166         if ( rTime.NanoSeconds != 0 )
167         {
168             OSL_ENSURE(rTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
169             sInfo.append('.');
170             std::ostringstream ostr;
171             ostr.fill('0');
172             ostr.width(9);
173             ostr << rTime.NanoSeconds;
174             sInfo.append(OUString::createFromAscii(ostr.str().c_str()));
175         }
176 
177         return sInfo.makeStringAndClear();
178     }
179 
180 
lcl_toXSD_UNOTime(const Any & rAny)181     OUString lcl_toXSD_UNOTime( const Any& rAny )
182     {
183         css::util::Time aTime;
184         OSL_VERIFY( rAny >>= aTime );
185         return lcl_toXSD_UNOTime_typed( aTime );
186     }
187 
188 
lcl_toUNOTime(const OUString & rString)189     css::util::Time lcl_toUNOTime( const OUString& rString )
190     {
191         css::util::Time aTime;
192 
193         bool bWellformed = ISO8601parseTime(rString, aTime);
194 
195         // sanity checks
196         // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere,
197         // but we accept them all the time for simplicity reasons
198         if  (  ( aTime.Hours > 24 )
199             || ( aTime.Minutes > 59 )
200             || ( aTime.Seconds > 60 )
201             )
202             bWellformed = false;
203 
204         if  (   bWellformed
205             &&  ( aTime.Hours == 24 )
206             &&  (   ( aTime.Minutes != 0 )
207                 ||  ( aTime.Seconds != 0 )
208                 ||  ( aTime.NanoSeconds != 0 )
209                 )
210             )
211             bWellformed = false;
212 
213         // all okay?
214         if ( !bWellformed )
215             return css::util::Time();
216 
217         return aTime;
218     }
219 
220 
lcl_toAny_UNOTime(const OUString & rString)221     Any lcl_toAny_UNOTime( const OUString& rString )
222     {
223         return makeAny( lcl_toUNOTime( rString ) );
224     }
225 
226 
lcl_toXSD_UNODateTime(const Any & rAny)227     OUString lcl_toXSD_UNODateTime( const Any& rAny )
228     {
229         css::util::DateTime aDateTime;
230         OSL_VERIFY( rAny >>= aDateTime );
231 
232         css::util::Date aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year );
233         OUString sDate = lcl_toXSD_UNODate_typed( aDate );
234 
235         css::util::Time const aTime( aDateTime.NanoSeconds, aDateTime.Seconds,
236                     aDateTime.Minutes, aDateTime.Hours, aDateTime.IsUTC);
237         OUString sTime = lcl_toXSD_UNOTime_typed( aTime );
238 
239         OUString sRet = sDate + "T" + sTime;
240         return sRet;
241     }
242 
243 
lcl_toAny_UNODateTime(const OUString & rString)244     Any lcl_toAny_UNODateTime( const OUString& rString )
245     {
246         // separate the date from the time part
247         sal_Int32 nDateTimeSep = rString.indexOf( 'T' );
248         if ( nDateTimeSep == -1 )
249             nDateTimeSep = rString.indexOf( 't' );
250 
251         css::util::Date aDate;
252         css::util::Time aTime;
253         if ( nDateTimeSep == -1 )
254         {   // no time part
255             aDate = lcl_toUNODate( rString );
256         }
257         else
258         {
259             aDate = lcl_toUNODate( rString.copy( 0, nDateTimeSep ) );
260             aTime = lcl_toUNOTime( rString.copy( nDateTimeSep + 1 ) );
261         }
262         css::util::DateTime aDateTime(
263             aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
264             aDate.Day, aDate.Month, aDate.Year, aTime.IsUTC
265         );
266         return makeAny( aDateTime );
267     }
268 }
269 
270 
init()271 void Convert::init()
272 {
273     ADD_ENTRY( this, OUString );
274     ADD_ENTRY( this, bool );
275     ADD_ENTRY( this, double );
276     maMap[ cppu::UnoType<css::util::Date>::get() ] = Convert_t( &lcl_toXSD_UNODate, &lcl_toAny_UNODate );
277     maMap[ cppu::UnoType<css::util::Time>::get() ] = Convert_t( &lcl_toXSD_UNOTime, &lcl_toAny_UNOTime );
278     maMap[ cppu::UnoType<css::util::DateTime>::get() ] = Convert_t( &lcl_toXSD_UNODateTime, &lcl_toAny_UNODateTime );
279 }
280 
281 
get()282 Convert& Convert::get()
283 {
284     // create our Singleton instance on demand
285     static Convert aConvert;
286     return aConvert;
287 }
288 
hasType(const css::uno::Type & rType)289 bool Convert::hasType( const css::uno::Type& rType )
290 {
291     return maMap.find( rType ) != maMap.end();
292 }
293 
getTypes() const294 css::uno::Sequence<css::uno::Type> Convert::getTypes() const
295 {
296     return comphelper::mapKeysToSequence( maMap );
297 }
298 
toXSD(const css::uno::Any & rAny)299 OUString Convert::toXSD( const css::uno::Any& rAny )
300 {
301     Map_t::iterator aIter = maMap.find( rAny.getValueType() );
302     return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString();
303 }
304 
toAny(const OUString & rValue,const css::uno::Type & rType)305 css::uno::Any Convert::toAny( const OUString& rValue,
306                               const css::uno::Type& rType )
307 {
308     Map_t::iterator aIter = maMap.find( rType );
309     return aIter != maMap.end() ? aIter->second.second( rValue ) : css::uno::Any();
310 }
311 
312 
collapseWhitespace(const OUString & _rString)313 OUString Convert::collapseWhitespace( const OUString& _rString )
314 {
315     sal_Int32 nLength = _rString.getLength();
316     OUStringBuffer aBuffer( nLength );
317     const sal_Unicode* pStr = _rString.getStr();
318     bool bStrip = true;
319     for( sal_Int32 i = 0; i < nLength; i++ )
320     {
321         sal_Unicode c = pStr[i];
322         if( c == u'\x0008' ||
323             c == u'\x000A' ||
324             c == u'\x000D' ||
325             c == u' ' )
326         {
327             if( ! bStrip )
328             {
329                 aBuffer.append( u' ' );
330                 bStrip = true;
331             }
332         }
333         else
334         {
335             bStrip = false;
336             aBuffer.append( c );
337         }
338     }
339     if( aBuffer[ aBuffer.getLength() - 1 ] == u' ' )
340         aBuffer.setLength( aBuffer.getLength() - 1 );
341     return aBuffer.makeStringAndClear();
342 }
343 
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
345