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 bool InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope
& aScope
,
165 bool& bDependencyComputed
,
166 bool& bDependencyCheckFailed
,
167 SCROW nStartOffset
, SCROW nEndOffset
);
168 bool InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope
& aScope
,
169 bool& bDependencyComputed
,
170 bool& bDependencyCheckFailed
);
171 bool InterpretInvariantFormulaGroup();
176 enum ScInterpretTailParameter
179 SCITP_FROM_ITERATION
,
180 SCITP_CLOSE_ITERATION_CIRCLE
182 void InterpretTail( ScInterpreterContext
&, ScInterpretTailParameter
);
184 void HandleStuffAfterParallelCalculation(ScInterpreter
* pInterpreter
);
186 enum CompareState
{ NotEqual
= 0, EqualInvariant
, EqualRelativeRef
};
190 virtual ~ScFormulaCell() override
;
192 ScFormulaCell
* Clone() const;
193 ScFormulaCell
* Clone( const ScAddress
& rPos
) const;
195 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
);
198 * Transfer the ownership of the passed token array instance to the
199 * formula cell being constructed. The caller <i>must not</i> pass a NULL
200 * token array pointer.
202 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
, std::unique_ptr
<ScTokenArray
> pArray
,
203 const formula::FormulaGrammar::Grammar eGrammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
204 ScMatrixMode cMatInd
= ScMatrixMode::NONE
);
206 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
, const ScTokenArray
& rArray
,
207 const formula::FormulaGrammar::Grammar eGrammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
208 ScMatrixMode cMatInd
= ScMatrixMode::NONE
);
210 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
, const ScFormulaCellGroupRef
& xGroup
,
211 const formula::FormulaGrammar::Grammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
212 ScMatrixMode
= ScMatrixMode::NONE
);
214 /** With formula string and grammar to compile with.
215 formula::FormulaGrammar::GRAM_DEFAULT effectively isformula::FormulaGrammar::GRAM_NATIVE_UI that
216 also includes formula::FormulaGrammar::CONV_UNSPECIFIED, therefore uses the address
217 convention associated with rPos::nTab by default. */
218 ScFormulaCell( ScDocument
& rDoc
, const ScAddress
& rPos
,
219 const OUString
& rFormula
,
220 const formula::FormulaGrammar::Grammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
221 ScMatrixMode cMatInd
= ScMatrixMode::NONE
);
223 ScFormulaCell(const ScFormulaCell
& rCell
, ScDocument
& rDoc
, const ScAddress
& rPos
, ScCloneFlags nCloneFlags
= ScCloneFlags::Default
);
225 void SetFreeFlying( bool b
) { mbFreeFlying
= b
; }
227 size_t GetHash() const;
229 OUString
GetFormula( const formula::FormulaGrammar::Grammar
= formula::FormulaGrammar::GRAM_DEFAULT
,
230 const ScInterpreterContext
* pContext
= nullptr ) const;
231 OUString
GetFormula( sc::CompileFormulaContext
& rCxt
, const ScInterpreterContext
* pContext
= nullptr ) const;
233 void SetDirty( bool bDirtyFlag
=true );
235 // If setting entire document dirty after load, no broadcasts but still append to FormulaTree.
236 void SetDirtyAfterLoad();
237 void ResetTableOpDirtyVar();
238 void SetTableOpDirty();
240 bool IsDirtyOrInTableOpDirty() const
242 return bDirty
|| (bTableOpDirty
&& rDocument
.IsInInterpreterTableOp());
245 bool GetDirty() const { return bDirty
; }
247 bool NeedsListening() const { return bNeedListening
; }
248 void SetNeedsListening( bool bVar
);
249 void SetNeedsDirty( bool bVar
);
250 void SetNeedNumberFormat( bool bVal
);
251 bool NeedsNumberFormat() const { return mbNeedsNumberFormat
;}
252 SvNumFormatType
GetFormatType() const { return nFormatType
; }
253 void Compile(const OUString
& rFormula
,
255 const formula::FormulaGrammar::Grammar
);
257 sc::CompileFormulaContext
& rCxt
, const OUString
& rFormula
, bool bNoListening
= false );
259 void CompileTokenArray( bool bNoListening
= false );
260 void CompileTokenArray( sc::CompileFormulaContext
& rCxt
, bool bNoListening
= false );
261 void CompileXML( sc::CompileFormulaContext
& rCxt
, ScProgress
& rProgress
); // compile temporary string tokens
262 void CalcAfterLoad( sc::CompileFormulaContext
& rCxt
, bool bStartListening
);
263 bool MarkUsedExternalReferences();
264 // Returns true if the cell was interpreted as part of the formula group.
265 // The parameters may limit which subset of the formula group should be interpreted, if possible.
266 bool Interpret(SCROW nStartOffset
= -1, SCROW nEndOffset
= -1);
267 bool IsIterCell() const { return bIsIterCell
; }
268 sal_uInt16
GetSeenInIteration() const { return nSeenInIteration
; }
270 bool HasOneReference( ScRange
& r
) const;
271 /* Checks if the formula contains reference list that can be
272 expressed by one reference (like A1;A2;A3:A5 -> A1:A5). The
273 reference list is not required to be sorted (i.e. A3;A1;A2 is
274 still recognized as A1:A3), but no overlapping is allowed.
275 If one reference is recognized, the rRange is filled.
277 It is similar to HasOneReference(), but more general.
279 bool HasRefListExpressibleAsOneReference(ScRange
& rRange
) const;
281 enum class RelNameRef
283 NONE
, ///< no relative reference from named expression
284 SINGLE
, ///< only single cell relative reference
285 DOUBLE
///< at least one range relative reference from named expression
287 RelNameRef
HasRelNameReference() const;
289 bool UpdateReference(
290 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
= nullptr, const ScAddress
* pUndoCellPos
= nullptr );
293 * Shift the position of formula cell as part of reference update.
295 * @return true if the position has shifted, false otherwise.
297 bool UpdatePosOnShift( const sc::RefUpdateContext
& rCxt
);
300 * Update reference in response to cell insertion or deletion.
302 bool UpdateReferenceOnShift(
303 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
);
306 * Update reference in response to cell move.
308 bool UpdateReferenceOnMove(
309 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
);
311 void TransposeReference();
312 void UpdateTranspose( const ScRange
& rSource
, const ScAddress
& rDest
,
313 ScDocument
* pUndoDoc
);
315 void UpdateGrow( const ScRange
& rArea
, SCCOL nGrowX
, SCROW nGrowY
);
317 void UpdateInsertTab( const sc::RefUpdateInsertTabContext
& rCxt
);
318 void UpdateInsertTabAbs(SCTAB nTable
);
319 void UpdateDeleteTab( const sc::RefUpdateDeleteTabContext
& rCxt
);
320 void UpdateMoveTab( const sc::RefUpdateMoveTabContext
& rCxt
, SCTAB nTabNo
);
321 bool TestTabRefAbs(SCTAB nTable
);
322 void UpdateCompile( bool bForceIfNameInUse
);
323 void FindRangeNamesInUse(sc::UpdatedRangeNames
& rIndexes
) const;
324 bool IsSubTotal() const { return bSubTotal
;}
325 bool IsChanged() const { return bChanged
;}
326 void SetChanged(bool b
);
327 bool IsEmpty(); // formula::svEmptyCell result
328 // display as empty string if formula::svEmptyCell result
329 bool IsEmptyDisplayedAsString();
330 bool IsValue(); // also true if formula::svEmptyCell
331 bool IsValueNoError();
332 bool IsValueNoError() const;
334 const svl::SharedString
& GetString();
337 * Get a numeric value without potentially triggering re-calculation.
339 double GetRawValue() const;
342 * Get a string value without potentially triggering re-calculation.
344 const svl::SharedString
& GetRawString() const;
345 const ScMatrix
* GetMatrix();
346 bool GetMatrixOrigin( const ScDocument
& rDoc
, ScAddress
& rPos
) const;
347 void GetResultDimensions( SCSIZE
& rCols
, SCSIZE
& rRows
);
348 sc::MatrixEdge
GetMatrixEdge( const ScDocument
& rDoc
, ScAddress
& rOrgPos
) const;
349 FormulaError
GetErrCode(); // interpret first if necessary
350 FormulaError
GetRawError() const; // don't interpret, just return code or result error
351 bool GetErrorOrValue( FormulaError
& rErr
, double& rVal
);
352 sc::FormulaResultValue
GetResult();
353 sc::FormulaResultValue
GetResult() const;
354 ScMatrixMode
GetMatrixFlag() const { return cMatrixFlag
;}
355 ScTokenArray
* GetCode() { return pCode
;}
356 const ScTokenArray
* GetCode() const { return pCode
;}
358 void SetCode( std::unique_ptr
<ScTokenArray
> pNew
);
360 bool IsRunning() const { return bRunning
;}
361 void SetRunning( bool bVal
);
362 void CompileDBFormula( sc::CompileFormulaContext
& rCxt
);
363 void CompileColRowNameFormula( sc::CompileFormulaContext
& rCxt
);
364 ScFormulaCell
* GetPrevious() const { return pPrevious
; }
365 ScFormulaCell
* GetNext() const { return pNext
; }
366 void SetPrevious( ScFormulaCell
* pF
);
367 void SetNext( ScFormulaCell
* pF
);
368 ScFormulaCell
* GetPreviousTrack() const { return pPreviousTrack
; }
369 ScFormulaCell
* GetNextTrack() const { return pNextTrack
; }
370 void SetPreviousTrack( ScFormulaCell
* pF
);
371 void SetNextTrack( ScFormulaCell
* pF
);
373 virtual void Notify( const SfxHint
& rHint
) override
;
374 virtual void Query( SvtListener::QueryBase
& rQuery
) const override
;
376 void SetCompile( bool bVal
);
377 ScDocument
& GetDocument() const { return rDocument
;}
378 void SetMatColsRows( SCCOL nCols
, SCROW nRows
);
379 void GetMatColsRows( SCCOL
& nCols
, SCROW
& nRows
) const;
381 // cell belongs to ChangeTrack and not to the real document
382 void SetInChangeTrack( bool bVal
);
383 bool IsInChangeTrack() const { return bInChangeTrack
;}
385 // For import filters!
386 void AddRecalcMode( ScRecalcMode
);
387 /** For import only: set a double result. */
388 void SetHybridDouble( double n
);
389 /** For import only: set a string result.
390 If for whatever reason you have to use both, SetHybridDouble() and
391 SetHybridString() or SetHybridFormula(), use SetHybridDouble() first
392 for performance reasons.*/
393 void SetHybridString( const svl::SharedString
& r
);
394 /** For import only: set an empty cell result to be displayed as empty string.
395 If for whatever reason you have to use both, SetHybridDouble() and
396 SetHybridEmptyDisplayedAsString() or SetHybridFormula(), use
397 SetHybridDouble() first for performance reasons and use
398 SetHybridEmptyDisplayedAsString() last because SetHybridDouble() and
399 SetHybridString() will override it.*/
400 void SetHybridEmptyDisplayedAsString();
401 /** For import only: set a temporary formula string to be compiled later.
402 If for whatever reason you have to use both, SetHybridDouble() and
403 SetHybridString() or SetHybridFormula(), use SetHybridDouble() first
404 for performance reasons.*/
405 void SetHybridFormula(
406 const OUString
& r
, const formula::FormulaGrammar::Grammar eGrammar
);
408 OUString
GetHybridFormula() const;
410 void SetResultMatrix( SCCOL nCols
, SCROW nRows
, const ScConstMatrixRef
& pMat
, const formula::FormulaToken
* pUL
);
412 /** For import only: set a double result.
413 Use this instead of SetHybridDouble() if there is no (temporary)
414 formula string because the formula is present as a token array, as it
415 is the case for binary Excel import.
417 void SetResultDouble( double n
);
419 void SetResultToken( const formula::FormulaToken
* pToken
);
421 const svl::SharedString
& GetResultString() const;
423 bool HasHybridStringResult() const;
425 /* Sets the shared code array to error state in addition to the cell result */
426 void SetErrCode( FormulaError n
);
428 /* Sets just the result to error */
429 void SetResultError( FormulaError n
);
431 bool IsHyperLinkCell() const;
432 std::unique_ptr
<EditTextObject
> CreateURLObject();
433 void GetURLResult( OUString
& rURL
, OUString
& rCellText
);
435 /** Determines whether or not the result string contains more than one paragraph */
436 bool IsMultilineResult();
438 bool NeedsInterpret() const
441 // Shortcut to force return of current value and not enter Interpret()
442 // as we're looping over all iteration cells.
445 if (!IsDirtyOrInTableOpDirty())
448 return rDocument
.GetAutoCalc() || (cMatrixFlag
!= ScMatrixMode::NONE
)
449 || (pCode
->IsRecalcModeMustAfterImport() && !pCode
->IsRecalcModeAlways());
452 void MaybeInterpret()
454 if (NeedsInterpret())
456 if (bRunning
&& !rDocument
.GetDocOptions().IsIter() && rDocument
.IsThreadedGroupCalcInProgress())
458 // This is actually copied from Interpret()'s if(bRunning)
459 // block that once caught this circular reference but now is
460 // prepended with various threaded group calc things which the
461 // assert() below is supposed to fail on when entering again.
462 // Nevertheless, we need some state here the caller can obtain.
463 aResult
.SetResultError( FormulaError::CircularReference
);
467 assert(!rDocument
.IsThreadedGroupCalcInProgress());
474 * Turn a non-grouped cell into the top of a grouped cell.
476 ScFormulaCellGroupRef
CreateCellGroup( SCROW nLen
, bool bInvariant
);
477 const ScFormulaCellGroupRef
& GetCellGroup() const { return mxGroup
;}
478 void SetCellGroup( const ScFormulaCellGroupRef
&xRef
);
480 CompareState
CompareByTokenArray( const ScFormulaCell
& rOther
) const;
482 bool InterpretFormulaGroup(SCROW nStartOffset
= -1, SCROW nEndOffset
= -1);
484 // nOnlyNames may be one or more of SC_LISTENING_NAMES_*
485 void StartListeningTo( ScDocument
& rDoc
);
486 void StartListeningTo( sc::StartListeningContext
& rCxt
);
488 ScDocument
& rDoc
, ScTokenArray
* pArr
= nullptr, ScAddress aPos
= ScAddress() );
489 void EndListeningTo( sc::EndListeningContext
& rCxt
);
491 bool IsShared() const;
492 bool IsSharedTop() const;
493 SCROW
GetSharedTopRow() const;
494 SCROW
GetSharedLength() const;
496 // An estimate of the number of cells referenced by the formula
497 sal_Int32
GetWeight() const;
499 ScTokenArray
* GetSharedCode();
500 const ScTokenArray
* GetSharedCode() const;
502 void SyncSharedCode();
504 bool IsPostponedDirty() const { return mbPostponedDirty
;}
506 void SetIsExtRef() { mbIsExtRef
= true; }
507 bool GetSeenInPath() const { return mbSeenInPath
; }
508 void SetSeenInPath(bool bSet
) { mbSeenInPath
= bSet
; }
510 #if DUMP_COLUMN_STORAGE
515 inline bool ScDocument::IsInFormulaTree( const ScFormulaCell
* pCell
) const { return pCell
->GetPrevious() || pFormulaTree
== pCell
; }
516 inline bool ScDocument::IsInFormulaTrack( const ScFormulaCell
* pCell
) const { return pCell
->GetPreviousTrack() || pFormulaTrack
== pCell
; }
518 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */