LibreOffice Module sc (master) 1
interpr7.cxx
Go to the documentation of this file.
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
10#include <interpre.hxx>
11#include <jumpmatrix.hxx>
12#include <formulacell.hxx>
13#include <scmatrix.hxx>
14#include <rtl/strbuf.hxx>
15#include <rtl/character.hxx>
17#include <sfx2/bindings.hxx>
18#include <sfx2/linkmgr.hxx>
19#include <tools/urlobj.hxx>
20
21#include <officecfg/Office/Common.hxx>
22#include <libxml/xpath.h>
23#include <datastreamgettime.hxx>
24#include <dpobject.hxx>
25#include <document.hxx>
26#include <tokenarray.hxx>
27#include <webservicelink.hxx>
28
29#include <sc.hrc>
30
31#include <cstring>
32#include <memory>
33#include <string_view>
34
35using namespace com::sun::star;
36
37// TODO: Add new methods for ScInterpreter here.
38
40{
41 sal_uInt8 nParamCount = GetByte();
42 if (!MustHaveParamCount( nParamCount, 2 ) )
43 return;
44
45 SCSIZE nMatCols = 1, nMatRows = 1, nNode = 0;
46 // In array/matrix context node elements' results are to be
47 // subsequently stored. Check this before obtaining any argument from
48 // the stack so the stack type can be used.
50 {
51 if (pJumpMatrix)
52 {
53 // Single result, GetString() will retrieve the corresponding
54 // argument and JumpMatrix() will store it at the proper
55 // position. Note that nMatCols and nMatRows are still 1.
56 SCSIZE nCurCol = 0, nCurRow = 0;
57 pJumpMatrix->GetPos( nCurCol, nCurRow);
58 nNode = nCurRow;
59 }
60 else if (bMatrixFormula)
61 {
62 // If there is no formula cell then continue with a single
63 // result.
65 {
66 SCCOL nCols;
67 SCROW nRows;
68 pMyFormulaCell->GetMatColsRows( nCols, nRows);
69 nMatCols = nCols;
70 nMatRows = nRows;
71 }
72 }
73 else if (GetStackType() == formula::svMatrix)
74 {
75 const ScMatrix* pPathMatrix = pStack[sp-1]->GetMatrix();
76 if (!pPathMatrix)
77 {
79 return;
80 }
81 pPathMatrix->GetDimensions( nMatCols, nMatRows);
82
83 /* TODO: it is unclear what should happen if there are
84 * different path arguments in matrix elements. We may have to
85 * evaluate each, and for repeated identical paths use
86 * subsequent nodes. As is, the path at 0,0 is used as obtained
87 * by GetString(). */
88
89 }
90 }
91 if (!nMatCols || !nMatRows)
92 {
94 return;
95 }
96
97 OUString aXPathExpression = GetString().getString();
98 OUString aString = GetString().getString();
99 if(aString.isEmpty() || aXPathExpression.isEmpty())
100 {
101 PushError( FormulaError::NoValue );
102 return;
103 }
104
105 OString aOXPathExpression = OUStringToOString( aXPathExpression, RTL_TEXTENCODING_UTF8 );
106 const char* pXPathExpr = aOXPathExpression.getStr();
107 OString aOString = OUStringToOString( aString, RTL_TEXTENCODING_UTF8 );
108 const char* pXML = aOString.getStr();
109
110 std::shared_ptr<xmlParserCtxt> pContext(
111 xmlNewParserCtxt(), xmlFreeParserCtxt );
112
113 std::shared_ptr<xmlDoc> pDoc( xmlParseMemory( pXML, aOString.getLength() ),
114 xmlFreeDoc );
115
116 if(!pDoc)
117 {
118 PushError( FormulaError::NoValue );
119 return;
120 }
121
122 std::shared_ptr<xmlXPathContext> pXPathCtx( xmlXPathNewContext(pDoc.get()),
123 xmlXPathFreeContext );
124
125 std::shared_ptr<xmlXPathObject> pXPathObj( xmlXPathEvalExpression(BAD_CAST(pXPathExpr), pXPathCtx.get()),
126 xmlXPathFreeObject );
127
128 if(!pXPathObj)
129 {
130 PushError( FormulaError::NoValue );
131 return;
132 }
133
134 switch(pXPathObj->type)
135 {
136 case XPATH_UNDEFINED:
137 PushNoValue();
138 break;
139 case XPATH_NODESET:
140 {
141 xmlNodeSetPtr pNodeSet = pXPathObj->nodesetval;
142 if(!pNodeSet)
143 {
144 PushError( FormulaError::NoValue );
145 return;
146 }
147
148 const size_t nSize = pNodeSet->nodeNr;
149 if (nNode >= nSize)
150 {
151 // For pJumpMatrix
152 PushError( FormulaError::NotAvailable);
153 return;
154 }
155
156 /* TODO: for nMatCols>1 IF stack type is svMatrix, i.e.
157 * pPathMatrix!=nullptr, we may want a result matrix with
158 * nMatCols columns as well, but clarify first how to treat
159 * differing path elements. */
160
161 ScMatrixRef xResMat;
162 if (nMatRows > 1)
163 {
164 xResMat = GetNewMat( 1, nMatRows, true);
165 if (!xResMat)
166 {
167 PushError( FormulaError::CodeOverflow);
168 return;
169 }
170 }
171
172 for ( ; nNode < nMatRows; ++nNode)
173 {
174 if( nSize > nNode )
175 {
176 OUString aResult;
177 if(pNodeSet->nodeTab[nNode]->type == XML_NAMESPACE_DECL)
178 {
179 xmlNsPtr ns = reinterpret_cast<xmlNsPtr>(pNodeSet->nodeTab[nNode]);
180 xmlNodePtr cur = reinterpret_cast<xmlNodePtr>(ns->next);
181 std::shared_ptr<xmlChar> pChar2(xmlNodeGetContent(cur), xmlFree);
182 aResult = OStringToOUString(std::string_view(reinterpret_cast<char*>(pChar2.get())), RTL_TEXTENCODING_UTF8);
183 }
184 else
185 {
186 xmlNodePtr cur = pNodeSet->nodeTab[nNode];
187 std::shared_ptr<xmlChar> pChar2(xmlNodeGetContent(cur), xmlFree);
188 aResult = OStringToOUString(std::string_view(reinterpret_cast<char*>(pChar2.get())), RTL_TEXTENCODING_UTF8);
189 }
190 if (xResMat)
191 xResMat->PutString( mrStrPool.intern( aResult), 0, nNode);
192 else
193 PushString(aResult);
194 }
195 else
196 {
197 if (xResMat)
198 xResMat->PutError( FormulaError::NotAvailable, 0, nNode);
199 else
200 PushError( FormulaError::NotAvailable );
201 }
202 }
203 if (xResMat)
204 PushMatrix( xResMat);
205 }
206 break;
207 case XPATH_BOOLEAN:
208 {
209 bool bVal = pXPathObj->boolval != 0;
210 PushDouble(double(bVal));
211 }
212 break;
213 case XPATH_NUMBER:
214 {
215 double fVal = pXPathObj->floatval;
216 PushDouble(fVal);
217 }
218 break;
219 case XPATH_STRING:
220 PushString(OUString::createFromAscii(reinterpret_cast<char*>(pXPathObj->stringval)));
221 break;
222#if LIBXML_VERSION < 21000 || defined(LIBXML_XPTR_LOCS_ENABLED)
223 case XPATH_POINT:
224 PushNoValue();
225 break;
226 case XPATH_RANGE:
227 PushNoValue();
228 break;
229 case XPATH_LOCATIONSET:
230 PushNoValue();
231 break;
232#endif
233 case XPATH_USERS:
234 PushNoValue();
235 break;
236 case XPATH_XSLT_TREE:
237 PushNoValue();
238 break;
239
240 }
241}
242
243static ScWebServiceLink* lcl_GetWebServiceLink(const sfx2::LinkManager* pLinkMgr, std::u16string_view rURL)
244{
245 size_t nCount = pLinkMgr->GetLinks().size();
246 for (size_t i=0; i<nCount; ++i)
247 {
248 ::sfx2::SvBaseLink* pBase = pLinkMgr->GetLinks()[i].get();
249 if (ScWebServiceLink* pLink = dynamic_cast<ScWebServiceLink*>(pBase))
250 {
251 if (pLink->GetURL() == rURL)
252 return pLink;
253 }
254 }
255
256 return nullptr;
257}
258
259static bool lcl_FunctionAccessLoadWebServiceLink( OUString& rResult, ScDocument* pDoc, const OUString& rURI )
260{
261 // For FunctionAccess service always force a changed data update.
262 ScWebServiceLink aLink( pDoc, rURI);
263 if (aLink.DataChanged( OUString(), css::uno::Any()) != sfx2::SvBaseLink::UpdateResult::SUCCESS)
264 return false;
265
266 if (!aLink.HasResult())
267 return false;
268
269 rResult = aLink.GetResult();
270
271 return true;
272}
273
275{
276 sal_uInt8 nParamCount = GetByte();
277 if (!MustHaveParamCount( nParamCount, 1 ) )
278 return;
279
280 OUString aURI = GetString().getString();
281
282 if (aURI.isEmpty())
283 {
284 PushError( FormulaError::NoValue );
285 return;
286 }
287
288 INetURLObject aObj(aURI, INetProtocol::File);
289 INetProtocol eProtocol = aObj.GetProtocol();
290 if (eProtocol != INetProtocol::Http && eProtocol != INetProtocol::Https)
291 {
292 PushError(FormulaError::NoValue);
293 return;
294 }
295
296 if (!mpLinkManager)
297 {
299 {
300 PushError( FormulaError::NoValue);
301 }
302 else
303 {
304 OUString aResult;
305 if (lcl_FunctionAccessLoadWebServiceLink( aResult, &mrDoc, aURI))
306 PushString( aResult);
307 else
308 PushError( FormulaError::NoValue);
309 }
310 return;
311 }
312
313 // Need to reinterpret after loading (build links)
314 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
315
316 // while the link is not evaluated, idle must be disabled (to avoid circular references)
317 bool bOldEnabled = mrDoc.IsIdleEnabled();
318 mrDoc.EnableIdle(false);
319
320 // Get/ Create link object
322
323 bool bWasError = (pMyFormulaCell && pMyFormulaCell->GetRawError() != FormulaError::NONE);
324
325 if (!pLink)
326 {
327 pLink = new ScWebServiceLink(&mrDoc, aURI);
329 if ( mpLinkManager->GetLinks().size() == 1 ) // the first one?
330 {
331 SfxBindings* pBindings = mrDoc.GetViewBindings();
332 if (pBindings)
333 pBindings->Invalidate( SID_LINKS ); // Link-Manager enabled
334 }
335
336 //if the document was just loaded, but the ScDdeLink entry was missing, then
337 //don't update this link until the links are updated in response to the users
338 //decision
340 {
341 pLink->Update();
342 }
343
344 if (pMyFormulaCell)
345 {
346 // StartListening after the Update to avoid circular references
348 }
349 }
350 else
351 {
352 if (pMyFormulaCell)
354 }
355
356 // If a new Error from Reschedule appears when the link is executed then reset the errorflag
357 if (pMyFormulaCell && pMyFormulaCell->GetRawError() != FormulaError::NONE && !bWasError)
358 pMyFormulaCell->SetErrCode(FormulaError::NONE);
359
360 // check the value
361 if (pLink->HasResult())
362 PushString(pLink->GetResult());
364 {
365 // If this formula cell is recalculated just after load and the
366 // expression is exactly WEBSERVICE("literal_URI") (i.e. no other
367 // calculation involved, not even a cell reference) and a cached
368 // result is set as hybrid string then use that as result value to
369 // prevent a #VALUE! result due to the "Automatic update of
370 // external links has been disabled."
371 // This will work only once, as the new formula cell result won't
372 // be a hybrid anymore.
373 /* TODO: the FormulaError::LinkFormulaNeedingCheck could be used as
374 * a signal for the formula cell to keep the hybrid string as
375 * result of the overall formula *iff* no higher prioritized
376 * ScRecalcMode than ONLOAD_LENIENT is present in the entire
377 * document (i.e. the formula result could not be influenced by an
378 * ONLOAD_MUST or ALWAYS recalc, necessary as we don't track
379 * interim results of subexpressions that could be compared), which
380 * also means to track setting ScRecalcMode somehow... note this is
381 * just a vague idea so far and might or might not work. */
383 {
384 if (pMyFormulaCell->GetCode()->GetCodeLen() == 2)
385 {
386 formula::FormulaToken const * const * pRPN = pMyFormulaCell->GetCode()->GetCode();
387 if (pRPN[0]->GetType() == formula::svString && pRPN[1]->GetOpCode() == ocWebservice)
389 else
390 PushError(FormulaError::LinkFormulaNeedingCheck);
391 }
392 else
393 PushError(FormulaError::LinkFormulaNeedingCheck);
394 }
395 else
396 PushError(FormulaError::LinkFormulaNeedingCheck);
397 }
398 else
399 PushError(FormulaError::NoValue);
400
401 mrDoc.EnableIdle(bOldEnabled);
403}
404
416{
417 sal_uInt8 nParamCount = GetByte();
418 if ( !MustHaveParamCount( nParamCount, 1 ) )
419 return;
420
421 OUString aStr = GetString().getString();
422 if ( aStr.isEmpty() )
423 {
424 PushError( FormulaError::NoValue );
425 return;
426 }
427
428 OString aUtf8Str( aStr.toUtf8());
429 const sal_Int32 nLen = aUtf8Str.getLength();
430 OStringBuffer aUrlBuf( nLen );
431 for ( int i = 0; i < nLen; i++ )
432 {
433 char c = aUtf8Str[ i ];
434 if ( rtl::isAsciiAlphanumeric( static_cast<unsigned char>( c ) ) || c == '-' || c == '_' )
435 aUrlBuf.append( c );
436 else
437 {
438 aUrlBuf.append( '%' );
439 OString convertedChar = OString::number( static_cast<unsigned char>( c ), 16 ).toAsciiUpperCase();
440 // RFC 3986 indicates:
441 // "A percent-encoded octet is encoded as a character triplet,
442 // consisting of the percent character "%" followed by the two hexadecimal digits
443 // representing that octet's numeric value"
444 if (convertedChar.getLength() == 1)
445 aUrlBuf.append("0");
446 aUrlBuf.append(convertedChar);
447 }
448 }
449 PushString( OUString::fromUtf8( aUrlBuf ) );
450}
451
453{
454 // This is to be used by developers only! Never document this for end
455 // users. This is a convenient way to extract arbitrary internal state to
456 // a cell for easier debugging.
457
458 if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
459 {
460 PushError(FormulaError::NoName);
461 return;
462 }
463
464 if (!MustHaveParamCount(GetByte(), 1))
465 return;
466
467 rtl_uString* p = GetString().getDataIgnoreCase();
468 if (!p)
469 {
471 return;
472 }
473
474 OUString aStrUpper(p);
475
476 if (aStrUpper == "PIVOTCOUNT")
477 {
478 // Set the number of pivot tables in the document.
479
480 double fVal = 0.0;
481 if (mrDoc.HasPivotTable())
482 {
483 const ScDPCollection* pDPs = mrDoc.GetDPCollection();
484 fVal = pDPs->GetCount();
485 }
486 PushDouble(fVal);
487 }
488 else if (aStrUpper == "DATASTREAM_IMPORT")
490 else if (aStrUpper == "DATASTREAM_RECALC")
492 else if (aStrUpper == "DATASTREAM_RENDER")
494 else
496}
497
499{
500 sal_uInt8 nParamCount = GetByte();
501 if (MustHaveParamCount( nParamCount, 1 ) )
502 PushDouble( std::erf( GetDouble() ) );
503}
504
506{
507 sal_uInt8 nParamCount = GetByte();
508 if (MustHaveParamCount( nParamCount, 1 ) )
509 PushDouble( std::erfc( GetDouble() ) );
510}
511
513{
514 sal_uInt8 nParamCount = GetByte();
515 if(!MustHaveParamCount(nParamCount, 3, 4))
516 return;
517
518 double nAlpha = 0;
519 if(nParamCount == 4)
520 nAlpha = rtl::math::approxFloor(GetDouble());
521
522 if(nAlpha < 0 || nAlpha > 255)
523 {
525 return;
526 }
527
528 double nBlue = rtl::math::approxFloor(GetDouble());
529
530 if(nBlue < 0 || nBlue > 255)
531 {
533 return;
534 }
535
536 double nGreen = rtl::math::approxFloor(GetDouble());
537
538 if(nGreen < 0 || nGreen > 255)
539 {
541 return;
542 }
543
544 double nRed = rtl::math::approxFloor(GetDouble());
545
546 if(nRed < 0 || nRed > 255)
547 {
549 return;
550 }
551
552 double nVal = 256*256*256*nAlpha + 256*256*nRed + 256*nGreen + nBlue;
553 PushDouble(nVal);
554}
555
556/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
INetProtocol GetProtocol() const
SC_DLLPUBLIC size_t GetCount() const
Definition: dpobject.cxx:3717
bool IsIdleEnabled() const
Definition: document.hxx:2207
SC_DLLPUBLIC bool HasPivotTable() const
Definition: documen3.cxx:360
SfxBindings * GetViewBindings()
Definition: documen8.cxx:1183
bool HasLinkFormulaNeedingCheck() const
Definition: document.hxx:2213
bool IsFunctionAccess() const
Definition: document.hxx:1597
void EnableIdle(bool bDo)
Definition: document.hxx:2208
SC_DLLPUBLIC ScDPCollection * GetDPCollection()
Definition: documen3.cxx:365
void GetMatColsRows(SCCOL &nCols, SCROW &nRows) const
void SetErrCode(FormulaError n)
const svl::SharedString & GetResultString() const
bool HasHybridStringResult() const
FormulaError GetRawError() const
ScTokenArray * GetCode()
bool bMatrixFormula
Definition: interpre.hxx:211
svl::SharedString GetString()
Definition: interpr4.cxx:2334
bool IsInArrayContext() const
Definition: interpre.hxx:1024
bool MustHaveParamCount(short nAct, short nMust)
Definition: interpre.hxx:1048
void ScErfc()
Definition: interpr7.cxx:505
ScDocument & mrDoc
Definition: interpre.hxx:186
void ScColor()
Definition: interpr7.cxx:512
void PushIllegalParameter()
Definition: interpr4.cxx:1938
void PushIllegalArgument()
Definition: interpr4.cxx:1943
void ScWebservice()
Definition: interpr7.cxx:274
void ScErf()
Definition: interpr7.cxx:498
void ScEncodeURL()
Returns a string in which all non-alphanumeric characters except stroke and underscore (-_) have been...
Definition: interpr7.cxx:415
void PushError(FormulaError nError)
Definition: interpr4.cxx:1927
sal_uInt8 GetByte() const
Definition: interpre.hxx:416
ScMatrixRef GetNewMat(SCSIZE nC, SCSIZE nR, bool bEmpty=false)
Definition: interpr5.cxx:284
double GetDouble()
Definition: interpr4.cxx:2097
void PushDouble(double nVal)
Definition: interpr4.cxx:1801
void ScDebugVar()
Definition: interpr7.cxx:452
const formula::FormulaToken ** pStack
Definition: interpre.hxx:197
ScTokenArray * pArr
Definition: interpre.hxx:184
ScJumpMatrix * pJumpMatrix
Definition: interpre.hxx:190
sfx2::LinkManager * mpLinkManager
Definition: interpre.hxx:187
void PushMatrix(const sc::RangeMatrix &rMat)
Definition: interpr4.cxx:1902
ScFormulaCell * pMyFormulaCell
Definition: interpre.hxx:192
void ScFilterXML()
Definition: interpr7.cxx:39
void PushNoValue()
Definition: interpr4.cxx:1953
formula::StackVar GetStackType()
Stack type with replacement of defaults, e.g. svMissing and formula::svEmptyCell will result in formu...
Definition: interpr4.cxx:1978
svl::SharedStringPool & mrStrPool
Definition: interpre.hxx:188
void PushString(const OUString &rStr)
Definition: interpr4.cxx:1825
sal_uInt16 sp
Definition: interpre.hxx:199
void GetPos(SCSIZE &rCol, SCSIZE &rRow) const
Definition: jumpmatrix.cxx:104
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:101
void GetDimensions(SCSIZE &rC, SCSIZE &rR) const
Definition: scmatrix.cxx:3143
void Invalidate(sal_uInt16 nId)
bool StartListening(SvtBroadcaster &rBroadcaster)
void AddRecalcMode(ScRecalcMode nBits)
sal_uInt16 GetCodeLen() const
FormulaToken ** GetCode() const
virtual const ScMatrix * GetMatrix() const
void InsertFileLink(sfx2::SvBaseLink &, SvBaseLinkObjectType nFileType, std::u16string_view rFileNm, const OUString *pFilterNm=nullptr, const OUString *pRange=nullptr)
const SvBaseLinks & GetLinks() const
SharedString intern(const OUString &rStr)
const OUString & getString() const
rtl_uString * getDataIgnoreCase()
int nCount
static bool lcl_FunctionAccessLoadWebServiceLink(OUString &rResult, ScDocument *pDoc, const OUString &rURI)
Definition: interpr7.cxx:259
static ScWebServiceLink * lcl_GetWebServiceLink(const sfx2::LinkManager *pLinkMgr, std::u16string_view rURL)
Definition: interpr7.cxx:243
void * p
SvBaseLink * pLink
aStr
ns
int i
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
double datastream_get_time(DebugTime nIdx)
Definition: datastream.cxx:40
ocWebservice
unsigned char sal_uInt8
sal_Int16 SCCOL
Definition: types.hxx:21
::boost::intrusive_ptr< ScMatrix > ScMatrixRef
Definition: types.hxx:25
sal_Int32 SCROW
Definition: types.hxx:17
INetProtocol