Kaydet (Commit) b0c92c9d authored tarafından Michael Meeks's avatar Michael Meeks Kaydeden (comit) Kohei Yoshida

initial detection of similar formula runs.

Change-Id: I5659c46668a0d0a6ef67368a4e6bbf0d99a6a80e
üst a1e22bde
......@@ -25,6 +25,7 @@
#include <set>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <tools/mempool.hxx>
#include <svl/listener.hxx>
......@@ -331,6 +332,32 @@ private:
::std::vector<Item> maArray;
};
struct ScSimilarFormulaDelta;
struct SC_DLLPUBLIC ScFormulaCellGroup
{
sal_Int32 mnRefCount;
ScSimilarFormulaDelta *mpDelta; // difference between items in column
sal_Int32 mnStart; // Start offset of that cell
sal_Int32 mnLength; // How many of these do we have ?
ScFormulaCellGroup();
~ScFormulaCellGroup();
bool IsCompatible( ScSimilarFormulaDelta *pDelta );
};
inline void intrusive_ptr_add_ref(ScFormulaCellGroup *p)
{
p->mnRefCount++;
}
inline void intrusive_ptr_release(ScFormulaCellGroup *p)
{
if( --p->mnRefCount == 0 )
delete p;
}
typedef ::boost::intrusive_ptr<ScFormulaCellGroup> ScFormulaCellGroupRef;
enum ScMatrixMode {
MM_NONE = 0, // No matrix formula
MM_FORMULA = 1, // Upper left matrix formula cell
......@@ -349,6 +376,7 @@ private:
ScFormulaCell* pNext;
ScFormulaCell* pPreviousTrack;
ScFormulaCell* pNextTrack;
ScFormulaCellGroupRef xGroup; // re-factoring hack - group of formulae we're part of.
sal_uLong nFormatIndex; // Number format set by calculation
short nFormatType; // Number format type set by calculation
sal_uInt16 nSeenInIteration; // Iteration cycle in which the cell was last encountered
......@@ -558,6 +586,14 @@ public:
bool IsMultilineResult();
void MaybeInterpret();
// Temporary formula cell grouping API
ScFormulaCellGroupRef GetCellGroup()
{ return xGroup; }
void SetCellGroup( const ScFormulaCellGroupRef &xRef )
{ xGroup = xRef; }
ScSimilarFormulaDelta *BuildDeltaTo( ScFormulaCell *pOther );
void ReleaseDelta( ScSimilarFormulaDelta *pDelta );
};
// Iterator for references in a formula cell
......
......@@ -110,6 +110,7 @@ class ScColumn
ScAttrArray* pAttrArray;
ScDocument* pDocument;
bool bDirtyGroups; /// formula groups are dirty.
friend class ScDocument; // for FillInfo
friend class ScDocumentIterator;
......@@ -146,6 +147,7 @@ public:
void ReserveSize( SCSIZE nSize );
void SwapRow( SCROW nRow1, SCROW nRow2 );
void SwapCell( SCROW nRow, ScColumn& rCol);
void RebuildFormulaGroups();
bool HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const;
bool HasAttribSelection( const ScMarkData& rMark, sal_uInt16 nMask ) const;
......
......@@ -1687,6 +1687,104 @@ void ScFormulaCell::CompileColRowNameFormula()
}
}
struct ScSimilarFormulaDelta : std::vector< size_t >
{
// we really want to be a lot more descriptive than this
bool IsCompatible( ScSimilarFormulaDelta *pDelta )
{
if ( size() != pDelta->size() )
return false;
for ( size_t i = 0; i < size(); i++ )
{
if ( (*this)[ i ] != (*pDelta)[ i ] )
return false;
}
return true;
}
void push_delta( const ScSingleRefData& a, const ScSingleRefData& b )
{
push_back( b.nCol - a.nCol );
push_back( b.nRow - a.nRow );
push_back( b.nTab - a.nTab );
}
};
bool ScFormulaCellGroup::IsCompatible( ScSimilarFormulaDelta *pDelta )
{
return pDelta && mpDelta && mpDelta->IsCompatible( pDelta );
}
/// compare formulae tokens and build a series of deltas describing
/// the difference - ie. the result, when added to this
/// formulae should produce pOther
ScSimilarFormulaDelta *ScFormulaCell::BuildDeltaTo( ScFormulaCell *pOtherCell )
{
// FIXME: TODO - M1
// if ( kohei_comparison_hash_not_equal( mnHash, pOther->mnHash )
// return NULL;
FormulaToken **pThis = pCode->GetCode();
sal_uInt16 pThisLen = pCode->GetCodeLen();
FormulaToken **pOther = pOtherCell->pCode->GetCode();
sal_uInt16 pOtherLen = pOtherCell->pCode->GetCodeLen();
if ( !pThis || !pOther )
{
fprintf( stderr, "no compiled code for cells !" );
return NULL;
}
if ( pThisLen != pOtherLen )
{
fprintf( stderr, "different length formulae !" );
return NULL;
}
// check we are basically the same function
for ( sal_uInt16 i = 0; i < pThisLen; i++ )
{
if ( pThis[ i ]->GetType() != pOther[ i ]->GetType() ||
pThis[ i ]->GetOpCode() != pOther[ i ]->GetOpCode() ||
pThis[ i ]->GetParamCount() != pOther[ i ]->GetParamCount() )
{
fprintf( stderr, "Incompatible type, op-code or param counts\n" );
return NULL;
}
if( pThis[ i ]->GetType() == formula::svMatrix ||
pOther[ i ]->GetType() == formula::svMatrix )
{
fprintf( stderr, "Ignoring matrix formulae for now\n" );
return NULL;
}
}
fprintf( stderr, "matching formulae !\n" );
ScSimilarFormulaDelta *pDelta = new ScSimilarFormulaDelta();
for ( sal_uInt16 i = 0; i < pThisLen; i++ )
{
ScToken *pThisTok = static_cast< ScToken * >( pThis[ i ] );
ScToken *pOtherTok = static_cast< ScToken * >( pOther[ i ] );
const ScSingleRefData& aThisRef = pThisTok->GetSingleRef();
const ScSingleRefData& aOtherRef = pOtherTok->GetSingleRef();
pDelta->push_delta( aThisRef, aOtherRef );
const ScSingleRefData& aThisRef2 = pThisTok->GetSingleRef2();
const ScSingleRefData& aOtherRef2 = pOtherTok->GetSingleRef2();
pDelta->push_delta( aThisRef2, aOtherRef2 );
}
return pDelta;
}
void ScFormulaCell::ReleaseDelta( ScSimilarFormulaDelta *pDelta )
{
delete pDelta;
}
// ============================================================================
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -85,7 +85,8 @@ ScColumn::ScColumn() :
maScriptTypes(MAXROWCOUNT),
nCol( 0 ),
pAttrArray( NULL ),
pDocument( NULL )
pDocument( NULL ),
bDirtyGroups( true )
{
}
......@@ -103,6 +104,7 @@ void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc)
nTab = nNewTab;
pDocument = pDoc;
pAttrArray = new ScAttrArray( nCol, nTab, pDocument );
bDirtyGroups = true;
}
......@@ -851,6 +853,8 @@ void ScColumn::SwapRow(SCROW nRow1, SCROW nRow2)
::std::swap( pCell1, pCell2 );
}
bDirtyGroups = true;
// from here: first cell (pCell1, nIndex1) exists always
ScAddress aPos1( nCol, nRow1, nTab );
......@@ -1010,6 +1014,8 @@ void ScColumn::SwapCell( SCROW nRow, ScColumn& rCol)
return;
}
bDirtyGroups = true;
// from here: own cell (pCell1, nIndex1) exists always
ScFormulaCell* pFmlaCell1 = (pCell1->GetCellType() == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0;
......@@ -1129,6 +1135,8 @@ void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize )
if ( i >= maItems.size() )
return ;
bDirtyGroups = true;
bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( false ); // avoid recalculations
......@@ -1646,6 +1654,10 @@ void ScColumn::SwapCol(ScColumn& rCol)
pAttrArray->SetCol(nCol);
rCol.pAttrArray->SetCol(rCol.nCol);
bool bDirty = bDirtyGroups;
bDirtyGroups = rCol.bDirtyGroups;
rCol.bDirtyGroups = bDirty;
SCSIZE i;
for (i = 0; i < maItems.size(); i++)
{
......@@ -2008,6 +2020,8 @@ void ScColumn::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
void ScColumn::UpdateCompile( bool bForceIfNameInUse )
{
if ( !maItems.empty() )
{
fprintf( stderr, "UpdateCompile - column !?\n" );
for (SCSIZE i = 0; i < maItems.size(); i++)
{
ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell;
......@@ -2019,6 +2033,8 @@ void ScColumn::UpdateCompile( bool bForceIfNameInUse )
Search( nRow, i ); // Listener deleted/inserted?
}
}
RebuildFormulaGroups();
}
}
......
......@@ -1431,6 +1431,7 @@ void ScColumn::CellStorageModified()
}
cout << "-- end" << endl;
#endif
RebuildFormulaGroups();
}
void ScColumn::CopyScriptTypesToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const
......
......@@ -120,6 +120,7 @@ void ScColumn::Insert( SCROW nRow, ScBaseCell* pNewCell )
ScAddress( nCol, nRow, nTab ), pNewCell ) );
}
}
bDirtyGroups = true;
}
......@@ -133,6 +134,8 @@ void ScColumn::Insert( SCROW nRow, sal_uInt32 nNumberFormat, ScBaseCell* pCell )
short eNewType = pDocument->GetFormatTable()->GetType(nNumberFormat);
if (!pDocument->GetFormatTable()->IsCompatible(eOldType, eNewType))
ApplyAttr( nRow, SfxUInt32Item( ATTR_VALUE_FORMAT, (sal_uInt32) nNumberFormat) );
bDirtyGroups = true;
}
......@@ -142,6 +145,7 @@ void ScColumn::Append( SCROW nRow, ScBaseCell* pCell )
maItems.back().pCell = pCell;
maItems.back().nRow = nRow;
bDirtyGroups = true;
maTextWidths.set<unsigned short>(nRow, TEXTWIDTH_DIRTY);
maScriptTypes.set<unsigned short>(nRow, SC_SCRIPTTYPE_UNKNOWN);
CellStorageModified();
......@@ -173,6 +177,7 @@ void ScColumn::Delete( SCROW nRow )
}
pCell->EndListeningTo( pDocument );
pCell->Delete();
bDirtyGroups = true;
CellStorageModified();
}
......@@ -192,6 +197,7 @@ void ScColumn::DeleteAtIndex( SCSIZE nIndex )
pCell->EndListeningTo( pDocument );
pCell->Delete();
bDirtyGroups = true;
maTextWidths.set_empty(nRow, nRow);
maScriptTypes.set_empty(nRow, nRow);
CellStorageModified();
......@@ -228,6 +234,8 @@ void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize )
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( false ); // Avoid calculating it multiple times
bDirtyGroups = true;
sal_Bool bFound=false;
SCROW nEndRow = nStartRow + nSize - 1;
SCSIZE nStartIndex = 0;
......@@ -551,6 +559,8 @@ void ScColumn::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex, sal_uInt16 nDe
(*aIt)->Delete();
}
}
bDirtyGroups = true;
}
......@@ -598,6 +608,8 @@ void ScColumn::DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag)
// Delete attributes just now
if ((nDelFlag & IDF_ATTRIB) == IDF_ATTRIB) pAttrArray->DeleteArea( nStartRow, nEndRow );
else if ((nDelFlag & IDF_ATTRIB) != 0) pAttrArray->DeleteHardAttr( nStartRow, nEndRow );
bDirtyGroups = true;
}
......@@ -1984,4 +1996,82 @@ xub_StrLen ScColumn::GetMaxNumberStringLen(
return nStringLen;
}
ScFormulaCellGroup::ScFormulaCellGroup()
{
}
ScFormulaCellGroup::~ScFormulaCellGroup()
{
}
// Very[!] slow way to look for and merge contiguous runs
// of similar formulae into a formulagroup
void ScColumn::RebuildFormulaGroups()
{
if ( maItems.empty() )
return;
// clear previous groups
ScFormulaCellGroupRef xNone;
for (size_t i = 0; i < maItems.size(); i++)
{
ColEntry &rCur = maItems[ i ];
if ( rCur.pCell && rCur.pCell->GetCellType() == CELLTYPE_FORMULA )
static_cast<ScFormulaCell *>( rCur.pCell )->SetCellGroup( xNone );
}
// re-build groups
for (size_t i = 1; i < maItems.size(); i++)
{
ColEntry &rCur = maItems[ i ];
ColEntry &rPrev = maItems[ i - 1 ];
if ( ( rPrev.nRow != rCur.nRow - 1 ) || // not contiguous
!rCur.pCell || !rPrev.pCell || // paranoia
rCur.pCell->GetCellType() != CELLTYPE_FORMULA || // not formulae
rPrev.pCell->GetCellType() != CELLTYPE_FORMULA )
continue;
// see if these formulae are similar
ScFormulaCell *pCur = static_cast< ScFormulaCell *>( rCur.pCell );
ScFormulaCell *pPrev = static_cast< ScFormulaCell *>( rPrev.pCell );
fprintf( stderr, "column has contiguous formulae\n" );
ScSimilarFormulaDelta *pDelta = pPrev->BuildDeltaTo( pCur );
if ( !pDelta )
{
// not similar
pCur->SetCellGroup( xNone );
continue;
}
ScFormulaCellGroupRef xGroup = pPrev->GetCellGroup();
if ( !xGroup.get() )
{
// create a new group ...
ScFormulaCellGroup *pGroup = new ScFormulaCellGroup();
pGroup->mpDelta = pDelta;
pGroup->mnStart = i - 1;
pGroup->mnLength = 2;
xGroup.reset( pGroup );
pCur->SetCellGroup( xGroup );
pPrev->SetCellGroup( xGroup );
}
else if ( xGroup->IsCompatible( pDelta ) )
{
// we are a compatible extension - extend the group
pCur->SetCellGroup( xGroup );
xGroup->mnLength++;
pCur->ReleaseDelta( pDelta );
}
else
{
fprintf( stderr, "unusual incompatible extension of formulae\n" );
pCur->ReleaseDelta( pDelta );
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment