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