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