xref: /aoo41x/main/forms/source/xforms/convert.cxx (revision 24acc546)
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 
Convert()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:
StringToken__anoncd25293c0111::StringToken73         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 
isEmpty__anoncd25293c0111::StringToken78         inline  bool                isEmpty() const { return m_nTokenEnd <= m_nTokenStart; }
getLength__anoncd25293c0111::StringToken79         inline  sal_Int32           getLength() const { return isEmpty() ? 0 : m_nTokenEnd - m_nTokenStart - 1; }
begin__anoncd25293c0111::StringToken80         inline  const sal_Unicode*  begin() const { return m_sString.getStr() + m_nTokenStart; }
end__anoncd25293c0111::StringToken81         inline  const sal_Unicode*  end() const { return m_sString.getStr() + m_nTokenEnd; }
82 
83         bool    toInt32( sal_Int32& _rValue ) const;
84     };
85 
86     // ------------------------------------------------------------------------
StringToken(const::rtl::OUString & _rString,sal_Int32 _nTokenStart,sal_Int32 _nTokenEnd)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     // ------------------------------------------------------------------------
StringToken(const StringToken & _rRHS)97     StringToken::StringToken( const StringToken& _rRHS )
98     {
99         *this = _rRHS;
100     }
101 
102     // ------------------------------------------------------------------------
operator =(const StringToken & _rRHS)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     // ------------------------------------------------------------------------
toInt32(sal_Int32 & _rValue) const116     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     // ------------------------------------------------------------------------
StringTokenizer(const::rtl::OUString & _rString,sal_Unicode _nTokenSeparator)167     StringTokenizer::StringTokenizer( const ::rtl::OUString& _rString, sal_Unicode _nTokenSeparator )
168         :m_sString( _rString )
169         ,m_nTokenSeparator( _nTokenSeparator )
170     {
171         reset();
172     }
173 
174     // ------------------------------------------------------------------------
reset()175     void StringTokenizer::reset()
176     {
177         m_nTokenStart = 0;
178     }
179 
180     // ------------------------------------------------------------------------
hasNextToken() const181     bool StringTokenizer::hasNextToken() const
182     {
183         return ( m_nTokenStart < m_sString.getLength() );
184     }
185 
186     // ------------------------------------------------------------------------
getNextToken()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     // ------------------------------------------------------------------------
lcl_toXSD_OUString(const Any & rAny)207     OUString lcl_toXSD_OUString( const Any& rAny )
208     { OUString sStr; rAny >>= sStr; return sStr; }
209 
210     // ------------------------------------------------------------------------
lcl_toAny_OUString(const OUString & rStr)211     Any lcl_toAny_OUString( const OUString& rStr )
212     { Any aAny; aAny <<= rStr; return aAny; }
213 
214     // ------------------------------------------------------------------------
lcl_toXSD_bool(const Any & rAny)215     OUString lcl_toXSD_bool( const Any& rAny )
216     { bool b = false; rAny >>= b; return b ? OUSTRING("true") : OUSTRING("false"); }
217 
218     // ------------------------------------------------------------------------
lcl_toAny_bool(const OUString & rStr)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     // ------------------------------------------------------------------------
lcl_toXSD_double(const Any & rAny)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     // ------------------------------------------------------------------------
lcl_toAny_double(const OUString & rString)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     // ------------------------------------------------------------------------
lcl_appendInt32ToBuffer(const sal_Int32 _nValue,::rtl::OUStringBuffer & _rBuffer,sal_Int16 _nMinDigits)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     // ------------------------------------------------------------------------
lcl_toXSD_UNODate_typed(const UNODate & rDate)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     // ------------------------------------------------------------------------
lcl_toXSD_UNODate(const Any & rAny)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     // ------------------------------------------------------------------------
lcl_toUNODate(const OUString & rString)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     // ------------------------------------------------------------------------
lcl_toAny_UNODate(const OUString & rString)331     Any lcl_toAny_UNODate( const OUString& rString )
332     {
333         return makeAny( lcl_toUNODate( rString ) );
334     }
335 
336     // ------------------------------------------------------------------------
lcl_toXSD_UNOTime_typed(const UNOTime & rTime)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     // ------------------------------------------------------------------------
lcl_toXSD_UNOTime(const Any & rAny)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     // ------------------------------------------------------------------------
lcl_toUNOTime(const OUString & rString)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     // ------------------------------------------------------------------------
lcl_toAny_UNOTime(const OUString & rString)453     Any lcl_toAny_UNOTime( const OUString& rString )
454     {
455         return makeAny( lcl_toUNOTime( rString ) );
456     }
457 
458     // ------------------------------------------------------------------------
lcl_toXSD_UNODateTime(const Any & rAny)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     // ------------------------------------------------------------------------
lcl_toAny_UNODateTime(const OUString & rString)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 // ============================================================================
init()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 
get()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 
hasType(const Type_t & rType)528 bool Convert::hasType( const Type_t& rType )
529 {
530     return maMap.find( rType ) != maMap.end();
531 }
532 
getTypes()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 
toXSD(const Any_t & rAny)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 
toAny(const rtl::OUString & rValue,const Type_t & rType)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 //------------------------------------------------------------------------
convertWhitespace(const::rtl::OUString & _rString,sal_Int16 _nWhitespaceTreatment)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 //------------------------------------------------------------------------
replaceWhitespace(const::rtl::OUString & _rString)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 //------------------------------------------------------------------------
collapseWhitespace(const::rtl::OUString & _rString)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