Line data Source code
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 <stdlib.h>
22 :
23 : #include <basic/sbxform.hxx>
24 : #include <rtl/ustrbuf.hxx>
25 :
26 : /*
27 : TODO: are there any Star-Basic characteristics unconsidered?
28 :
29 : what means: * as placeholder
30 :
31 : COMMENT: Visual-Basic treats the following (invalid) format-strings
32 : as shown:
33 :
34 : ##0##.##0## --> ##000.000##
35 :
36 : (this class behaves the same way)
37 : */
38 :
39 : #include <stdio.h> // for: sprintf()
40 : #include <float.h> // for: DBL_DIG, DBL_EPSILON
41 : #include <math.h> // for: floor(), fabs(), log10(), pow()
42 :
43 : //=================================================================
44 : //=========================== DEFINES =============================
45 : //=================================================================
46 :
47 : #define _NO_DIGIT -1
48 :
49 : #define MAX_NO_OF_EXP_DIGITS 5
50 : // +4 because of the value range: between -308 and +308
51 : // +1 for closing 0
52 : #define MAX_NO_OF_DIGITS DBL_DIG
53 : #define MAX_DOUBLE_BUFFER_LENGTH MAX_NO_OF_DIGITS + 9
54 : // +1 for leading sign
55 : // +1 for digit before the decimal point
56 : // +1 for decimal point
57 : // +2 for exponent E and exp. leading sign
58 : // +3 for the exponent's value
59 : // +1 for closing 0
60 :
61 : // Defines for the digits:
62 : #define ASCII_0 '0' // 48
63 : #define ASCII_9 '9' // 57
64 :
65 : #define CREATE_1000SEP_CHAR '@'
66 :
67 : #define FORMAT_SEPARATOR ';'
68 :
69 : // predefined formats for the Format$()-command:
70 : #define BASICFORMAT_GENERALNUMBER "General Number"
71 : #define BASICFORMAT_CURRENCY "Currency"
72 : #define BASICFORMAT_FIXED "Fixed"
73 : #define BASICFORMAT_STANDARD "Standard"
74 : #define BASICFORMAT_PERCENT "Percent"
75 : #define BASICFORMAT_SCIENTIFIC "Scientific"
76 : #define BASICFORMAT_YESNO "Yes/No"
77 : #define BASICFORMAT_TRUEFALSE "True/False"
78 : #define BASICFORMAT_ONOFF "On/Off"
79 :
80 : #define EMPTYFORMATSTRING ""
81 :
82 : // Comment: Visual-Basic has a maximum of 12 positions after the
83 : // decimal point for floating-point-numbers.
84 : // all format-strings are compatible to Visual-Basic:
85 : #define GENERALNUMBER_FORMAT "0.############"
86 : #define CURRENCY_FORMAT "@$0.00;@($0.00)"
87 : #define FIXED_FORMAT "0.00"
88 : #define STANDARD_FORMAT "@0.00"
89 : #define PERCENT_FORMAT "0.00%"
90 : #define SCIENTIFIC_FORMAT "#.00E+00"
91 : // Comment: the character @ means that thousand-separators shall
92 : // be generated. That's a StarBasic 'extension'.
93 :
94 : //=================================================================
95 :
96 :
97 0 : double get_number_of_digits( double dNumber )
98 : //double floor_log10_fabs( double dNumber )
99 : {
100 0 : if( dNumber==0.0 )
101 0 : return 0.0; // used to be 1.0, now 0.0 because of #40025;
102 : else
103 0 : return floor( log10( fabs( dNumber ) ) );
104 : }
105 :
106 : //=================================================================
107 : //======================= IMPLEMENTATION ==========================
108 : //=================================================================
109 :
110 0 : SbxBasicFormater::SbxBasicFormater( sal_Unicode _cDecPoint, sal_Unicode _cThousandSep,
111 : OUString _sOnStrg,
112 : OUString _sOffStrg,
113 : OUString _sYesStrg,
114 : OUString _sNoStrg,
115 : OUString _sTrueStrg,
116 : OUString _sFalseStrg,
117 : OUString _sCurrencyStrg,
118 0 : OUString _sCurrencyFormatStrg )
119 : {
120 0 : cDecPoint = _cDecPoint;
121 0 : cThousandSep = _cThousandSep;
122 0 : sOnStrg = _sOnStrg;
123 0 : sOffStrg = _sOffStrg;
124 0 : sYesStrg = _sYesStrg;
125 0 : sNoStrg = _sNoStrg;
126 0 : sTrueStrg = _sTrueStrg;
127 0 : sFalseStrg = _sFalseStrg;
128 0 : sCurrencyStrg = _sCurrencyStrg;
129 0 : sCurrencyFormatStrg = _sCurrencyFormatStrg;
130 0 : }
131 :
132 : // function for ouput of a error-text (for debugging)
133 : // displaces all characters of the string, starting from nStartPos
134 : // for one position to larger indexes, i. e. place for a new
135 : // character (which is to be inserted) is created.
136 : // ATTENTION: the string MUST be long enough!
137 0 : inline void SbxBasicFormater::ShiftString( OUStringBuffer& sStrg, sal_uInt16 nStartPos )
138 : {
139 0 : sStrg.remove(nStartPos,1);
140 0 : }
141 :
142 0 : void SbxBasicFormater::AppendDigit( OUStringBuffer& sStrg, short nDigit )
143 : {
144 0 : if( nDigit>=0 && nDigit<=9 )
145 : {
146 0 : sStrg.append((sal_Unicode)(nDigit+ASCII_0));
147 : }
148 0 : }
149 :
150 0 : void SbxBasicFormater::LeftShiftDecimalPoint( OUStringBuffer& sStrg )
151 : {
152 0 : sal_Int32 nPos = -1;
153 :
154 0 : for(sal_Int32 i = 0; i < sStrg.getLength(); i++)
155 : {
156 0 : if(sStrg[i] == cDecPoint)
157 : {
158 0 : nPos = i;
159 0 : break;
160 : }
161 : }
162 0 : if( nPos >= 0 )
163 : {
164 0 : sStrg[nPos] = sStrg[nPos - 1];
165 0 : sStrg[nPos - 1] = cDecPoint;
166 : }
167 0 : }
168 :
169 : // returns a flag if rounding a 9
170 0 : void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos, sal_Bool& bOverflow )
171 : {
172 0 : if( nPos<0 )
173 : {
174 0 : return;
175 : }
176 0 : bOverflow = sal_False;
177 0 : sal_Unicode c = sStrg[nPos];
178 0 : if( nPos > 0 && (c == cDecPoint || c == cThousandSep) )
179 : {
180 0 : StrRoundDigit( sStrg, nPos - 1, bOverflow );
181 : // CHANGE from 9.3.1997: end the method immediately after recursive call!
182 0 : return;
183 : }
184 : // skip non-digits:
185 : // COMMENT:
186 : // in a valid format-string the number's output should be done
187 : // in one piece, i. e. special characters should ONLY be in
188 : // front OR behind the number and not right in the middle of
189 : // the format information for the number
190 0 : while( nPos >= 0 && ( sStrg[nPos] < ASCII_0 || sStrg[nPos] > ASCII_9 ))
191 : {
192 0 : nPos--;
193 : }
194 0 : if( nPos==-1 )
195 : {
196 0 : ShiftString( sStrg, 0 );
197 0 : sStrg[0] = (sal_Unicode)'1';
198 0 : bOverflow = sal_True;
199 : }
200 : else
201 : {
202 0 : sal_Unicode c2 = sStrg[nPos];
203 0 : if( c2 >= ASCII_0 && c2 <= ASCII_9 )
204 : {
205 0 : if( c2 == ASCII_9 )
206 : {
207 0 : sStrg[nPos] = (sal_Unicode)'0';
208 0 : StrRoundDigit( sStrg, nPos - 1, bOverflow );
209 : }
210 : else
211 : {
212 0 : sStrg[nPos] = c2 + 1;
213 : }
214 : }
215 : else
216 : {
217 0 : ShiftString( sStrg,nPos+1 );
218 0 : sStrg[nPos + 1] = (sal_Unicode)'1';
219 0 : bOverflow = sal_True;
220 : }
221 : }
222 : }
223 :
224 0 : void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos )
225 : {
226 : sal_Bool bOverflow;
227 :
228 0 : StrRoundDigit( sStrg, nPos, bOverflow );
229 0 : }
230 :
231 0 : void SbxBasicFormater::ParseBack( OUStringBuffer& sStrg, const OUString& sFormatStrg,
232 : short nFormatPos )
233 : {
234 0 : for( sal_Int32 i = nFormatPos;
235 0 : i>0 && sFormatStrg[ i ] == (sal_Unicode)'#' && sStrg[sStrg.getLength() - 1] == (sal_Unicode)'0';
236 : i-- )
237 : {
238 0 : sStrg.setLength(sStrg.getLength() - 1 );
239 : }
240 0 : }
241 :
242 : #ifdef _with_sprintf
243 :
244 :
245 0 : void SbxBasicFormater::InitScan( double _dNum )
246 : {
247 : char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ];
248 :
249 0 : dNum = _dNum;
250 0 : InitExp( get_number_of_digits( dNum ) );
251 : // maximum of 15 positions behind the decimal point, example: -1.234000000000000E-001
252 0 : /*int nCount =*/ sprintf( sBuffer,"%+22.15lE",dNum );
253 0 : sSciNumStrg = rtl::OUString::createFromAscii( sBuffer );
254 0 : }
255 :
256 :
257 0 : void SbxBasicFormater::InitExp( double _dNewExp )
258 : {
259 : char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ];
260 0 : nNumExp = (short)_dNewExp;
261 0 : /*int nCount =*/ sprintf( sBuffer,"%+i",nNumExp );
262 0 : sNumExpStrg = rtl::OUString::createFromAscii( sBuffer );
263 0 : nExpExp = (short)get_number_of_digits( (double)nNumExp );
264 0 : }
265 :
266 :
267 0 : short SbxBasicFormater::GetDigitAtPosScan( short nPos, sal_Bool& bFoundFirstDigit )
268 : {
269 : // trying to read a higher digit,
270 : // e. g. position 4 in 1.234,
271 : // or to read a digit outside of the
272 : // number's dissolution (double)
273 0 : if( nPos>nNumExp || abs(nNumExp-nPos)>MAX_NO_OF_DIGITS )
274 : {
275 0 : return _NO_DIGIT;
276 : }
277 : // determine the index of the position in the number-string:
278 : // skip the leading sign
279 0 : sal_uInt16 no = 1;
280 : // skip the decimal point if necessary
281 0 : if( nPos<nNumExp )
282 0 : no++;
283 0 : no += nNumExp-nPos;
284 : // query of the number's first valid digit --> set flag
285 0 : if( nPos==nNumExp )
286 0 : bFoundFirstDigit = sal_True;
287 0 : return (short)(sSciNumStrg[ no ] - ASCII_0);
288 : }
289 :
290 0 : short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, sal_Bool& bFoundFirstDigit )
291 : {
292 0 : if( nPos>nExpExp )
293 0 : return -1;
294 :
295 0 : sal_uInt16 no = 1;
296 0 : no += nExpExp-nPos;
297 :
298 0 : if( nPos==nExpExp )
299 0 : bFoundFirstDigit = sal_True;
300 0 : return (short)(sNumExpStrg[ no ] - ASCII_0);
301 : }
302 :
303 : // a value for the exponent can be given because the number maybe shall
304 : // not be displayed in a normed way (e. g. 1.2345e-03) but maybe 123.345e-3 !
305 0 : short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent, short nPos,
306 : sal_Bool& bFoundFirstDigit )
307 : {
308 0 : InitExp( dNewExponent );
309 :
310 0 : return GetDigitAtPosExpScan( nPos,bFoundFirstDigit );
311 : }
312 :
313 : #else
314 :
315 : /* Problems with the following method:
316 :
317 : TODO: an 'intelligent' peek-parser might be needed to detect rounding
318 : mistakes at double-numbers - e. g. for 0.00115 #.#e-000
319 :
320 : problem with: format( 0.3345 , "0.000" )
321 : problem with: format( 0.00115 , "0.0000" )
322 :
323 : */
324 : // returns the digit at the given '10 system'-position,
325 : // i. e. positive nPos for positions before the decimal
326 : // point and negative for positions after.
327 : // nPos==0 means first position after the decimalpoint, so 10^0.
328 : // returns 0..9 for valid digits and -1 for not existing,
329 : // i. e. if the passed number is too small
330 : // (e. g. position 5 of dNumber=123).
331 : // Furthermore in dNextNumber the number shorted by leading
332 : // positions (till nPos) is returned, e. g.
333 : // GetDigitAtPos( 3434.565 , 2 , dNewNumber ) --> dNewNumber = 434.565
334 : // In bFoundFirstDigit a flag is set if a digit has been found,
335 : // this is used to prevent 'errors' on parsing 202
336 : // ATTENTION: apparently there are sometimes still problems with rounding mistakes!
337 : short SbxBasicFormater::GetDigitAtPos( double dNumber, short nPos,
338 : double& dNextNumber, sal_Bool& bFoundFirstDigit )
339 : {
340 : double dDigit;
341 : short nMaxDigit;
342 :
343 : dNumber = fabs( dNumber );
344 :
345 : nMaxDigit = (short)get_number_of_digits( dNumber );
346 : // error only at numbers > 0, i. e. for digits before
347 : // the decimal point
348 : if( nMaxDigit<nPos && !bFoundFirstDigit && nPos>=0 )
349 : return _NO_DIGIT;
350 :
351 : bFoundFirstDigit = sal_True;
352 : for( short i=nMaxDigit; i>=nPos; i-- )
353 : {
354 : double dI = (double)i;
355 : double dTemp1 = pow( 10.0,dI );
356 :
357 : dDigit = floor( pow( 10.0,log10( fabs( dNumber ) )-dI ) );
358 : dNumber -= dTemp1 * dDigit;
359 : }
360 : // for optimized loop run
361 : dNextNumber = dNumber;
362 :
363 : return RoundDigit( dDigit );
364 : }
365 :
366 :
367 : short SbxBasicFormater::RoundDigit( double dNumber )
368 : {
369 : if( dNumber<0.0 || dNumber>10.0 )
370 : return -1;
371 : short nTempHigh = (short)(dNumber+0.5); // maybe floor( )
372 : return nTempHigh;
373 : }
374 :
375 : #endif
376 :
377 : // Copies the respective part of the format-string, if existing, and returns it.
378 : // So a new string is created, which has to be freed by the caller later.
379 0 : OUString SbxBasicFormater::GetPosFormatString( const OUString& sFormatStrg, sal_Bool & bFound )
380 : {
381 0 : bFound = sal_False; // default...
382 0 : sal_Int32 nPos = sFormatStrg.indexOf( FORMAT_SEPARATOR );
383 :
384 0 : if( nPos >= 0 )
385 : {
386 0 : bFound = sal_True;
387 : // the format-string for positive numbers is
388 : // everything before the first ';'
389 0 : return sFormatStrg.copy( 0,nPos );
390 : }
391 :
392 0 : OUString aRetStr;
393 0 : aRetStr = rtl::OUString::createFromAscii( EMPTYFORMATSTRING );
394 0 : return aRetStr;
395 : }
396 :
397 : // see also GetPosFormatString()
398 0 : OUString SbxBasicFormater::GetNegFormatString( const OUString& sFormatStrg, sal_Bool & bFound )
399 : {
400 0 : bFound = sal_False; // default...
401 0 : sal_Int32 nPos = sFormatStrg.indexOf( FORMAT_SEPARATOR );
402 :
403 0 : if( nPos >= 0)
404 : {
405 : // the format-string for negative numbers is
406 : // everything between the first and the second ';'
407 0 : OUString sTempStrg = sFormatStrg.copy( nPos+1 );
408 0 : nPos = sTempStrg.indexOf( FORMAT_SEPARATOR );
409 0 : bFound = sal_True;
410 0 : if( nPos < 0 )
411 : {
412 0 : return sTempStrg;
413 : }
414 : else
415 : {
416 0 : return sTempStrg.copy( 0,nPos );
417 0 : }
418 : }
419 0 : OUString aRetStr;
420 0 : aRetStr = rtl::OUString::createFromAscii( EMPTYFORMATSTRING );
421 0 : return aRetStr;
422 : }
423 :
424 : // see also GetPosFormatString()
425 0 : OUString SbxBasicFormater::Get0FormatString( const OUString& sFormatStrg, sal_Bool & bFound )
426 : {
427 0 : bFound = sal_False; // default...
428 0 : sal_Int32 nPos = sFormatStrg.indexOf( FORMAT_SEPARATOR );
429 :
430 0 : if( nPos >= 0 )
431 : {
432 : // the format string for the zero is
433 : // everything after the second ';'
434 0 : OUString sTempStrg = sFormatStrg.copy( nPos+1 );
435 0 : nPos = sTempStrg.indexOf( FORMAT_SEPARATOR );
436 0 : if( nPos >= 0 )
437 : {
438 0 : bFound = sal_True;
439 0 : sTempStrg = sTempStrg.copy( nPos+1 );
440 0 : nPos = sTempStrg.indexOf( FORMAT_SEPARATOR );
441 0 : if( nPos < 0 )
442 : {
443 0 : return sTempStrg;
444 : }
445 : else
446 : {
447 0 : return sTempStrg.copy( 0,nPos );
448 : }
449 0 : }
450 : }
451 :
452 0 : OUString aRetStr;
453 0 : aRetStr = rtl::OUString::createFromAscii( EMPTYFORMATSTRING );
454 0 : return aRetStr;
455 : }
456 :
457 : // see also GetPosFormatString()
458 0 : OUString SbxBasicFormater::GetNullFormatString( const OUString& sFormatStrg, sal_Bool & bFound )
459 : {
460 0 : bFound = sal_False; // default...
461 0 : sal_Int32 nPos = sFormatStrg.indexOf( FORMAT_SEPARATOR );
462 :
463 0 : if( nPos >= 0 )
464 : {
465 : // the format-string for the Null is
466 : // everything after the third ';'
467 0 : OUString sTempStrg = sFormatStrg.copy( nPos+1 );
468 0 : nPos = sTempStrg.indexOf( FORMAT_SEPARATOR );
469 0 : if( nPos >= 0 )
470 : {
471 0 : sTempStrg = sTempStrg.copy( nPos+1 );
472 0 : nPos = sTempStrg.indexOf( FORMAT_SEPARATOR );
473 0 : if( nPos >= 0 )
474 : {
475 0 : bFound = sal_True;
476 0 : return sTempStrg.copy( nPos+1 );
477 : }
478 0 : }
479 : }
480 :
481 0 : OUString aRetStr;
482 0 : aRetStr = rtl::OUString::createFromAscii( EMPTYFORMATSTRING );
483 0 : return aRetStr;
484 : }
485 :
486 : // returns value <> 0 in case of an error
487 0 : short SbxBasicFormater::AnalyseFormatString( const OUString& sFormatStrg,
488 : short& nNoOfDigitsLeft, short& nNoOfDigitsRight,
489 : short& nNoOfOptionalDigitsLeft,
490 : short& nNoOfExponentDigits, short& nNoOfOptionalExponentDigits,
491 : sal_Bool& bPercent, sal_Bool& bCurrency, sal_Bool& bScientific,
492 : sal_Bool& bGenerateThousandSeparator,
493 : short& nMultipleThousandSeparators )
494 : {
495 : sal_Int32 nLen;
496 0 : short nState = 0;
497 :
498 0 : nLen = sFormatStrg.getLength();
499 0 : nNoOfDigitsLeft = 0;
500 0 : nNoOfDigitsRight = 0;
501 0 : nNoOfOptionalDigitsLeft = 0;
502 0 : nNoOfExponentDigits = 0;
503 0 : nNoOfOptionalExponentDigits = 0;
504 0 : bPercent = sal_False;
505 0 : bCurrency = sal_False;
506 0 : bScientific = sal_False;
507 : // from 11.7.97: as soon as a comma (point?) is found in the format string,
508 : // all three decimal powers are marked (i. e. thousand, million, ...)
509 0 : bGenerateThousandSeparator = sFormatStrg.indexOf( ',' ) >= 0;
510 0 : nMultipleThousandSeparators = 0;
511 :
512 0 : for( sal_Int32 i = 0; i < nLen; i++ )
513 : {
514 0 : sal_Unicode c = sFormatStrg[ i ];
515 0 : switch( c )
516 : {
517 : case '#':
518 : case '0':
519 0 : if( nState==0 )
520 : {
521 0 : nNoOfDigitsLeft++;
522 : // TODO here maybe better error inspection of the mantissa for valid syntax (see grammar)h
523 : // ATTENTION: 'undefined' behaviour if # and 0 are combined!
524 : // REMARK: #-placeholders are actually useless for
525 : // scientific display before the decimal point!
526 0 : if( c=='#' )
527 : {
528 0 : nNoOfOptionalDigitsLeft++;
529 : }
530 : }
531 0 : else if( nState==1 )
532 : {
533 0 : nNoOfDigitsRight++;
534 : }
535 0 : else if( nState==-1 ) // search 0 in the exponent
536 : {
537 0 : if( c=='#' ) // # switches on the condition
538 : {
539 0 : nNoOfOptionalExponentDigits++;
540 0 : nState = -2;
541 : }
542 0 : nNoOfExponentDigits++;
543 : }
544 0 : else if( nState==-2 ) // search # in the exponent
545 : {
546 0 : if( c=='0' )
547 : {
548 : // ERROR: 0 after # in the exponent is NOT allowed!!
549 0 : return -4;
550 : }
551 0 : nNoOfOptionalExponentDigits++;
552 0 : nNoOfExponentDigits++;
553 : }
554 0 : break;
555 : case '.':
556 0 : nState++;
557 0 : if( nState>1 )
558 : {
559 0 : return -1; // ERROR: too many decimal points
560 : }
561 0 : break;
562 : case '%':
563 0 : bPercent = sal_True;
564 0 : break;
565 : case '(':
566 0 : bCurrency = sal_True;
567 0 : break;
568 : case ',':
569 : {
570 0 : sal_Unicode ch = sFormatStrg[ i+1 ];
571 :
572 0 : if( ch!=0 && (ch==',' || ch=='.') )
573 : {
574 0 : nMultipleThousandSeparators++;
575 : }
576 : }
577 0 : break;
578 : case 'e':
579 : case 'E':
580 : // #i13821 not when no digits before
581 0 : if( nNoOfDigitsLeft > 0 || nNoOfDigitsRight > 0 )
582 : {
583 0 : nState = -1; // abort counting digits
584 0 : bScientific = sal_True;
585 : }
586 0 : break;
587 : // OWN command-character which turns on
588 : // the creation of thousand-separators
589 : case '\\':
590 : // Ignore next char
591 0 : i++;
592 0 : break;
593 : case CREATE_1000SEP_CHAR:
594 0 : bGenerateThousandSeparator = sal_True;
595 0 : break;
596 : }
597 : }
598 0 : return 0;
599 : }
600 :
601 : // the flag bCreateSign says that at the mantissa a leading sign
602 : // shall be created
603 0 : void SbxBasicFormater::ScanFormatString( double dNumber,
604 : const OUString& sFormatStrg, OUString& sReturnStrgFinal,
605 : sal_Bool bCreateSign )
606 : {
607 : short /*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft,
608 : nNoOfExponentDigits,nNoOfOptionalExponentDigits,
609 : nMultipleThousandSeparators;
610 : sal_Bool bPercent,bCurrency,bScientific,bGenerateThousandSeparator;
611 :
612 0 : OUStringBuffer sReturnStrg = OUStringBuffer();
613 :
614 : // analyse the format-string, i. e. determine the following values:
615 : /*
616 : - number of digits before decimal point
617 : - number of digits after decimal point
618 : - optional digits before decimal point
619 : - number of digits in the exponent
620 : - optional digits in the exponent
621 : - percent-character found?
622 : - () for negative leading sign?
623 : - exponetial-notation?
624 : - shall thousand-separators be generated?
625 : - is a percent-character being found? --> dNumber *= 100.0;
626 : - are there thousand-separators in a row?
627 : ,, or ,. --> dNumber /= 1000.0;
628 : - other errors? multiple decimal points, E's, etc.
629 : --> errors are simply ignored at the moment
630 : */
631 : AnalyseFormatString( sFormatStrg, nNoOfDigitsLeft, nNoOfDigitsRight,
632 : nNoOfOptionalDigitsLeft, nNoOfExponentDigits,
633 : nNoOfOptionalExponentDigits,
634 : bPercent, bCurrency, bScientific,
635 0 : bGenerateThousandSeparator, nMultipleThousandSeparators );
636 : // special handling for special characters
637 0 : if( bPercent )
638 : {
639 0 : dNumber *= 100.0;
640 : }
641 : // TODO: this condition (,, or ,.) is NOT Visual-Basic compatible!
642 : // Question: shall this stay here (requirements)?
643 0 : if( nMultipleThousandSeparators )
644 : {
645 0 : dNumber /= 1000.0;
646 : }
647 : double dExponent;
648 : short i,nLen;
649 : short nState,nDigitPos,nExponentPos,nMaxDigit,nMaxExponentDigit;
650 : sal_Bool bFirstDigit,bFirstExponentDigit,bFoundFirstDigit,
651 : bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative;
652 :
653 0 : bSignHappend = sal_False;
654 0 : bFoundFirstDigit = sal_False;
655 0 : bIsNegative = dNumber < 0.0;
656 0 : nLen = sFormatStrg.getLength();
657 0 : dExponent = get_number_of_digits( dNumber );
658 0 : nExponentPos = 0;
659 0 : nMaxExponentDigit = 0;
660 0 : nMaxDigit = (short)dExponent;
661 0 : bDigitPosNegative = false;
662 0 : if( bScientific )
663 : {
664 0 : dExponent = dExponent - (double)(nNoOfDigitsLeft-1);
665 0 : nDigitPos = nMaxDigit;
666 0 : nMaxExponentDigit = (short)get_number_of_digits( dExponent );
667 0 : nExponentPos = nNoOfExponentDigits - 1 - nNoOfOptionalExponentDigits;
668 : }
669 : else
670 : {
671 0 : nDigitPos = nNoOfDigitsLeft - 1; // counting starts at 0, 10^0
672 : // no exponent-data is needed here!
673 0 : bDigitPosNegative = (nDigitPos < 0);
674 : }
675 0 : bFirstDigit = sal_True;
676 0 : bFirstExponentDigit = sal_True;
677 0 : nState = 0; // 0 --> mantissa; 1 --> exponent
678 0 : bZeroSpaceOn = 0;
679 :
680 :
681 : #ifdef _with_sprintf
682 0 : InitScan( dNumber );
683 : #endif
684 : // scanning the format-string:
685 0 : sal_Unicode cForce = 0;
686 0 : for( i = 0; i < nLen; i++ )
687 : {
688 : sal_Unicode c;
689 0 : if( cForce )
690 : {
691 0 : c = cForce;
692 0 : cForce = 0;
693 : }
694 : else
695 : {
696 0 : c = sFormatStrg[ i ];
697 : }
698 0 : switch( c )
699 : {
700 : case '0':
701 : case '#':
702 0 : if( nState==0 )
703 : {
704 : // handling of the mantissa
705 0 : if( bFirstDigit )
706 : {
707 : // remark: at bCurrency the negative
708 : // leading sign shall be shown with ()
709 0 : if( bIsNegative && !bCreateSign && !bSignHappend )
710 : {
711 0 : bSignHappend = sal_True;
712 0 : sReturnStrg.append('-');
713 : }
714 : // output redundant positions, i. e. those which
715 : // are undocumented by the format-string
716 0 : if( nMaxDigit > nDigitPos )
717 : {
718 0 : for( short j = nMaxDigit; j > nDigitPos; j-- )
719 : {
720 : short nTempDigit;
721 : #ifdef _with_sprintf
722 0 : AppendDigit( sReturnStrg, nTempDigit = GetDigitAtPosScan( j, bFoundFirstDigit ) );
723 : #else
724 : AppendDigit( sReturnStrg, nTempDigit = GetDigitAtPos( dNumber, j, dNumber, bFoundFirstDigit ) );
725 : #endif
726 0 : if( nTempDigit!=_NO_DIGIT )
727 : {
728 0 : bFirstDigit = sal_False;
729 : }
730 0 : if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit >= nDigitPos ) && j > 0 && (j % 3 == 0) )
731 : {
732 0 : sReturnStrg.append(cThousandSep );
733 : }
734 : }
735 : }
736 : }
737 :
738 0 : if( nMaxDigit<nDigitPos && ( c=='0' || bZeroSpaceOn ) )
739 : {
740 0 : AppendDigit( sReturnStrg, 0 );
741 0 : bFirstDigit = sal_False;
742 0 : bZeroSpaceOn = 1;
743 : // Remark: in Visual-Basic the first 0 turns on the 0 for
744 : // all the following # (up to the decimal point),
745 : // this behaviour is simulated here with the flag.
746 0 : if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit >= nDigitPos ) && nDigitPos > 0 && (nDigitPos % 3 == 0) )
747 : {
748 0 : sReturnStrg.append(cThousandSep);
749 : }
750 : }
751 : else
752 : {
753 : short nTempDigit;
754 : #ifdef _with_sprintf
755 0 : AppendDigit( sReturnStrg, nTempDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit ) );
756 : #else
757 : AppendDigit( sReturnStrg, nTempDigit = GetDigitAtPos( dNumber, nDigitPos, dNumber, bFoundFirstDigit ) );
758 : #endif
759 :
760 0 : if( nTempDigit != _NO_DIGIT )
761 : {
762 0 : bFirstDigit = sal_False;
763 : }
764 0 : if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) )
765 : {
766 0 : sReturnStrg.append(cThousandSep);
767 : }
768 : }
769 0 : nDigitPos--;
770 : }
771 : else
772 : {
773 : // handling the exponent
774 0 : if( bFirstExponentDigit )
775 : {
776 : // leading sign has been given out at e/E already
777 0 : bFirstExponentDigit = sal_False;
778 0 : if( nMaxExponentDigit > nExponentPos )
779 : // output redundant positions, i. e. those which
780 : // are undocumented by the format-string
781 : {
782 0 : for( short j = nMaxExponentDigit; j > nExponentPos; j-- )
783 : {
784 : #ifdef _with_sprintf
785 0 : AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, j, bFoundFirstDigit ) );
786 : #else
787 : AppendDigit( sReturnStrg,GetDigitAtPos( dExponent, j, dExponent, bFoundFirstDigit ) );
788 : #endif
789 : }
790 : }
791 : }
792 :
793 0 : if( nMaxExponentDigit < nExponentPos && c=='0' )
794 : {
795 0 : AppendDigit( sReturnStrg, 0 );
796 : }
797 : else
798 : {
799 : #ifdef _with_sprintf
800 0 : AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, nExponentPos, bFoundFirstDigit ) );
801 : #else
802 : AppendDigit( sReturnStrg, GetDigitAtPos( dExponent, nExponentPos, dExponent, bFoundFirstDigit ) );
803 : #endif
804 : }
805 0 : nExponentPos--;
806 : }
807 0 : break;
808 : case '.':
809 0 : if( bDigitPosNegative ) // #i13821: If no digits before .
810 : {
811 0 : bDigitPosNegative = false;
812 0 : nDigitPos = 0;
813 0 : cForce = '#';
814 0 : i-=2;
815 0 : break;
816 : }
817 0 : sReturnStrg.append(cDecPoint);
818 0 : break;
819 : case '%':
820 : // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
821 0 : ParseBack( sReturnStrg, sFormatStrg, i-1 );
822 0 : sReturnStrg.insert(0,'%');
823 0 : break;
824 : case 'e':
825 : case 'E':
826 : // does mantissa have to be rounded, before the exponent is displayed?
827 : {
828 : // is there a mantissa at all?
829 0 : if( bFirstDigit )
830 : {
831 : // apparently not, i. e. invalid format string, e. g. E000.00
832 : // so ignore these e and E characters
833 : // maybe output an error (like in Visual Basic)?
834 :
835 : // #i13821: VB 6 behaviour
836 0 : sReturnStrg.append(c);
837 : break;
838 : }
839 :
840 0 : sal_Bool bOverflow = sal_False;
841 : #ifdef _with_sprintf
842 0 : short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
843 : #else
844 : short nNextDigit = GetDigitAtPos( dNumber, nDigitPos, dNumber, bFoundFirstDigit );
845 : #endif
846 0 : if( nNextDigit>=5 )
847 : {
848 0 : StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1, bOverflow );
849 : }
850 0 : if( bOverflow )
851 : {
852 : // a leading 9 has been rounded
853 0 : LeftShiftDecimalPoint( sReturnStrg );
854 0 : sReturnStrg[sReturnStrg.getLength() - 1] = 0;
855 0 : dExponent += 1.0;
856 : }
857 : // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
858 0 : ParseBack( sReturnStrg, sFormatStrg, i-1 );
859 : }
860 : // change the scanner's condition
861 0 : nState++;
862 : // output exponent character
863 0 : sReturnStrg.append(c);
864 : // i++; // MANIPULATION of the loop-variable!
865 0 : c = sFormatStrg[ ++i ];
866 : // output leading sign / exponent
867 0 : if( c != 0 )
868 : {
869 0 : if( c == '-' )
870 : {
871 0 : if( dExponent < 0.0 )
872 : {
873 0 : sReturnStrg.append('-');
874 : }
875 : }
876 0 : else if( c == '+' )
877 : {
878 0 : if( dExponent < 0.0 )
879 : {
880 0 : sReturnStrg.append('-');
881 : }
882 : else
883 : {
884 0 : sReturnStrg.append('+');
885 : }
886 : }
887 : }
888 0 : break;
889 : case ',':
890 0 : break;
891 : case ';':
892 0 : break;
893 : case '(':
894 : case ')':
895 : // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
896 0 : ParseBack( sReturnStrg, sFormatStrg, i-1 );
897 0 : if( bIsNegative )
898 : {
899 0 : sReturnStrg.append(c);
900 : }
901 0 : break;
902 : case '$':
903 : // append the string for the currency:
904 0 : sReturnStrg.append(sCurrencyStrg);
905 0 : break;
906 : case ' ':
907 : case '-':
908 : case '+':
909 0 : ParseBack( sReturnStrg, sFormatStrg, i-1 );
910 0 : sReturnStrg.append(c);
911 0 : break;
912 : case '\\':
913 0 : ParseBack( sReturnStrg, sFormatStrg, i-1 );
914 : // special character found, output next
915 : // character directly (if existing)
916 0 : c = sFormatStrg[ ++i ];
917 0 : if( c!=0 )
918 : {
919 0 : sReturnStrg.append(c);
920 : }
921 0 : break;
922 : case CREATE_1000SEP_CHAR:
923 : // ignore here, action has already been
924 : // executed in AnalyseFormatString
925 0 : break;
926 : default:
927 : // output characters and digits, too (like in Visual-Basic)
928 0 : if( ( c>='a' && c<='z' ) ||
929 : ( c>='A' && c<='Z' ) ||
930 : ( c>='1' && c<='9' ) )
931 : {
932 0 : sReturnStrg.append(c);
933 : }
934 : }
935 : }
936 :
937 : // scan completed - rounding necessary?
938 0 : if( !bScientific )
939 : {
940 : #ifdef _with_sprintf
941 0 : short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
942 : #else
943 : short nNextDigit = GetDigitAtPos( dNumber, nDigitPos, dNumber, bFoundFirstDigit );
944 : #endif
945 0 : if( nNextDigit>=5 )
946 : {
947 0 : StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1 );
948 : }
949 : }
950 :
951 0 : if( nNoOfDigitsRight>0 )
952 : {
953 0 : ParseBack( sReturnStrg, sFormatStrg, sFormatStrg.getLength()-1 );
954 : }
955 0 : sReturnStrgFinal = sReturnStrg.makeStringAndClear();
956 0 : }
957 :
958 0 : OUString SbxBasicFormater::BasicFormatNull( OUString sFormatStrg )
959 : {
960 : sal_Bool bNullFormatFound;
961 0 : OUString sNullFormatStrg = GetNullFormatString( sFormatStrg, bNullFormatFound );
962 :
963 0 : if( bNullFormatFound )
964 : {
965 0 : return sNullFormatStrg;
966 : }
967 0 : OUString aRetStr;
968 0 : aRetStr = rtl::OUString::createFromAscii( "null" );
969 0 : return aRetStr;
970 : }
971 :
972 0 : OUString SbxBasicFormater::BasicFormat( double dNumber, OUString sFormatStrg )
973 : {
974 : sal_Bool bPosFormatFound,bNegFormatFound,b0FormatFound;
975 :
976 : // analyse format-string concerning predefined formats:
977 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_GENERALNUMBER ) )
978 : {
979 0 : sFormatStrg = rtl::OUString::createFromAscii( GENERALNUMBER_FORMAT );
980 : }
981 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_CURRENCY ) )
982 : {
983 0 : sFormatStrg = sCurrencyFormatStrg; // old: CURRENCY_FORMAT;
984 : }
985 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_FIXED ) )
986 : {
987 0 : sFormatStrg = rtl::OUString::createFromAscii( FIXED_FORMAT );
988 : }
989 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_STANDARD ) )
990 : {
991 0 : sFormatStrg = rtl::OUString::createFromAscii( STANDARD_FORMAT );
992 : }
993 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_PERCENT ) )
994 : {
995 0 : sFormatStrg = rtl::OUString::createFromAscii( PERCENT_FORMAT );
996 : }
997 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_SCIENTIFIC ) )
998 : {
999 0 : sFormatStrg = rtl::OUString::createFromAscii( SCIENTIFIC_FORMAT );
1000 : }
1001 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_YESNO ) )
1002 : {
1003 0 : return ( dNumber==0.0 ) ? sNoStrg : sYesStrg ;
1004 : }
1005 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_TRUEFALSE ) )
1006 : {
1007 0 : return ( dNumber==0.0 ) ? sFalseStrg : sTrueStrg ;
1008 : }
1009 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_ONOFF ) )
1010 : {
1011 0 : return ( dNumber==0.0 ) ? sOffStrg : sOnStrg ;
1012 : }
1013 :
1014 : // analyse format-string concerning ';', i. e. format-strings for
1015 : // positive-, negative- and 0-values
1016 0 : OUString sPosFormatStrg = GetPosFormatString( sFormatStrg, bPosFormatFound );
1017 0 : OUString sNegFormatStrg = GetNegFormatString( sFormatStrg, bNegFormatFound );
1018 0 : OUString s0FormatStrg = Get0FormatString( sFormatStrg, b0FormatFound );
1019 :
1020 0 : OUString sReturnStrg;
1021 0 : OUString sTempStrg;
1022 :
1023 0 : if( dNumber==0.0 )
1024 : {
1025 0 : sTempStrg = sFormatStrg;
1026 0 : if( b0FormatFound )
1027 : {
1028 0 : if( s0FormatStrg.isEmpty() && bPosFormatFound )
1029 : {
1030 0 : sTempStrg = sPosFormatStrg;
1031 : }
1032 : else
1033 : {
1034 0 : sTempStrg = s0FormatStrg;
1035 : }
1036 : }
1037 0 : else if( bPosFormatFound )
1038 : {
1039 0 : sTempStrg = sPosFormatStrg;
1040 : }
1041 0 : ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/sal_False );
1042 : }
1043 : else
1044 : {
1045 0 : if( dNumber<0.0 )
1046 : {
1047 0 : if( bNegFormatFound )
1048 : {
1049 0 : if( sNegFormatStrg.isEmpty() && bPosFormatFound )
1050 : {
1051 0 : sTempStrg = "-";
1052 0 : sTempStrg += sPosFormatStrg;
1053 : }
1054 : else
1055 : {
1056 0 : sTempStrg = sNegFormatStrg;
1057 : }
1058 : }
1059 : else
1060 : {
1061 0 : sTempStrg = sFormatStrg;
1062 : }
1063 : // if NO format-string especially for negative
1064 : // values is given, output the leading sign
1065 0 : ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/bNegFormatFound/*sNegFormatStrg!=EMPTYFORMATSTRING*/ );
1066 : }
1067 : else // if( dNumber>0.0 )
1068 : {
1069 : ScanFormatString( dNumber,
1070 : (/*sPosFormatStrg!=EMPTYFORMATSTRING*/bPosFormatFound ? sPosFormatStrg : sFormatStrg),
1071 0 : sReturnStrg,/*bCreateSign=*/sal_False );
1072 : }
1073 : }
1074 0 : return sReturnStrg;
1075 : }
1076 :
1077 0 : sal_Bool SbxBasicFormater::isBasicFormat( OUString sFormatStrg )
1078 : {
1079 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_GENERALNUMBER ) )
1080 : {
1081 0 : return sal_True;
1082 : }
1083 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_CURRENCY ) )
1084 : {
1085 0 : return sal_True;
1086 : }
1087 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_FIXED ) )
1088 : {
1089 0 : return sal_True;
1090 : }
1091 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_STANDARD ) )
1092 : {
1093 0 : return sal_True;
1094 : }
1095 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_PERCENT ) )
1096 : {
1097 0 : return sal_True;
1098 : }
1099 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_SCIENTIFIC ) )
1100 : {
1101 0 : return sal_True;
1102 : }
1103 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_YESNO ) )
1104 : {
1105 0 : return sal_True;
1106 : }
1107 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_TRUEFALSE ) )
1108 : {
1109 0 : return sal_True;
1110 : }
1111 0 : if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_ONOFF ) )
1112 : {
1113 0 : return sal_True;
1114 : }
1115 0 : return sal_False;
1116 : }
1117 :
1118 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|