1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
21 #include <config_fuzzers.h>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/string.hxx>
32 #include <docstat.hxx>
34 #include <IDocumentFieldsAccess.hxx>
35 #include <IDocumentStatistics.hxx>
36 #include <editeng/langitem.hxx>
38 #include <hintids.hxx>
39 #include <o3tl/temporary.hxx>
40 #include <osl/diagnose.h>
41 #include <rtl/math.hxx>
42 #include <shellres.hxx>
43 #include <svl/numformat.hxx>
44 #include <svl/languageoptions.hxx>
45 #include <swmodule.hxx>
46 #include <swtypes.hxx>
47 #include <unotools/charclass.hxx>
48 #include <unotools/localedatawrapper.hxx>
49 #include <unotools/useroptions.hxx>
53 #include <com/sun/star/i18n/KParseTokens.hpp>
54 #include <com/sun/star/i18n/KParseType.hpp>
56 using namespace ::com::sun::star
;
58 const char sCalc_Add
[] = "add";
59 const char sCalc_Sub
[] = "sub";
60 const char sCalc_Mul
[] = "mul";
61 const char sCalc_Div
[] = "div";
62 const char sCalc_Phd
[] = "phd";
63 const char sCalc_Sqrt
[] = "sqrt";
64 const char sCalc_Pow
[] = "pow";
65 const char sCalc_Or
[] = "or";
66 const char sCalc_Xor
[] = "xor";
67 const char sCalc_And
[] = "and";
68 const char sCalc_Not
[] = "not";
69 const char sCalc_Eq
[] = "eq";
70 const char sCalc_Neq
[] = "neq";
71 const char sCalc_Leq
[] = "leq";
72 const char sCalc_Geq
[] = "geq";
73 const char sCalc_L
[] = "l";
74 const char sCalc_G
[] = "g";
75 const char sCalc_Sum
[] = "sum";
76 const char sCalc_Mean
[] = "mean";
77 const char sCalc_Min
[] = "min";
78 const char sCalc_Max
[] = "max";
79 const char sCalc_Sin
[] = "sin";
80 const char sCalc_Cos
[] = "cos";
81 const char sCalc_Tan
[] = "tan";
82 const char sCalc_Asin
[] = "asin";
83 const char sCalc_Acos
[] = "acos";
84 const char sCalc_Atan
[] = "atan";
85 const char sCalc_Round
[]= "round";
86 const char sCalc_Date
[] = "date";
87 const char sCalc_Product
[] = "product";
88 const char sCalc_Average
[] = "average";
89 const char sCalc_Count
[]= "count";
90 const char sCalc_Sign
[] = "sign";
91 const char sCalc_Abs
[] = "abs";
92 const char sCalc_Int
[] = "int";
94 // ATTENTION: sorted list of all operators
99 const OUString
* pUName
;
104 CalcOp
const aOpTable
[] = {
105 /* ABS */ {{sCalc_Abs
}, CALC_ABS
}, // Abs (since LibreOffice 7.1)
106 /* ACOS */ {{sCalc_Acos
}, CALC_ACOS
}, // Arc cosine
107 /* ADD */ {{sCalc_Add
}, CALC_PLUS
}, // Addition
108 /* AND */ {{sCalc_And
}, CALC_AND
}, // log. AND
109 /* ASIN */ {{sCalc_Asin
}, CALC_ASIN
}, // Arc sine
110 /* ATAN */ {{sCalc_Atan
}, CALC_ATAN
}, // Arc tangent
111 /* AVERAGE */ {{sCalc_Average
}, CALC_AVERAGE
}, // Average (since LibreOffice 7.1)
112 /* COS */ {{sCalc_Cos
}, CALC_COS
}, // Cosine
113 /* COUNT */ {{sCalc_Count
}, CALC_COUNT
}, // Count (since LibreOffice 7.1)
114 /* DATE */ {{sCalc_Date
}, CALC_DATE
}, // Date
115 /* DIV */ {{sCalc_Div
}, CALC_DIV
}, // Division
116 /* EQ */ {{sCalc_Eq
}, CALC_EQ
}, // Equality
117 /* G */ {{sCalc_G
}, CALC_GRE
}, // Greater than
118 /* GEQ */ {{sCalc_Geq
}, CALC_GEQ
}, // Greater or equal
119 /* INT */ {{sCalc_Int
}, CALC_INT
}, // Int (since LibreOffice 7.4)
120 /* L */ {{sCalc_L
}, CALC_LES
}, // Less than
121 /* LEQ */ {{sCalc_Leq
}, CALC_LEQ
}, // Less or equal
122 /* MAX */ {{sCalc_Max
}, CALC_MAX
}, // Maximum value
123 /* MEAN */ {{sCalc_Mean
}, CALC_MEAN
}, // Mean
124 /* MIN */ {{sCalc_Min
}, CALC_MIN
}, // Minimum value
125 /* MUL */ {{sCalc_Mul
}, CALC_MUL
}, // Multiplication
126 /* NEQ */ {{sCalc_Neq
}, CALC_NEQ
}, // Not equal
127 /* NOT */ {{sCalc_Not
}, CALC_NOT
}, // log. NOT
128 /* OR */ {{sCalc_Or
}, CALC_OR
}, // log. OR
129 /* PHD */ {{sCalc_Phd
}, CALC_PHD
}, // Percentage
130 /* POW */ {{sCalc_Pow
}, CALC_POW
}, // Exponentiation
131 /* PRODUCT */ {{sCalc_Product
}, CALC_PRODUCT
}, // Product (since LibreOffice 7.1)
132 /* ROUND */ {{sCalc_Round
}, CALC_ROUND
}, // Rounding
133 /* SIGN */ {{sCalc_Sign
}, CALC_SIGN
}, // Sign (since LibreOffice 7.1)
134 /* SIN */ {{sCalc_Sin
}, CALC_SIN
}, // Sine
135 /* SQRT */ {{sCalc_Sqrt
}, CALC_SQRT
}, // Square root
136 /* SUB */ {{sCalc_Sub
}, CALC_MINUS
}, // Subtraction
137 /* SUM */ {{sCalc_Sum
}, CALC_SUM
}, // Sum
138 /* TAN */ {{sCalc_Tan
}, CALC_TAN
}, // Tangent
139 /* XOR */ {{sCalc_Xor
}, CALC_XOR
} // log. XOR
142 double const nRoundVal
[] = {
143 5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6,
144 0.5e-7, 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,
148 // First character may be any alphabetic or underscore.
149 const sal_Int32 coStartFlags
=
150 i18n::KParseTokens::ANY_LETTER_OR_NUMBER
|
151 i18n::KParseTokens::ASC_UNDERSCORE
|
152 i18n::KParseTokens::IGNORE_LEADING_WS
;
154 // Continuing characters may be any alphanumeric, underscore, or dot.
155 const sal_Int32 coContFlags
=
156 (coStartFlags
| i18n::KParseTokens::ASC_DOT
| i18n::KParseTokens::GROUP_SEPARATOR_IN_NUMBER
)
157 & ~i18n::KParseTokens::IGNORE_LEADING_WS
;
160 static int OperatorCompare( const void *pFirst
, const void *pSecond
)
163 if( CALC_NAME
== static_cast<const CalcOp
*>(pFirst
)->eOp
)
165 if( CALC_NAME
== static_cast<const CalcOp
*>(pSecond
)->eOp
)
166 nRet
= static_cast<const CalcOp
*>(pFirst
)->pUName
->compareTo(
167 *static_cast<const CalcOp
*>(pSecond
)->pUName
);
169 nRet
= static_cast<const CalcOp
*>(pFirst
)->pUName
->compareToAscii(
170 static_cast<const CalcOp
*>(pSecond
)->pName
);
174 if( CALC_NAME
== static_cast<const CalcOp
*>(pSecond
)->eOp
)
175 nRet
= -1 * static_cast<const CalcOp
*>(pSecond
)->pUName
->compareToAscii(
176 static_cast<const CalcOp
*>(pFirst
)->pName
);
178 nRet
= strcmp( static_cast<const CalcOp
*>(pFirst
)->pName
,
179 static_cast<const CalcOp
*>(pSecond
)->pName
);
185 CalcOp
* FindOperator( const OUString
& rSrch
)
188 aSrch
.pUName
= &rSrch
;
189 aSrch
.eOp
= CALC_NAME
;
191 return static_cast<CalcOp
*>(bsearch( static_cast<void*>(&aSrch
),
192 static_cast<void const *>(aOpTable
),
193 SAL_N_ELEMENTS( aOpTable
),
199 LanguageType
SwCalc::GetDocAppScriptLang( SwDoc
const & rDoc
)
201 TypedWhichId
<SvxLanguageItem
> nWhich
=
202 GetWhichOfScript( RES_CHRATR_LANGUAGE
,
203 SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ));
204 return rDoc
.GetDefault(nWhich
).GetLanguage();
207 static double lcl_ConvertToDateValue( SwDoc
& rDoc
, sal_Int32 nDate
)
210 SvNumberFormatter
* pFormatter
= rDoc
.GetNumberFormatter();
213 const Date
& rNull
= pFormatter
->GetNullDate();
214 Date
aDate( nDate
>> 24, (nDate
& 0x00FF0000) >> 16, nDate
& 0x0000FFFF );
215 nRet
= aDate
- rNull
;
220 SwCalc::SwCalc( SwDoc
& rD
)
222 , m_aErrExpr( OUString(), SwSbxValue(), nullptr )
225 , m_pCharClass( &GetAppCharClass() )
227 , m_bHasNumber( false )
228 , m_eCurrOper( CALC_NAME
)
229 , m_eCurrListOper( CALC_NAME
)
230 , m_eError( SwCalcError::NONE
)
232 m_aErrExpr
.aStr
= "~C_ERR~";
233 LanguageType eLang
= GetDocAppScriptLang( m_rDoc
);
234 LanguageTag
aLanguageTag( eLang
);
236 if( eLang
!= m_pCharClass
->getLanguageTag().getLanguageType() )
238 m_pCharClass
= new CharClass( ::comphelper::getProcessComponentContext(), aLanguageTag
);
240 m_xLocaleDataWrapper
.reset(new LocaleDataWrapper( std::move(aLanguageTag
) ));
242 m_sCurrSym
= comphelper::string::strip(m_xLocaleDataWrapper
->getCurrSymbol(), ' ');
243 m_sCurrSym
= m_pCharClass
->lowercase( m_sCurrSym
);
250 sNType4
[] = "tables",
258 sNType11
[] = "user_firstname" ,
259 sNType12
[] = "user_lastname" ,
260 sNType13
[] = "user_initials" ,
261 sNType14
[] = "user_company" ,
262 sNType15
[] = "user_street" ,
263 sNType16
[] = "user_country" ,
264 sNType17
[] = "user_zipcode" ,
265 sNType18
[] = "user_city" ,
266 sNType19
[] = "user_title" ,
267 sNType20
[] = "user_position" ,
268 sNType21
[] = "user_tel_work" ,
269 sNType22
[] = "user_tel_home" ,
270 sNType23
[] = "user_fax" ,
271 sNType24
[] = "user_email" ,
272 sNType25
[] = "user_state" ,
275 static const char* const sNTypeTab
[ 27 ] =
277 sNType0
, sNType1
, sNType2
, sNType3
, sNType4
, sNType5
,
278 sNType6
, sNType7
, sNType8
, sNType9
, sNType10
, sNType11
,
279 sNType12
, sNType13
, sNType14
, sNType15
, sNType16
, sNType17
,
280 sNType18
, sNType19
, sNType20
, sNType21
, sNType22
, sNType23
,
283 // those have two HashIds
286 static sal_uInt16
const aHashValue
[ 27 ] =
288 34, 38, 43, 7, 18, 32, 22, 29, 30, 33, 3,
289 28, 24, 40, 9, 11, 26, 45, 4, 23, 36, 44, 19, 5, 1,
290 // those have two HashIds
293 static UserOptToken
const aAdrToken
[ 12 ] =
295 UserOptToken::Company
, UserOptToken::Street
, UserOptToken::Country
, UserOptToken::Zip
,
296 UserOptToken::City
, UserOptToken::Title
, UserOptToken::Position
, UserOptToken::TelephoneWork
,
297 UserOptToken::TelephoneHome
, UserOptToken::Fax
, UserOptToken::Email
, UserOptToken::State
300 static sal_uInt16
SwDocStat::* const aDocStat1
[ 3 ] =
302 &SwDocStat::nTable
, &SwDocStat::nGrf
, &SwDocStat::nOLE
304 static sal_uLong
SwDocStat::* const aDocStat2
[ 4 ] =
306 &SwDocStat::nPage
, &SwDocStat::nPara
,
307 &SwDocStat::nWord
, &SwDocStat::nChar
311 #error Did you adjust all hash values?
314 const SwDocStat
& rDocStat
= m_rDoc
.getIDocumentStatistics().GetDocStat();
320 for( n
= 0; n
< 25; ++n
)
322 sTmpStr
= OUString::createFromAscii(sNTypeTab
[n
]);
323 m_aVarTable
[ aHashValue
[ n
] ].reset( new SwCalcExp( sTmpStr
, nVal
, nullptr ) );
326 m_aVarTable
[ aHashValue
[ 0 ] ]->nValue
.PutBool( false );
327 m_aVarTable
[ aHashValue
[ 1 ] ]->nValue
.PutBool( true );
328 m_aVarTable
[ aHashValue
[ 2 ] ]->nValue
.PutDouble( M_PI
);
329 m_aVarTable
[ aHashValue
[ 3 ] ]->nValue
.PutDouble( M_E
);
331 for( n
= 0; n
< 3; ++n
)
332 m_aVarTable
[ aHashValue
[ n
+ 4 ] ]->nValue
.PutLong( rDocStat
.*aDocStat1
[ n
] );
333 for( n
= 0; n
< 4; ++n
)
334 m_aVarTable
[ aHashValue
[ n
+ 7 ] ]->nValue
.PutLong( rDocStat
.*aDocStat2
[ n
] );
336 SvtUserOptions
& rUserOptions
= SW_MOD()->GetUserOptions();
338 m_aVarTable
[ aHashValue
[ 11 ] ]->nValue
.PutString( rUserOptions
.GetFirstName() );
339 m_aVarTable
[ aHashValue
[ 12 ] ]->nValue
.PutString( rUserOptions
.GetLastName() );
340 m_aVarTable
[ aHashValue
[ 13 ] ]->nValue
.PutString( rUserOptions
.GetID() );
342 for( n
= 0; n
< 11; ++n
)
343 m_aVarTable
[ aHashValue
[ n
+ 14 ] ]->nValue
.PutString(
344 rUserOptions
.GetToken( aAdrToken
[ n
] ));
346 nVal
.PutString( rUserOptions
.GetToken( aAdrToken
[ 11 ] ));
347 sTmpStr
= OUString::createFromAscii(sNTypeTab
[25]);
348 m_aVarTable
[ aHashValue
[ 25 ] ]->pNext
.reset( new SwCalcExp( sTmpStr
, nVal
, nullptr ) );
352 void SwCalc::ImplDestroy()
354 if( m_pCharClass
!= &GetAppCharClass() )
360 suppress_fun_call_w_exception(ImplDestroy());
363 SwSbxValue
SwCalc::Calculate( const OUString
& rStr
)
365 m_eError
= SwCalcError::NONE
;
372 m_eCurrListOper
= CALC_PLUS
; // default: sum
379 m_eCurrOper
= GetToken();
380 if (m_eCurrOper
== CALC_ENDCALC
|| m_eError
!= SwCalcError::NONE
)
385 if( m_eError
!= SwCalcError::NONE
)
386 nResult
.PutDouble( DBL_MAX
);
391 OUString
SwCalc::GetStrResult( const SwSbxValue
& rVal
)
393 if( !rVal
.IsDouble() )
395 return rVal
.GetOUString();
397 return GetStrResult( rVal
.GetDouble() );
400 OUString
SwCalc::GetStrResult( double nValue
)
402 if( nValue
>= DBL_MAX
)
405 case SwCalcError::Syntax
: return SwViewShell::GetShellRes()->aCalc_Syntax
;
406 case SwCalcError::DivByZero
: return SwViewShell::GetShellRes()->aCalc_ZeroDiv
;
407 case SwCalcError::FaultyBrackets
: return SwViewShell::GetShellRes()->aCalc_Brack
;
408 case SwCalcError::OverflowInPower
: return SwViewShell::GetShellRes()->aCalc_Pow
;
409 case SwCalcError::Overflow
: return SwViewShell::GetShellRes()->aCalc_Overflow
;
410 default : return SwViewShell::GetShellRes()->aCalc_Default
;
413 const sal_Int32 nDecPlaces
= 15;
414 OUString
aRetStr( ::rtl::math::doubleToUString(
416 rtl_math_StringFormat_Automatic
,
418 m_xLocaleDataWrapper
->getNumDecimalSep()[0],
423 SwCalcExp
* SwCalc::VarInsert( const OUString
&rStr
)
425 OUString aStr
= m_pCharClass
->lowercase( rStr
);
426 return VarLook( aStr
, true );
429 SwCalcExp
* SwCalc::VarLook( const OUString
& rStr
, bool bIns
)
431 m_aErrExpr
.nValue
.SetVoidValue(false);
434 OUString aStr
= m_pCharClass
->lowercase( rStr
);
436 SwCalcExp
* pFnd
= m_aVarTable
.Find(aStr
, &ii
);
441 SwHashTable
<SwCalcFieldType
> const & rDocTable
= m_rDoc
.getIDocumentFieldsAccess().GetUpdateFields().GetFieldTypeTable();
442 for( SwHash
* pEntry
= rDocTable
[ii
].get(); pEntry
; pEntry
= pEntry
->pNext
.get() )
444 if( aStr
== pEntry
->aStr
)
447 pFnd
= new SwCalcExp( aStr
, SwSbxValue(),
448 static_cast<SwCalcFieldType
*>(pEntry
)->pFieldType
);
449 pFnd
->pNext
= std::move( m_aVarTable
[ii
] );
450 m_aVarTable
[ii
].reset(pFnd
);
458 if( pFnd
->pFieldType
&& pFnd
->pFieldType
->Which() == SwFieldIds::User
)
460 SwUserFieldType
* pUField
= const_cast<SwUserFieldType
*>(static_cast<const SwUserFieldType
*>(pFnd
->pFieldType
));
461 if( nsSwGetSetExpType::GSE_STRING
& pUField
->GetType() )
463 pFnd
->nValue
.PutString( pUField
->GetContent() );
465 else if( !pUField
->IsValid() )
467 // Save the current values...
468 sal_uInt16 nListPor
= m_nListPor
;
469 bool bHasNumber
= m_bHasNumber
;
470 SwSbxValue nLastLeft
= m_nLastLeft
;
471 SwSbxValue nNumberValue
= m_nNumberValue
;
472 sal_Int32 nCommandPos
= m_nCommandPos
;
473 SwCalcOper eCurrOper
= m_eCurrOper
;
474 SwCalcOper eCurrListOper
= m_eCurrListOper
;
475 OUString sCurrCommand
= m_sCommand
;
477 pFnd
->nValue
.PutDouble( pUField
->GetValue( *this ) );
479 // ...and write them back.
480 m_nListPor
= nListPor
;
481 m_bHasNumber
= bHasNumber
;
482 m_nLastLeft
= nLastLeft
;
483 m_nNumberValue
= nNumberValue
;
484 m_nCommandPos
= nCommandPos
;
485 m_eCurrOper
= eCurrOper
;
486 m_eCurrListOper
= eCurrListOper
;
487 m_sCommand
= sCurrCommand
;
491 pFnd
->nValue
.PutDouble( pUField
->GetValue() );
494 else if ( !pFnd
->pFieldType
&& pFnd
->nValue
.IsDBvalue() )
496 if ( pFnd
->nValue
.IsString() )
497 m_aErrExpr
.nValue
.PutString( pFnd
->nValue
.GetOUString() );
498 else if ( pFnd
->nValue
.IsDouble() )
499 m_aErrExpr
.nValue
.PutDouble( pFnd
->nValue
.GetDouble() );
505 // At this point the "real" case variable has to be used
506 OUString
const sTmpName( ::ReplacePoint(rStr
) );
510 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
511 SwDBManager
*pMgr
= m_rDoc
.GetDBManager();
512 OUString
sDBName(GetDBName( sTmpName
));
513 OUString
sSourceName(sDBName
.getToken(0, DB_DELIM
));
514 OUString
sTableName(sDBName
.getToken(0, ';').getToken(1, DB_DELIM
));
515 if( pMgr
&& !sSourceName
.isEmpty() && !sTableName
.isEmpty() &&
516 pMgr
->OpenDataSource(sSourceName
, sTableName
))
518 OUString
sColumnName( GetColumnName( sTmpName
));
519 OSL_ENSURE(!sColumnName
.isEmpty(), "Missing DB column name");
521 OUString
sDBNum( SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber
) );
522 sDBNum
= m_pCharClass
->lowercase(sDBNum
);
524 // Initialize again because this doesn't happen in docfld anymore for
525 // elements != SwFieldIds::Database. E.g. if there is an expression field before
526 // a DB_Field in a document.
527 const sal_uInt32 nTmpRec
= pMgr
->GetSelectedRecordId(sSourceName
, sTableName
);
528 VarChange(sDBNum
, nTmpRec
);
530 if( sDBNum
.equalsIgnoreAsciiCase(sColumnName
) )
532 m_aErrExpr
.nValue
.PutULong(nTmpRec
);
537 double nNumber
= DBL_MAX
;
539 LanguageType nLang
= m_xLocaleDataWrapper
->getLanguageTag().getLanguageType();
540 if(pMgr
->GetColumnCnt( sSourceName
, sTableName
, sColumnName
,
541 nTmpRec
, nLang
, sResult
, &nNumber
))
543 if (nNumber
!= DBL_MAX
)
544 m_aErrExpr
.nValue
.PutDouble( nNumber
);
546 m_aErrExpr
.nValue
.PutString( sResult
);
554 //data source was not available - set return to "NoValue"
555 m_aErrExpr
.nValue
.SetVoidValue(true);
561 SwCalcExp
* pNewExp
= new SwCalcExp( aStr
, SwSbxValue(), nullptr );
562 pNewExp
->pNext
= std::move( m_aVarTable
[ ii
] );
563 m_aVarTable
[ ii
].reset( pNewExp
);
565 OUString
sColumnName( GetColumnName( sTmpName
));
566 OSL_ENSURE( !sColumnName
.isEmpty(), "Missing DB column name" );
567 if( sColumnName
.equalsIgnoreAsciiCase(
568 SwFieldType::GetTypeStr( SwFieldTypesEnum::DatabaseSetNumber
) ))
570 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
571 SwDBManager
*pMgr
= m_rDoc
.GetDBManager();
572 OUString
sDBName(GetDBName( sTmpName
));
573 OUString
sSourceName(sDBName
.getToken(0, DB_DELIM
));
574 OUString
sTableName(sDBName
.getToken(0, ';').getToken(1, DB_DELIM
));
575 if( pMgr
&& !sSourceName
.isEmpty() && !sTableName
.isEmpty() &&
576 pMgr
->OpenDataSource(sSourceName
, sTableName
) &&
579 pNewExp
->nValue
.PutULong( pMgr
->GetSelectedRecordId(sSourceName
, sTableName
));
584 pNewExp
->nValue
.SetVoidValue(true);
591 void SwCalc::VarChange( const OUString
& rStr
, double nValue
)
593 SwSbxValue
aVal( nValue
);
594 VarChange( rStr
, aVal
);
597 void SwCalc::VarChange( const OUString
& rStr
, const SwSbxValue
& rValue
)
599 OUString aStr
= m_pCharClass
->lowercase( rStr
);
602 SwCalcExp
* pFnd
= m_aVarTable
.Find( aStr
, &nPos
);
606 pFnd
= new SwCalcExp( aStr
, rValue
, nullptr );
607 pFnd
->pNext
= std::move( m_aVarTable
[ nPos
] );
608 m_aVarTable
[ nPos
].reset( pFnd
);
612 pFnd
->nValue
= rValue
;
616 bool SwCalc::Push( const SwUserFieldType
* pUserFieldType
)
618 if( m_aRekurStack
.end() != std::find(m_aRekurStack
.begin(), m_aRekurStack
.end(), pUserFieldType
) )
621 m_aRekurStack
.push_back( pUserFieldType
);
627 OSL_ENSURE( m_aRekurStack
.size(), "SwCalc: Pop on an invalid pointer" );
629 m_aRekurStack
.pop_back();
632 const CharClass
* SwCalc::GetCharClass() const
637 void SwCalc::SetCharClass(const LanguageTag
& rLanguageTag
)
639 m_pCharClass
= new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag
);
642 SwCalcOper
SwCalc::GetToken()
644 if( m_nCommandPos
>= m_sCommand
.getLength() )
646 m_eCurrOper
= CALC_ENDCALC
;
650 using namespace ::com::sun::star::i18n
;
653 ParseResult aRes
= m_pCharClass
->parseAnyToken( m_sCommand
, m_nCommandPos
,
654 coStartFlags
, OUString(),
655 coContFlags
, OUString());
657 bool bSetError
= true;
658 sal_Int32 nRealStt
= m_nCommandPos
+ aRes
.LeadingWhiteSpace
;
659 if( aRes
.TokenType
& (KParseType::ASC_NUMBER
| KParseType::UNI_NUMBER
) )
661 m_nNumberValue
.PutDouble( aRes
.Value
);
662 m_eCurrOper
= CALC_NUMBER
;
665 else if( aRes
.TokenType
& KParseType::IDENTNAME
)
667 OUString
aName( m_sCommand
.copy( nRealStt
,
668 aRes
.EndPos
- nRealStt
) );
669 //#101436#: The variable may contain a database name. It must not be
670 // converted to lower case! Instead all further comparisons must be
671 // done case-insensitive
672 OUString sLowerCaseName
= m_pCharClass
->lowercase( aName
);
673 // catch currency symbol
674 if( sLowerCaseName
== m_sCurrSym
)
676 m_nCommandPos
= aRes
.EndPos
;
677 return GetToken(); // call again
681 CalcOp
* pFnd
= ::FindOperator( sLowerCaseName
);
684 m_eCurrOper
= pFnd
->eOp
;
685 switch( m_eCurrOper
)
691 m_eCurrListOper
= CALC_PLUS
;
694 m_eCurrListOper
= CALC_MIN_IN
;
697 m_eCurrListOper
= CALC_MAX_IN
;
700 m_eCurrListOper
= CALC_MONTH
;
703 m_eCurrListOper
= CALC_MUL
;
708 m_nCommandPos
= aRes
.EndPos
;
712 m_eCurrOper
= CALC_NAME
;
715 else if ( aRes
.TokenType
& KParseType::DOUBLE_QUOTE_STRING
)
717 m_nNumberValue
.PutString( aRes
.DequotedNameOrString
);
718 m_eCurrOper
= CALC_NUMBER
;
721 else if( aRes
.TokenType
& KParseType::ONE_SINGLE_CHAR
)
723 std::u16string_view
aName( m_sCommand
.subView( nRealStt
,
724 aRes
.EndPos
- nRealStt
));
725 if( 1 == aName
.size() )
728 sal_Unicode ch
= aName
[0];
732 if( CALC_MONTH
== m_eCurrListOper
|| CALC_DAY
== m_eCurrListOper
)
734 m_eCurrOper
= m_eCurrListOper
;
739 m_eCurrOper
= CALC_PRINT
;
750 m_eCurrOper
= SwCalcOper(ch
);
759 m_eCurrOper
= SwCalcOper('=');
764 m_eCurrOper
= CALC_NOT
;
768 if( aRes
.EndPos
< m_sCommand
.getLength() &&
769 '=' == m_sCommand
[aRes
.EndPos
] )
778 m_eCurrOper
= m_eCurrListOper
;
782 if( aRes
.EndPos
< m_sCommand
.getLength() )
784 m_aVarName
.setLength(0);
785 sal_Int32 nFndPos
= aRes
.EndPos
,
789 nFndPos
= m_sCommand
.indexOf( ']', nFndPos
);
793 if ('\\' == m_sCommand
[nFndPos
-1])
795 m_aVarName
.append(m_sCommand
.subView(nSttPos
,
796 nFndPos
- nSttPos
- 1) );
802 } while( nFndPos
!= -1 );
806 if( nSttPos
!= nFndPos
)
807 m_aVarName
.append(m_sCommand
.subView(nSttPos
,
808 nFndPos
- nSttPos
) );
809 aRes
.EndPos
= nFndPos
+ 1;
810 m_eCurrOper
= CALC_NAME
;
827 else if( aRes
.TokenType
& KParseType::BOOLEAN
)
829 std::u16string_view
aName( m_sCommand
.subView( nRealStt
,
830 aRes
.EndPos
- nRealStt
));
833 sal_Unicode ch
= aName
[0];
836 if ('<' == ch
|| '>' == ch
)
840 SwCalcOper eTmp2
= ('<' == ch
) ? CALC_LEQ
: CALC_GEQ
;
841 m_eCurrOper
= ('<' == ch
) ? CALC_LES
: CALC_GRE
;
843 if( 2 == aName
.size() && '=' == aName
[1] )
845 else if( 1 != aName
.size() )
850 else if( nRealStt
== m_sCommand
.getLength() )
852 m_eCurrOper
= CALC_ENDCALC
;
858 m_eError
= SwCalcError::Syntax
;
859 m_eCurrOper
= CALC_PRINT
;
861 m_nCommandPos
= aRes
.EndPos
;
867 SwSbxValue
SwCalc::Term()
869 SwSbxValue
left( Prim() );
873 sal_uInt16 nSbxOper
= USHRT_MAX
;
875 switch( m_eCurrOper
)
880 bool bB
= Prim().GetBool();
881 left
.PutBool( left
.GetBool() && bB
);
887 bool bB
= Prim().GetBool();
888 left
.PutBool( left
.GetBool() || bB
);
894 bool bR
= Prim().GetBool();
895 bool bL
= left
.GetBool();
896 left
.PutBool(bL
!= bR
);
900 case CALC_EQ
: nSbxOper
= SbxEQ
; break;
901 case CALC_NEQ
: nSbxOper
= SbxNE
; break;
902 case CALC_LEQ
: nSbxOper
= SbxLE
; break;
903 case CALC_GEQ
: nSbxOper
= SbxGE
; break;
904 case CALC_GRE
: nSbxOper
= SbxGT
; break;
905 case CALC_LES
: nSbxOper
= SbxLT
; break;
907 case CALC_MUL
: nSbxOper
= SbxMUL
; break;
908 case CALC_DIV
: nSbxOper
= SbxDIV
; break;
913 SwSbxValue e
= Prim();
914 left
= left
.GetDouble() < e
.GetDouble() ? left
: e
;
920 SwSbxValue e
= Prim();
921 left
= left
.GetDouble() > e
.GetDouble() ? left
: e
;
927 SwSbxValue e
= Prim();
928 sal_Int32 nYear
= static_cast<sal_Int32
>(floor( left
.GetDouble() ));
929 nYear
= nYear
& 0x0000FFFF;
930 sal_Int32 nMonth
= static_cast<sal_Int32
>(floor( e
.GetDouble() ));
931 nMonth
= ( nMonth
& 0x000000FF ) << 16;
932 left
.PutLong( nMonth
+ nYear
);
933 m_eCurrOper
= CALC_DAY
;
939 SwSbxValue e
= Prim();
940 sal_Int32 nYearMonth
= static_cast<sal_Int32
>(floor( left
.GetDouble() ));
941 nYearMonth
= nYearMonth
& 0x00FFFFFF;
942 sal_Int32 nDay
= static_cast<sal_Int32
>(floor( e
.GetDouble() ));
943 nDay
= ( nDay
& 0x000000FF ) << 24;
944 left
= lcl_ConvertToDateValue( m_rDoc
, nDay
+ nYearMonth
);
950 SwSbxValue e
= Prim();
954 sal_Int32 nDec
= static_cast<sal_Int32
>(floor( e
.GetDouble() ));
955 if( nDec
< -20 || nDec
> 20 )
957 m_eError
= SwCalcError::Overflow
;
961 fVal
= left
.GetDouble();
964 for (sal_Int32 i
= 0; i
< nDec
; ++i
)
969 for (sal_Int32 i
= 0; i
< -nDec
; ++i
)
986 double fNum
= fVal
; // find the exponent
995 while( fNum
>= 10.0 )
1004 else if( nExp
<= 1 )
1006 fVal
= floor( fVal
+ 0.5 + nRoundVal
[ nExp
] );
1013 left
.PutDouble( fVal
);
1017 //#77448# (=2*3^2 != 18)
1023 if( USHRT_MAX
!= nSbxOper
)
1025 // #i47706: cast to SbxOperator AFTER comparing to USHRT_MAX
1026 SbxOperator eSbxOper
= static_cast<SbxOperator
>(nSbxOper
);
1029 if( SbxEQ
<= eSbxOper
&& eSbxOper
<= SbxGE
)
1031 left
.PutBool( left
.Compare( eSbxOper
, Prim() ));
1035 SwSbxValue
aRight( Prim() );
1036 aRight
.MakeDouble();
1039 if( SbxDIV
== eSbxOper
&& !aRight
.GetDouble() )
1040 m_eError
= SwCalcError::DivByZero
;
1042 left
.Compute( eSbxOper
, aRight
);
1048 SwSbxValue
SwCalc::StdFunc(pfCalc pFnc
, bool bChkTrig
)
1052 double nVal
= Prim().GetDouble();
1053 if( !bChkTrig
|| ( nVal
> -1 && nVal
< 1 ) )
1054 nErg
.PutDouble( (*pFnc
)( nVal
) );
1056 m_eError
= SwCalcError::Overflow
;
1060 SwSbxValue
SwCalc::PrimFunc(bool &rChkPow
)
1064 switch (m_eCurrOper
)
1067 SAL_INFO("sw.calc", "sin");
1068 return StdFunc(&sin
, false);
1070 SAL_INFO("sw.calc", "cos");
1071 return StdFunc(&cos
, false);
1073 SAL_INFO("sw.calc", "tan");
1074 return StdFunc(&tan
, false);
1076 SAL_INFO("sw.calc", "atan");
1077 return StdFunc(&atan
, false);
1079 SAL_INFO("sw.calc", "asin");
1080 return StdFunc(&asin
, true);
1082 SAL_INFO("sw.calc", "acos");
1083 return StdFunc(&acos
, true);
1085 SAL_INFO("sw.calc", "abs");
1086 return StdFunc(&abs
, false);
1089 SAL_INFO("sw.calc", "sign");
1092 double nVal
= Prim().GetDouble();
1093 nErg
.PutDouble( int(0 < nVal
) - int(nVal
< 0) );
1098 SAL_INFO("sw.calc", "int");
1101 sal_Int32 nVal
= static_cast<sal_Int32
>( Prim().GetDouble() );
1102 nErg
.PutDouble( nVal
);
1107 SAL_INFO("sw.calc", "not");
1109 SwSbxValue nErg
= Prim();
1110 if( SbxSTRING
== nErg
.GetType() )
1112 nErg
.PutBool( nErg
.GetOUString().isEmpty() );
1114 else if(SbxBOOL
== nErg
.GetType() )
1116 nErg
.PutBool(!nErg
.GetBool());
1118 // Evaluate arguments manually so that the binary NOT below does not
1119 // get called. We want a BOOLEAN NOT.
1120 else if (nErg
.IsNumeric())
1122 nErg
.PutLong( nErg
.GetDouble() == 0.0 ? 1 : 0 );
1126 OSL_FAIL( "unexpected case. computing binary NOT" );
1127 //!! computes a binary NOT
1128 nErg
.Compute( SbxNOT
, nErg
);
1134 SAL_INFO("sw.calc", "number: " << m_nNumberValue
.GetDouble());
1136 m_bHasNumber
= true;
1137 if( GetToken() == CALC_PHD
)
1139 double aTmp
= m_nNumberValue
.GetDouble();
1141 nErg
.PutDouble( aTmp
);
1144 else if( m_eCurrOper
== CALC_NAME
)
1146 m_eError
= SwCalcError::Syntax
;
1150 nErg
= m_nNumberValue
;
1157 SAL_INFO("sw.calc", "name");
1159 switch(SwCalcOper eOper
= GetToken())
1163 SwCalcExp
* n
= VarInsert(m_aVarName
.toString());
1165 nErg
= n
->nValue
= Expr();
1169 nErg
= VarLook(m_aVarName
.toString())->nValue
;
1170 // Explicitly disallow unknown function names (followed by "("),
1171 // allow unknown variable names (equal to zero)
1172 if (nErg
.IsVoidValue() && (eOper
== CALC_LP
))
1173 m_eError
= SwCalcError::Syntax
;
1182 SAL_INFO("sw.calc", "-");
1185 nErg
.PutDouble( -(Prim().GetDouble()) );
1190 SAL_INFO("sw.calc", "(");
1192 SwSbxValue nErg
= Expr();
1193 if( m_eCurrOper
!= CALC_RP
)
1195 m_eError
= SwCalcError::FaultyBrackets
;
1200 rChkPow
= true; // in order for =(7)^2 to work
1205 // ignore, see tdf#121962
1206 SAL_INFO("sw.calc", ")");
1211 SAL_INFO("sw.calc", "mean");
1213 m_bHasNumber
= CALC_MEAN
== m_eCurrOper
;
1215 SwSbxValue nErg
= Expr();
1216 double aTmp
= nErg
.GetDouble();
1218 if ( !m_bHasNumber
)
1219 m_eError
= SwCalcError::DivByZero
;
1221 nErg
.PutDouble( aTmp
);
1226 SAL_INFO("sw.calc", "count");
1228 m_bHasNumber
= false;
1230 SwSbxValue nErg
= Expr();
1231 nErg
.PutDouble( m_bHasNumber
? m_nListPor
: 0 );
1236 SAL_INFO("sw.calc", "sqrt");
1238 SwSbxValue nErg
= Prim();
1239 if( nErg
.GetDouble() < 0 )
1240 m_eError
= SwCalcError::Overflow
;
1242 nErg
.PutDouble( sqrt( nErg
.GetDouble() ));
1251 SAL_INFO("sw.calc", "sum/product/date/min/max");
1253 SwSbxValue nErg
= Expr();
1258 SAL_INFO("sw.calc", "endcalc");
1264 SAL_INFO("sw.calc", "syntax error");
1265 m_eError
= SwCalcError::Syntax
;
1269 return SwSbxValue();
1272 SwSbxValue
SwCalc::Prim()
1275 SwSbxValue nErg
= PrimFunc(bChkPow
);
1277 if (bChkPow
&& m_eCurrOper
== CALC_POW
)
1279 double dleft
= nErg
.GetDouble();
1281 double right
= Prim().GetDouble();
1284 fraction
= modf( right
, &o3tl::temporary(double()) );
1285 if( ( dleft
< 0.0 && 0.0 != fraction
) ||
1286 ( 0.0 == dleft
&& right
< 0.0 ) )
1288 m_eError
= SwCalcError::Overflow
;
1293 dleft
= pow(dleft
, right
);
1294 if( dleft
== HUGE_VAL
)
1296 m_eError
= SwCalcError::OverflowInPower
;
1301 nErg
.PutDouble( dleft
);
1309 SwSbxValue
SwCalc::Expr()
1311 SwSbxValue left
= Term();
1321 SwSbxValue
right(Term());
1323 left
.Compute(SbxPLUS
, right
);
1331 SwSbxValue
right(Term());
1333 left
.Compute(SbxMINUS
, right
);
1344 OUString
SwCalc::GetColumnName(const OUString
& rName
)
1346 sal_Int32 nPos
= rName
.indexOf(DB_DELIM
);
1349 nPos
= rName
.indexOf(DB_DELIM
, nPos
+ 1);
1352 return rName
.copy(nPos
+ 1);
1357 OUString
SwCalc::GetDBName(std::u16string_view rName
)
1359 size_t nPos
= rName
.find(DB_DELIM
);
1360 if( std::u16string_view::npos
!= nPos
)
1362 nPos
= rName
.find(DB_DELIM
, nPos
+ 1);
1364 if( std::u16string_view::npos
!= nPos
)
1365 return OUString(rName
.substr( 0, nPos
));
1367 SwDBData aData
= m_rDoc
.GetDBData();
1368 return aData
.sDataSource
+ OUStringChar(DB_DELIM
) + aData
.sCommand
;
1373 bool lcl_Str2Double( const OUString
& rCommand
, sal_Int32
& rCommandPos
,
1375 const LocaleDataWrapper
* const pLclData
)
1378 const sal_Unicode nCurrCmdPos
= rCommandPos
;
1379 rtl_math_ConversionStatus eStatus
;
1380 const sal_Unicode
* pEnd
;
1381 rVal
= pLclData
->stringToDouble( rCommand
.getStr() + rCommandPos
,
1382 rCommand
.getStr() + rCommand
.getLength(),
1386 rCommandPos
= static_cast<sal_Int32
>(pEnd
- rCommand
.getStr());
1388 return rtl_math_ConversionStatus_Ok
== eStatus
&&
1389 nCurrCmdPos
!= rCommandPos
;
1393 bool SwCalc::Str2Double( const OUString
& rCommand
, sal_Int32
& rCommandPos
,
1396 const SvtSysLocale aSysLocale
;
1397 return lcl_Str2Double( rCommand
, rCommandPos
, rVal
, &aSysLocale
.GetLocaleData() );
1400 bool SwCalc::Str2Double( const OUString
& rCommand
, sal_Int32
& rCommandPos
,
1401 double& rVal
, SwDoc
const * const pDoc
)
1403 const SvtSysLocale aSysLocale
;
1404 std::unique_ptr
<const LocaleDataWrapper
> pLclD
;
1407 LanguageType eLang
= GetDocAppScriptLang( *pDoc
);
1408 if (eLang
!= aSysLocale
.GetLanguageTag().getLanguageType())
1410 pLclD
.reset( new LocaleDataWrapper( LanguageTag( eLang
)) );
1414 bool const bRet
= lcl_Str2Double(rCommand
, rCommandPos
, rVal
,
1415 pLclD
? pLclD
.get() : &aSysLocale
.GetLocaleData());
1420 bool SwCalc::IsValidVarName( const OUString
& rStr
, OUString
* pValidName
)
1423 using namespace ::com::sun::star::i18n
;
1426 ParseResult aRes
= GetAppCharClass().parseAnyToken( rStr
, 0,
1427 coStartFlags
, OUString(),
1428 coContFlags
, OUString() );
1430 if( aRes
.TokenType
& KParseType::IDENTNAME
)
1432 bRet
= aRes
.EndPos
== rStr
.getLength();
1435 *pValidName
= rStr
.copy( aRes
.LeadingWhiteSpace
,
1436 aRes
.EndPos
- aRes
.LeadingWhiteSpace
);
1439 else if( pValidName
)
1440 pValidName
->clear();
1445 SwHash::SwHash(OUString _aStr
)
1446 : aStr(std::move(_aStr
))
1454 SwCalcExp::SwCalcExp(const OUString
& rStr
, SwSbxValue aVal
,
1455 const SwFieldType
* pType
)
1457 , nValue(std::move(aVal
))
1462 bool SwSbxValue::GetBool() const
1464 return SbxSTRING
== GetType() ? !GetOUString().isEmpty()
1465 : SbxValue::GetBool();
1468 double SwSbxValue::GetDouble() const
1471 if( SbxSTRING
== GetType() )
1474 SwCalc::Str2Double( GetOUString(), nStt
, nRet
);
1478 nRet
= GetBool() ? 1.0 : 0.0;
1482 nRet
= SbxValue::GetDouble();
1487 SwSbxValue
& SwSbxValue::MakeDouble()
1489 if( GetType() == SbxSTRING
|| GetType() == SbxBOOL
)
1490 PutDouble( GetDouble() );
1494 #ifdef STANDALONE_HASHCALC
1496 // this is example code how to create hash values in the CTOR:
1502 sNType0
[] = "false", sNType1
[] = "true", sNType2
[] = "pi",
1503 sNType3
[] = "e", sNType4
[] = "tables", sNType5
[] = "graf",
1504 sNType6
[] = "ole", sNType7
[] = "page", sNType8
[] = "para",
1505 sNType9
[] = "word", sNType10
[]= "char",
1506 sNType11
[] = "user_company" , sNType12
[] = "user_firstname" ,
1507 sNType13
[] = "user_lastname" , sNType14
[] = "user_initials",
1508 sNType15
[] = "user_street" , sNType16
[] = "user_country" ,
1509 sNType17
[] = "user_zipcode" , sNType18
[] = "user_city" ,
1510 sNType19
[] = "user_title" , sNType20
[] = "user_position" ,
1511 sNType21
[] = "user_tel_home", sNType22
[] = "user_tel_work",
1512 sNType23
[] = "user_fax" , sNType24
[] = "user_email" ,
1513 sNType25
[] = "user_state", sNType26
[] = "graph"
1516 static const char* sNTypeTab
[ 27 ] =
1518 sNType0
, sNType1
, sNType2
, sNType3
, sNType4
, sNType5
,
1519 sNType6
, sNType7
, sNType8
, sNType9
, sNType10
, sNType11
,
1520 sNType12
, sNType13
, sNType14
, sNType15
, sNType16
, sNType17
,
1521 sNType18
, sNType19
, sNType20
, sNType21
, sNType22
, sNType23
,
1522 sNType24
, sNType25
, sNType26
1525 const unsigned short nTableSize
= 47;
1526 int aArr
[ nTableSize
] = { 0 };
1529 for( int n
= 0; n
< 27; ++n
)
1531 unsigned int ii
= 0;
1532 const char* pp
= sNTypeTab
[ n
];
1536 ii
= ii
<< 1 ^ *pp
++;
1540 ch
= aArr
[ ii
] ? 'X' : ' ';
1542 printf( "%-20s -> %3u [%c]\n", sNTypeTab
[ n
], ii
, ch
);
1548 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */