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 .
26 #include <formula/tokenarray.hxx>
27 #include <formula/errorcodes.hxx>
28 #include <svl/listener.hxx>
31 #include "interpretercontext.hxx"
32 #include "document.hxx"
33 #include "docoptio.hxx"
34 #include "formulalogger.hxx"
35 #include "formularesult.hxx"
36 #include "tokenarray.hxx"
37 #include "grouparealistener.hxx"
41 class StartListeningContext
;
42 class EndListeningContext
;
43 struct RefUpdateContext
;
44 struct RefUpdateInsertTabContext
;
45 struct RefUpdateDeleteTabContext
;
46 struct RefUpdateMoveTabContext
;
47 class CompileFormulaContext
;
48 class UpdatedRangeNames
;
54 enum class SvNumFormatType
: sal_Int16
;
56 struct AreaListenerKey
62 AreaListenerKey( const ScRange
& rRange
, bool bStartFixed
, bool bEndFixed
) :
63 maRange(rRange
), mbStartFixed(bStartFixed
), mbEndFixed(bEndFixed
) {}
65 bool operator < ( const AreaListenerKey
& r
) const;
68 typedef std::map
<AreaListenerKey
, sc::FormulaGroupAreaListener
> AreaListenersType
;
70 struct SC_DLLPUBLIC ScFormulaCellGroup
72 AreaListenersType m_AreaListeners
;
75 mutable size_t mnRefCount
;
77 std::optional
<ScTokenArray
> mpCode
;
78 ScFormulaCell
*mpTopCell
;
79 SCROW mnLength
; // How many of these do we have ?
81 SvNumFormatType mnFormatType
;
84 bool mbPartOfCycle
:1; // To flag FG's part of a cycle
86 sal_uInt8 meCalcState
;
89 ScFormulaCellGroup(const ScFormulaCellGroup
&) = delete;
90 const ScFormulaCellGroup
& operator=(const ScFormulaCellGroup
&) = delete;
91 ~ScFormulaCellGroup();
93 void setCode( const ScTokenArray
& rCode
);
95 ScDocument
& rDoc
, const ScAddress
& rPos
, formula::FormulaGrammar::Grammar eGram
);
97 sc::FormulaGroupAreaListener
* getAreaListener(
98 ScFormulaCell
** ppTopCell
, const ScRange
& rRange
, bool bStartFixed
, bool bEndFixed
);
100 void endAllGroupListening( ScDocument
& rDoc
);
103 inline void intrusive_ptr_add_ref(const ScFormulaCellGroup
*p
)
108 inline void intrusive_ptr_release(const ScFormulaCellGroup
*p
)
110 if( --p
->mnRefCount
== 0 )
114 enum class ScMatrixMode
: sal_uInt8
{
115 NONE
= 0, // No matrix formula
116 Formula
= 1, // Upper left matrix formula cell
117 Reference
= 2 // Remaining cells, via ocMatRef reference token
120 class SC_DLLPUBLIC ScFormulaCell final
: public SvtListener
123 ScFormulaCellGroupRef mxGroup
; // Group of formulae we're part of
124 bool bDirty
: 1; // Must be (re)calculated
125 bool bTableOpDirty
: 1; // Dirty flag for TableOp
126 bool bChanged
: 1; // Whether something changed regarding display/representation
127 bool bRunning
: 1; // Already interpreting right now
128 bool bCompile
: 1; // Must be (re)compiled
129 bool bSubTotal
: 1; // Cell is part of or contains a SubTotal
130 bool bIsIterCell
: 1; // Cell is part of a circular reference
131 bool bInChangeTrack
: 1; // Cell is in ChangeTrack
132 bool bNeedListening
: 1; // Listeners need to be re-established after UpdateReference
133 bool mbNeedsNumberFormat
: 1; // set the calculated number format as hard number format
134 bool mbAllowNumberFormatChange
: 1; /* allow setting further calculated
135 number formats as hard number format */
136 bool mbPostponedDirty
: 1; // if cell needs to be set dirty later
137 bool mbIsExtRef
: 1; // has references in ScExternalRefManager; never cleared after set
138 bool mbSeenInPath
: 1; // For detecting cycle involving formula groups and singleton formulacells
139 bool mbFreeFlying
: 1; // Cell is out of sheets interpreted, like in conditional format
140 ScMatrixMode cMatrixFlag
: 8;
141 sal_uInt16 nSeenInIteration
: 16; // Iteration cycle in which the cell was last encountered
142 SvNumFormatType nFormatType
: 16;
143 ScFormulaResult aResult
;
144 formula::FormulaGrammar::Grammar eTempGrammar
; // used between string (creation) and (re)compilation
145 // If this cell is in a cell group (mxGroup!=nullptr), then this pCode is a not-owning pointer
146 // to the mxGroup's mpCode, which owns the array. If the cell is not in a group, this is an owning pointer.
147 ScTokenArray
* pCode
; // The token array
148 ScDocument
& rDocument
;
149 ScFormulaCell
* pPrevious
;
150 ScFormulaCell
* pNext
;
151 ScFormulaCell
* pPreviousTrack
;
152 ScFormulaCell
* pNextTrack
;
155 * Update reference in response to cell copy-n-paste.
157 bool UpdateReferenceOnCopy(
158 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
);
160 ScFormulaCell( const ScFormulaCell
& ) = delete;
162 bool CheckComputeDependencies(sc::FormulaLogger::GroupScope
& rScope
, bool fromFirstRow
,
163 SCROW nStartOffset
, SCROW nEndOffset
, bool bCalcDependencyOnly
= false,
164 ScRangeList
* pSuccessfulDependencies
= nullptr,
165 ScAddress
* pFailedAndDirtiedAddress
= nullptr);
166 bool InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope
& aScope
,
167 bool& bDependencyComputed
,
168 bool& bDependencyCheckFailed
,
169 SCROW nStartOffset
, SCROW nEndOffset
);
170 bool InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope
& aScope
,
171 bool& bDependencyComputed
,
172 bool& bDependencyCheckFailed
);
173 bool InterpretInvariantFormulaGroup();
178 enum ScInterpretTailParameter
181 SCITP_FROM_ITERATION
,
182 SCITP_CLOSE_ITERATION_CIRCLE
184 void InterpretTail( ScInterpreterContext
&, ScInterpretTailParameter
);
186 void HandleStuffAfterParallelCalculation(ScInterpreter
* pInterpreter
);
188 enum CompareState
{ NotEqual
= 0, EqualInvariant
, EqualRelativeRef
};
192 virtual ~ScFormulaCell() override
;
194 ScFormulaCell
* Clone() const;
195 ScFormulaCell
* Clone( const ScAddress
& rPos
) const;
197 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
);
200 * Transfer the ownership of the passed token array instance to the
201 * formula cell being constructed. The caller <i>must not</i> pass a NULL
202 * token array pointer.
204 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
, std::unique_ptr
<ScTokenArray
> pArray
,
205 const formula::FormulaGrammar::Grammar eGrammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
206 ScMatrixMode cMatInd
= ScMatrixMode::NONE
);
208 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
, const ScTokenArray
& rArray
,
209 const formula::FormulaGrammar::Grammar eGrammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
210 ScMatrixMode cMatInd
= ScMatrixMode::NONE
);
212 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
, const ScFormulaCellGroupRef
& xGroup
,
213 const formula::FormulaGrammar::Grammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
214 ScMatrixMode
= ScMatrixMode::NONE
);
216 /** With formula string and grammar to compile with.
217 formula::FormulaGrammar::GRAM_DEFAULT effectively isformula::FormulaGrammar::GRAM_NATIVE_UI that
218 also includes formula::FormulaGrammar::CONV_UNSPECIFIED, therefore uses the address
219 convention associated with rPos::nTab by default. */
220 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
,
221 const OUString
& rFormula
,
222 const formula::FormulaGrammar::Grammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
223 ScMatrixMode cMatInd
= ScMatrixMode::NONE
);
225 ScFormulaCell(const ScFormulaCell
& rCell
, ScDocument
& rDoc
, const ScAddress
& rPos
, ScCloneFlags nCloneFlags
= ScCloneFlags::Default
);
227 void SetFreeFlying( bool b
) { mbFreeFlying
= b
; }
229 size_t GetHash() const;
231 OUString
GetFormula( const formula::FormulaGrammar::Grammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
232 const ScInterpreterContext
* pContext
= nullptr ) const;
233 OUString
GetFormula( sc::CompileFormulaContext
& rCxt
, const ScInterpreterContext
* pContext
= nullptr ) const;
235 void SetDirty( bool bDirtyFlag
=true );
237 // If setting entire document dirty after load, no broadcasts but still append to FormulaTree.
238 void SetDirtyAfterLoad();
239 void ResetTableOpDirtyVar();
240 void SetTableOpDirty();
242 bool IsDirtyOrInTableOpDirty() const
244 return bDirty
|| (bTableOpDirty
&& rDocument
.IsInInterpreterTableOp());
247 bool GetDirty() const { return bDirty
; }
249 bool NeedsListening() const { return bNeedListening
; }
250 void SetNeedsListening( bool bVar
);
251 void SetNeedsDirty( bool bVar
);
252 void SetNeedNumberFormat( bool bVal
);
253 bool NeedsNumberFormat() const { return mbNeedsNumberFormat
;}
254 SvNumFormatType
GetFormatType() const { return nFormatType
; }
255 void Compile(const OUString
& rFormula
,
257 const formula::FormulaGrammar::Grammar
);
259 sc::CompileFormulaContext
& rCxt
, const OUString
& rFormula
, bool bNoListening
= false );
261 void CompileTokenArray( bool bNoListening
= false );
262 void CompileTokenArray( sc::CompileFormulaContext
& rCxt
, bool bNoListening
= false );
263 void CompileXML( sc::CompileFormulaContext
& rCxt
, ScProgress
& rProgress
); // compile temporary string tokens
264 void CalcAfterLoad( sc::CompileFormulaContext
& rCxt
, bool bStartListening
);
265 bool MarkUsedExternalReferences();
266 // Returns true if the cell was interpreted as part of the formula group.
267 // The parameters may limit which subset of the formula group should be interpreted, if possible.
268 bool Interpret(SCROW nStartOffset
= -1, SCROW nEndOffset
= -1);
269 bool IsIterCell() const { return bIsIterCell
; }
270 sal_uInt16
GetSeenInIteration() const { return nSeenInIteration
; }
272 bool HasOneReference( ScRange
& r
) const;
273 /* Checks if the formula contains reference list that can be
274 expressed by one reference (like A1;A2;A3:A5 -> A1:A5). The
275 reference list is not required to be sorted (i.e. A3;A1;A2 is
276 still recognized as A1:A3), but no overlapping is allowed.
277 If one reference is recognized, the rRange is filled.
279 It is similar to HasOneReference(), but more general.
281 bool HasRefListExpressibleAsOneReference(ScRange
& rRange
) const;
283 enum class RelNameRef
285 NONE
, ///< no relative reference from named expression
286 SINGLE
, ///< only single cell relative reference
287 DOUBLE
///< at least one range relative reference from named expression
289 RelNameRef
HasRelNameReference() const;
291 bool UpdateReference(
292 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
= nullptr, const ScAddress
* pUndoCellPos
= nullptr );
295 * Shift the position of formula cell as part of reference update.
297 * @return true if the position has shifted, false otherwise.
299 bool UpdatePosOnShift( const sc::RefUpdateContext
& rCxt
);
302 * Update reference in response to cell insertion or deletion.
304 bool UpdateReferenceOnShift(
305 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
);
308 * Update reference in response to cell move.
310 bool UpdateReferenceOnMove(
311 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
);
313 void TransposeReference();
314 void UpdateTranspose( const ScRange
& rSource
, const ScAddress
& rDest
,
315 ScDocument
* pUndoDoc
);
317 void UpdateGrow( const ScRange
& rArea
, SCCOL nGrowX
, SCROW nGrowY
);
319 void UpdateInsertTab( const sc::RefUpdateInsertTabContext
& rCxt
);
320 void UpdateInsertTabAbs(SCTAB nTable
);
321 void UpdateDeleteTab( const sc::RefUpdateDeleteTabContext
& rCxt
);
322 void UpdateMoveTab( const sc::RefUpdateMoveTabContext
& rCxt
, SCTAB nTabNo
);
323 bool TestTabRefAbs(SCTAB nTable
);
324 void UpdateCompile( bool bForceIfNameInUse
);
325 void FindRangeNamesInUse(sc::UpdatedRangeNames
& rIndexes
) const;
326 bool IsSubTotal() const { return bSubTotal
;}
327 bool IsChanged() const { return bChanged
;}
328 void SetChanged(bool b
);
329 bool IsEmpty(); // formula::svEmptyCell result
330 // display as empty string if formula::svEmptyCell result
331 bool IsEmptyDisplayedAsString();
332 bool IsValue(); // also true if formula::svEmptyCell
333 bool IsValueNoError();
334 bool IsValueNoError() const;
336 const svl::SharedString
& GetString();
339 * Get a numeric value without potentially triggering re-calculation.
341 double GetRawValue() const;
344 * Get a string value without potentially triggering re-calculation.
346 const svl::SharedString
& GetRawString() const;
347 const ScMatrix
* GetMatrix();
348 bool GetMatrixOrigin( const ScDocument
& rDoc
, ScAddress
& rPos
) const;
349 void GetResultDimensions( SCSIZE
& rCols
, SCSIZE
& rRows
);
350 sc::MatrixEdge
GetMatrixEdge( const ScDocument
& rDoc
, ScAddress
& rOrgPos
) const;
351 FormulaError
GetErrCode(); // interpret first if necessary
352 FormulaError
GetRawError() const; // don't interpret, just return code or result error
353 bool GetErrorOrValue( FormulaError
& rErr
, double& rVal
);
354 sc::FormulaResultValue
GetResult();
355 sc::FormulaResultValue
GetResult() const;
356 ScMatrixMode
GetMatrixFlag() const { return cMatrixFlag
;}
357 ScTokenArray
* GetCode() { return pCode
;}
358 const ScTokenArray
* GetCode() const { return pCode
;}
360 void SetCode( std::unique_ptr
<ScTokenArray
> pNew
);
362 bool IsRunning() const { return bRunning
;}
363 void SetRunning( bool bVal
);
364 void CompileDBFormula( sc::CompileFormulaContext
& rCxt
);
365 void CompileColRowNameFormula( sc::CompileFormulaContext
& rCxt
);
366 ScFormulaCell
* GetPrevious() const { return pPrevious
; }
367 ScFormulaCell
* GetNext() const { return pNext
; }
368 void SetPrevious( ScFormulaCell
* pF
);
369 void SetNext( ScFormulaCell
* pF
);
370 ScFormulaCell
* GetPreviousTrack() const { return pPreviousTrack
; }
371 ScFormulaCell
* GetNextTrack() const { return pNextTrack
; }
372 void SetPreviousTrack( ScFormulaCell
* pF
);
373 void SetNextTrack( ScFormulaCell
* pF
);
375 virtual void Notify( const SfxHint
& rHint
) override
;
376 virtual void Query( SvtListener::QueryBase
& rQuery
) const override
;
378 void SetCompile( bool bVal
);
379 ScDocument
& GetDocument() const { return rDocument
;}
380 void SetMatColsRows( SCCOL nCols
, SCROW nRows
);
381 void GetMatColsRows( SCCOL
& nCols
, SCROW
& nRows
) const;
383 // cell belongs to ChangeTrack and not to the real document
384 void SetInChangeTrack( bool bVal
);
385 bool IsInChangeTrack() const { return bInChangeTrack
;}
387 // For import filters!
388 void AddRecalcMode( ScRecalcMode
);
389 /** For import only: set a double result. */
390 void SetHybridDouble( double n
);
391 /** For import only: set a string result.
392 If for whatever reason you have to use both, SetHybridDouble() and
393 SetHybridString() or SetHybridFormula(), use SetHybridDouble() first
394 for performance reasons.*/
395 void SetHybridString( const svl::SharedString
& r
);
396 /** For import only: set an empty cell result to be displayed as empty string.
397 If for whatever reason you have to use both, SetHybridDouble() and
398 SetHybridEmptyDisplayedAsString() or SetHybridFormula(), use
399 SetHybridDouble() first for performance reasons and use
400 SetHybridEmptyDisplayedAsString() last because SetHybridDouble() and
401 SetHybridString() will override it.*/
402 void SetHybridEmptyDisplayedAsString();
403 /** For import only: set a temporary formula string to be compiled later.
404 If for whatever reason you have to use both, SetHybridDouble() and
405 SetHybridString() or SetHybridFormula(), use SetHybridDouble() first
406 for performance reasons.*/
407 void SetHybridFormula(
408 const OUString
& r
, const formula::FormulaGrammar::Grammar eGrammar
);
410 OUString
GetHybridFormula() const;
412 void SetResultMatrix( SCCOL nCols
, SCROW nRows
, const ScConstMatrixRef
& pMat
, const formula::FormulaToken
* pUL
);
414 /** For import only: set a double result.
415 Use this instead of SetHybridDouble() if there is no (temporary)
416 formula string because the formula is present as a token array, as it
417 is the case for binary Excel import.
419 void SetResultDouble( double n
);
421 void SetResultToken( const formula::FormulaToken
* pToken
);
423 const svl::SharedString
& GetResultString() const;
425 bool HasHybridStringResult() const;
427 /* Sets the shared code array to error state in addition to the cell result */
428 void SetErrCode( FormulaError n
);
430 /* Sets just the result to error */
431 void SetResultError( FormulaError n
);
433 bool IsHyperLinkCell() const;
434 std::unique_ptr
<EditTextObject
> CreateURLObject();
435 void GetURLResult( OUString
& rURL
, OUString
& rCellText
);
437 /** Determines whether or not the result string contains more than one paragraph */
438 bool IsMultilineResult();
440 bool NeedsInterpret() const
443 // Shortcut to force return of current value and not enter Interpret()
444 // as we're looping over all iteration cells.
447 if (!IsDirtyOrInTableOpDirty())
450 return rDocument
.GetAutoCalc() || (cMatrixFlag
!= ScMatrixMode::NONE
)
451 || (pCode
->IsRecalcModeMustAfterImport() && !pCode
->IsRecalcModeAlways());
454 void MaybeInterpret()
456 if (NeedsInterpret())
458 if (bRunning
&& !rDocument
.GetDocOptions().IsIter() && rDocument
.IsThreadedGroupCalcInProgress())
460 // This is actually copied from Interpret()'s if(bRunning)
461 // block that once caught this circular reference but now is
462 // prepended with various threaded group calc things which the
463 // assert() below is supposed to fail on when entering again.
464 // Nevertheless, we need some state here the caller can obtain.
465 aResult
.SetResultError( FormulaError::CircularReference
);
469 assert(!rDocument
.IsThreadedGroupCalcInProgress());
476 * Turn a non-grouped cell into the top of a grouped cell.
478 ScFormulaCellGroupRef
CreateCellGroup( SCROW nLen
, bool bInvariant
);
479 const ScFormulaCellGroupRef
& GetCellGroup() const { return mxGroup
;}
480 void SetCellGroup( const ScFormulaCellGroupRef
&xRef
);
482 CompareState
CompareByTokenArray( const ScFormulaCell
& rOther
) const;
484 bool InterpretFormulaGroup(SCROW nStartOffset
= -1, SCROW nEndOffset
= -1);
486 // nOnlyNames may be one or more of SC_LISTENING_NAMES_*
487 void StartListeningTo( ScDocument
& rDoc
);
488 void StartListeningTo( sc::StartListeningContext
& rCxt
);
490 ScDocument
& rDoc
, ScTokenArray
* pArr
= nullptr, ScAddress aPos
= ScAddress() );
491 void EndListeningTo( sc::EndListeningContext
& rCxt
);
493 bool IsShared() const;
494 bool IsSharedTop() const;
495 SCROW
GetSharedTopRow() const;
496 SCROW
GetSharedLength() const;
498 // An estimate of the number of cells referenced by the formula
499 sal_Int32
GetWeight() const;
501 ScTokenArray
* GetSharedCode();
502 const ScTokenArray
* GetSharedCode() const;
504 void SyncSharedCode();
506 bool IsPostponedDirty() const { return mbPostponedDirty
;}
508 void SetIsExtRef() { mbIsExtRef
= true; }
509 bool GetSeenInPath() const { return mbSeenInPath
; }
510 void SetSeenInPath(bool bSet
) { mbSeenInPath
= bSet
; }
512 #if DUMP_COLUMN_STORAGE
517 inline bool ScDocument::IsInFormulaTree( const ScFormulaCell
* pCell
) const { return pCell
->GetPrevious() || pFormulaTree
== pCell
; }
518 inline bool ScDocument::IsInFormulaTrack( const ScFormulaCell
* pCell
) const { return pCell
->GetPreviousTrack() || pFormulaTrack
== pCell
; }
520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */