diff options
Diffstat (limited to 'sc/source/core/data/dociter.cxx')
-rw-r--r-- | sc/source/core/data/dociter.cxx | 2158 |
1 files changed, 2158 insertions, 0 deletions
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx new file mode 100644 index 000000000000..aa5b011da93b --- /dev/null +++ b/sc/source/core/data/dociter.cxx @@ -0,0 +1,2158 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +// INCLUDE --------------------------------------------------------------- + +#include <svl/zforlist.hxx> + +#include "scitems.hxx" +#include "global.hxx" +#include "dociter.hxx" +#include "document.hxx" +#include "table.hxx" +#include "column.hxx" +#include "cell.hxx" +#include "attarray.hxx" +#include "patattr.hxx" +#include "docoptio.hxx" +#include "cellform.hxx" + +#include <vector> + +using ::rtl::math::approxEqual; +using ::std::vector; +using ::rtl::OUString; +using ::std::set; + +// STATIC DATA ----------------------------------------------------------- + +namespace { + +void lcl_toUpper(OUString& rStr) +{ + rStr = ScGlobal::pCharClass->toUpper(rStr.trim(), 0, static_cast<USHORT>(rStr.getLength())); +} + +} + +ScDocumentIterator::ScDocumentIterator( ScDocument* pDocument, + SCTAB nStartTable, SCTAB nEndTable ) : + pDoc( pDocument ), + nStartTab( nStartTable ), + nEndTab( nEndTable ) +{ + PutInOrder( nStartTab, nEndTab ); + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + pDefPattern = pDoc->GetDefPattern(); + + nCol = 0; + nRow = 0; + nTab = nStartTab; + + nColPos = 0; + nAttrPos = 0; +} + +ScDocumentIterator::~ScDocumentIterator() +{ +} + +BOOL ScDocumentIterator::GetThisCol() +{ + ScTable* pTab; + while ( (pTab = pDoc->pTab[nTab]) == NULL ) + { + if ( nTab == nEndTab ) + { + nCol = MAXCOL; + nRow = MAXROW; + return FALSE; + } + ++nTab; + } + ScColumn* pCol = &pTab->aCol[nCol]; + ScAttrArray* pAtt = pCol->pAttrArray; + + BOOL bFound = FALSE; + do + { + SCROW nColRow; + SCROW nAttrEnd; + + do + { + nAttrEnd = pAtt->pData[nAttrPos].nRow; + if (nAttrEnd < nRow) + ++nAttrPos; + } + while (nAttrEnd < nRow); + + do + { + nColRow = (nColPos < pCol->nCount) ? pCol->pItems[nColPos].nRow : MAXROW+1; + if (nColRow < nRow) + ++nColPos; + } + while (nColRow < nRow); + + if (nColRow == nRow) + { + bFound = TRUE; + pCell = pCol->pItems[nColPos].pCell; + pPattern = pAtt->pData[nAttrPos].pPattern; + } + else if ( pAtt->pData[nAttrPos].pPattern != pDefPattern ) + { + bFound = TRUE; + pCell = NULL; + pPattern = pAtt->pData[nAttrPos].pPattern; + } + else + { + nRow = Min( (SCROW)nColRow, (SCROW)(nAttrEnd+1) ); + } + } + while (!bFound && nRow <= MAXROW); + + return bFound; +} + +BOOL ScDocumentIterator::GetThis() +{ + BOOL bEnd = FALSE; + BOOL bSuccess = FALSE; + + while ( !bSuccess && !bEnd ) + { + if ( nRow > MAXROW ) + bSuccess = FALSE; + else + bSuccess = GetThisCol(); + + if ( !bSuccess ) + { + ++nCol; + if (nCol > MAXCOL) + { + nCol = 0; + ++nTab; + if (nTab > nEndTab) + bEnd = TRUE; + } + nRow = 0; + nColPos = 0; + nAttrPos = 0; + } + } + + return !bEnd; +} + +BOOL ScDocumentIterator::GetFirst() +{ + nCol = 0; + nTab = nStartTab; + + nRow = 0; + nColPos = 0; + nAttrPos = 0; + + return GetThis(); +} + +BOOL ScDocumentIterator::GetNext() +{ + ++nRow; + + return GetThis(); +} + +//------------------------------------------------------------------------ + +ScBaseCell* ScDocumentIterator::GetCell() +{ + return pCell; +} + +const ScPatternAttr* ScDocumentIterator::GetPattern() +{ + return pPattern; +} + +void ScDocumentIterator::GetPos( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ) +{ + rCol = nCol; + rRow = nRow; + rTab = nTab; +} + + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +void lcl_IterGetNumberFormat( ULONG& nFormat, const ScAttrArray*& rpArr, + SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow, + ScDocument* pDoc ) +{ + if ( rpArr != pNewArr || nAttrEndRow < nRow ) + { + SCSIZE nPos; + pNewArr->Search( nRow, nPos ); // nPos 0 gueltig wenn nicht gefunden + const ScPatternAttr* pPattern = pNewArr->pData[nPos].pPattern; + nFormat = pPattern->GetNumberFormat( pDoc->GetFormatTable() ); + rpArr = pNewArr; + nAttrEndRow = pNewArr->pData[nPos].nRow; + } +} + +//UNUSED2008-05 ScValueIterator::ScValueIterator( ScDocument* pDocument, +//UNUSED2008-05 SCCOL nSCol, SCROW nSRow, SCTAB nSTab, +//UNUSED2008-05 SCCOL nECol, SCROW nERow, SCTAB nETab, +//UNUSED2008-05 BOOL bSTotal, BOOL bTextZero ) : +//UNUSED2008-05 pDoc( pDocument ), +//UNUSED2008-05 nNumFmtIndex(0), +//UNUSED2008-05 nStartCol( nSCol), +//UNUSED2008-05 nStartRow( nSRow), +//UNUSED2008-05 nStartTab( nSTab ), +//UNUSED2008-05 nEndCol( nECol ), +//UNUSED2008-05 nEndRow( nERow), +//UNUSED2008-05 nEndTab( nETab ), +//UNUSED2008-05 nNumFmtType( NUMBERFORMAT_UNDEFINED ), +//UNUSED2008-05 bNumValid( FALSE ), +//UNUSED2008-05 bSubTotal(bSTotal), +//UNUSED2008-05 bNextValid( FALSE ), +//UNUSED2008-05 bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), +//UNUSED2008-05 bTextAsZero( bTextZero ) +//UNUSED2008-05 { +//UNUSED2008-05 PutInOrder( nStartCol, nEndCol); +//UNUSED2008-05 PutInOrder( nStartRow, nEndRow); +//UNUSED2008-05 PutInOrder( nStartTab, nEndTab ); +//UNUSED2008-05 +//UNUSED2008-05 if (!ValidCol(nStartCol)) nStartCol = MAXCOL; +//UNUSED2008-05 if (!ValidCol(nEndCol)) nEndCol = MAXCOL; +//UNUSED2008-05 if (!ValidRow(nStartRow)) nStartRow = MAXROW; +//UNUSED2008-05 if (!ValidRow(nEndRow)) nEndRow = MAXROW; +//UNUSED2008-05 if (!ValidTab(nStartTab)) nStartTab = MAXTAB; +//UNUSED2008-05 if (!ValidTab(nEndTab)) nEndTab = MAXTAB; +//UNUSED2008-05 +//UNUSED2008-05 nCol = nStartCol; +//UNUSED2008-05 nRow = nStartRow; +//UNUSED2008-05 nTab = nStartTab; +//UNUSED2008-05 +//UNUSED2008-05 nColRow = 0; // wird bei GetFirst initialisiert +//UNUSED2008-05 +//UNUSED2008-05 nNumFormat = 0; // werden bei GetNumberFormat initialisiert +//UNUSED2008-05 pAttrArray = 0; +//UNUSED2008-05 nAttrEndRow = 0; +//UNUSED2008-05 } + +ScValueIterator::ScValueIterator( ScDocument* pDocument, const ScRange& rRange, + BOOL bSTotal, BOOL bTextZero ) : + pDoc( pDocument ), + nNumFmtIndex(0), + nStartCol( rRange.aStart.Col() ), + nStartRow( rRange.aStart.Row() ), + nStartTab( rRange.aStart.Tab() ), + nEndCol( rRange.aEnd.Col() ), + nEndRow( rRange.aEnd.Row() ), + nEndTab( rRange.aEnd.Tab() ), + nNumFmtType( NUMBERFORMAT_UNDEFINED ), + bNumValid( FALSE ), + bSubTotal(bSTotal), + bNextValid( FALSE ), + bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), + bTextAsZero( bTextZero ) +{ + PutInOrder( nStartCol, nEndCol); + PutInOrder( nStartRow, nEndRow); + PutInOrder( nStartTab, nEndTab ); + + if (!ValidCol(nStartCol)) nStartCol = MAXCOL; + if (!ValidCol(nEndCol)) nEndCol = MAXCOL; + if (!ValidRow(nStartRow)) nStartRow = MAXROW; + if (!ValidRow(nEndRow)) nEndRow = MAXROW; + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + + nColRow = 0; // wird bei GetFirst initialisiert + + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; +} + +BOOL ScValueIterator::GetThis(double& rValue, USHORT& rErr) +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + for (;;) + { + if ( nRow > nEndRow ) + { + nRow = nStartRow; + do + { + nCol++; + if ( nCol > nEndCol ) + { + nCol = nStartCol; + nTab++; + if ( nTab > nEndTab ) + { + // rValue = 0.0; //! do not change caller's value! + rErr = 0; + return FALSE; // Ende und Aus + } + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + } + + while (( nColRow < pCol->nCount ) && ( pCol->pItems[nColRow].nRow < nRow )) + nColRow++; + + if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow ) + { + nRow = pCol->pItems[nColRow].nRow + 1; + if ( !bSubTotal || !pDoc->pTab[nTab]->RowFiltered( nRow-1 ) ) + { + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + ++nColRow; + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + { + bNumValid = FALSE; + rValue = ((ScValueCell*)pCell)->GetValue(); + rErr = 0; + --nRow; + if ( bCalcAsShown ) + { + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); + rValue = pDoc->RoundValueAsShown( rValue, nNumFormat ); + } + // + // wenn in der selben Spalte gleich noch eine Value-Cell folgt, die + // auch noch im Block liegt, den Wert jetzt schon holen + // + if ( nColRow < pCol->nCount && + pCol->pItems[nColRow].nRow <= nEndRow && + pCol->pItems[nColRow].pCell->GetCellType() == CELLTYPE_VALUE && + !bSubTotal ) + { + fNextValue = ((ScValueCell*)pCol->pItems[nColRow].pCell)->GetValue(); + nNextRow = pCol->pItems[nColRow].nRow; + bNextValid = TRUE; + if ( bCalcAsShown ) + { + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nNextRow, pDoc ); + fNextValue = pDoc->RoundValueAsShown( fNextValue, nNumFormat ); + } + } + + return TRUE; // gefunden + } +// break; + case CELLTYPE_FORMULA: + { + if (!bSubTotal || !((ScFormulaCell*)pCell)->IsSubTotal()) + { + rErr = ((ScFormulaCell*)pCell)->GetErrCode(); + if ( rErr || ((ScFormulaCell*)pCell)->IsValue() ) + { + rValue = ((ScFormulaCell*)pCell)->GetValue(); + nRow--; + bNumValid = FALSE; + return TRUE; // gefunden + } + else if ( bTextAsZero ) + { + rValue = 0.0; + nRow--; + bNumValid = FALSE; + return TRUE; + } + } + } + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + { + if ( bTextAsZero ) + { + rErr = 0; + rValue = 0.0; + nNumFmtType = NUMBERFORMAT_NUMBER; + nNumFmtIndex = 0; + bNumValid = TRUE; + --nRow; + return TRUE; + } + } + break; + default: + { + // added to avoid warnings + } + } + } + } + else + nRow = nEndRow + 1; // naechste Spalte + } +} + +void ScValueIterator::GetCurNumFmtInfo( short& nType, ULONG& nIndex ) +{ + if (!bNumValid) + { + const ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + nNumFmtIndex = pCol->GetNumberFormat( nRow ); + if ( (nNumFmtIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + { + const ScBaseCell* pCell; + SCSIZE nIdx = nColRow - 1; + // there might be rearranged something, so be on the safe side + if ( nIdx < pCol->nCount && pCol->pItems[nIdx].nRow == nRow ) + pCell = pCol->pItems[nIdx].pCell; + else + { + if ( pCol->Search( nRow, nIdx ) ) + pCell = pCol->pItems[nIdx].pCell; + else + pCell = NULL; + } + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) + ((const ScFormulaCell*)pCell)->GetFormatInfo( nNumFmtType, nNumFmtIndex ); + else + nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); + } + else + nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); + bNumValid = TRUE; + } + nType = nNumFmtType; + nIndex = nNumFmtIndex; +} + +BOOL ScValueIterator::GetFirst(double& rValue, USHORT& rErr) +{ + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; + + return GetThis(rValue, rErr); +} + +/* ist inline: +BOOL ScValueIterator::GetNext(double& rValue, USHORT& rErr) +{ + ++nRow; + return GetThis(rValue, rErr); +} +*/ + +// ============================================================================ + +ScDBQueryDataIterator::DataAccess::DataAccess(const ScDBQueryDataIterator* pParent) : + mpParent(pParent) +{ +} + +ScDBQueryDataIterator::DataAccess::~DataAccess() +{ +} + +SCROW ScDBQueryDataIterator::GetRowByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow) +{ + ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; + return pCol->pItems[nColRow].nRow; +} + +ScBaseCell* ScDBQueryDataIterator::GetCellByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow) +{ + ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; + return pCol->pItems[nColRow].pCell; +} + +ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol) +{ + ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; + return pCol->pAttrArray; +} + +bool ScDBQueryDataIterator::IsQueryValid(ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScBaseCell* pCell) +{ + return rDoc.pTab[nTab]->ValidQuery(nRow, rParam, NULL, pCell); +} + +SCSIZE ScDBQueryDataIterator::SearchColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCROW nRow, SCCOL nCol) +{ + ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; + SCSIZE nColRow; + pCol->Search(nRow, nColRow); + return nColRow; +} + +// ---------------------------------------------------------------------------- + +ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(const ScDBQueryDataIterator* pParent, ScDBQueryParamInternal* pParam, ScDocument* pDoc) : + DataAccess(pParent), + mpParam(pParam), + mpDoc(pDoc) +{ + nCol = mpParam->mnField; + nRow = mpParam->nRow1; + nTab = mpParam->nTab; + + nColRow = 0; // wird bei GetFirst initialisiert + SCSIZE i; + SCSIZE nCount = mpParam->GetEntryCount(); + for (i=0; (i<nCount) && (mpParam->GetEntry(i).bDoQuery); i++) + { + ScQueryEntry& rEntry = mpParam->GetEntry(i); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(mpDoc->GetFormatTable()->IsNumberFormat(*rEntry.pStr, nIndex, rEntry.nVal)); + } + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; +} + +ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal() +{ +} + +bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue) +{ + SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField; + for ( ;; ) + { + if (nRow > mpParam->nRow2) + { + // Bottom of the range reached. Bail out. + rValue.mnError = 0; + return false; + } + + SCSIZE nCellCount = mpDoc->GetCellCount(nTab, nCol); + SCROW nThisRow = ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nCol, nColRow); + while ( (nColRow < nCellCount) && (nThisRow < nRow) ) + nThisRow = ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nCol, ++nColRow); + + if ( nColRow < nCellCount && nThisRow <= mpParam->nRow2 ) + { + nRow = nThisRow; + ScBaseCell* pCell = NULL; + if (nCol == static_cast<SCCOL>(nFirstQueryField)) + pCell = ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nCol, nColRow); + + if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, *mpParam, nTab, nRow, pCell)) + { + // #i109812# get cell here if it wasn't done above + if (nCol != static_cast<SCCOL>(nFirstQueryField)) + pCell = ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nCol, nColRow); + + switch (pCell ? pCell->GetCellType() : CELLTYPE_NONE) + { + case CELLTYPE_VALUE: + { + rValue.mfValue = ((ScValueCell*)pCell)->GetValue(); + rValue.mbIsNumber = true; + if ( bCalcAsShown ) + { + const ScAttrArray* pNewAttrArray = + ScDBQueryDataIterator::GetAttrArrayByCol(*mpDoc, nTab, nCol); + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pNewAttrArray, nRow, mpDoc ); + rValue.mfValue = mpDoc->RoundValueAsShown( rValue.mfValue, nNumFormat ); + } + nNumFmtType = NUMBERFORMAT_NUMBER; + nNumFmtIndex = 0; + rValue.mnError = 0; + return TRUE; // gefunden + } +// break; + case CELLTYPE_FORMULA: + { + if (((ScFormulaCell*)pCell)->IsValue()) + { + rValue.mfValue = ((ScFormulaCell*)pCell)->GetValue(); + rValue.mbIsNumber = true; + mpDoc->GetNumberFormatInfo( nNumFmtType, + nNumFmtIndex, ScAddress( nCol, nRow, nTab ), + pCell ); + rValue.mnError = ((ScFormulaCell*)pCell)->GetErrCode(); + return TRUE; // gefunden + } + else + nRow++; + } + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + if (mpParam->mbSkipString) + ++nRow; + else + { + rValue.maString = pCell->GetStringData(); + rValue.mfValue = 0.0; + rValue.mnError = 0; + rValue.mbIsNumber = false; + return true; + } + break; + default: + nRow++; + break; + } + } + else + nRow++; + } + else + nRow = mpParam->nRow2 + 1; // Naechste Spalte + } +// statement unreachable +// return false; +} + +bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue) +{ + if (mpParam->bHasHeader) + nRow++; + + nColRow = ScDBQueryDataIterator::SearchColEntryIndex(*mpDoc, nTab, nRow, nCol); + return getCurrent(rValue); +} + +bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue) +{ + ++nRow; + return getCurrent(rValue); +} + +// ---------------------------------------------------------------------------- + +ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(const ScDBQueryDataIterator* pParent, ScDBQueryParamMatrix* pParam) : + DataAccess(pParent), + mpParam(pParam) +{ + SCSIZE nC, nR; + mpParam->mpMatrix->GetDimensions(nC, nR); + mnRows = static_cast<SCROW>(nR); + mnCols = static_cast<SCCOL>(nC); +} + +ScDBQueryDataIterator::DataAccessMatrix::~DataAccessMatrix() +{ +} + +bool ScDBQueryDataIterator::DataAccessMatrix::getCurrent(Value& rValue) +{ + // Starting from row == mnCurRow, get the first row that satisfies all the + // query parameters. + for ( ;mnCurRow < mnRows; ++mnCurRow) + { + const ScMatrix& rMat = *mpParam->mpMatrix; + if (rMat.IsEmpty(mpParam->mnField, mnCurRow)) + // Don't take empty values into account. + continue; + + bool bIsStrVal = rMat.IsString(mpParam->mnField, mnCurRow); + if (bIsStrVal && mpParam->mbSkipString) + continue; + + if (isValidQuery(mnCurRow, rMat)) + { + rValue.maString = rMat.GetString(mpParam->mnField, mnCurRow); + rValue.mfValue = rMat.GetDouble(mpParam->mnField, mnCurRow); + rValue.mbIsNumber = !bIsStrVal; + rValue.mnError = 0; + return true; + } + } + return false; +} + +bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value& rValue) +{ + mnCurRow = mpParam->bHasHeader ? 1 : 0; + return getCurrent(rValue); +} + +bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value& rValue) +{ + ++mnCurRow; + return getCurrent(rValue); +} + +namespace { + +bool lcl_isQueryByValue(const ScQueryEntry& rEntry, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow) +{ + if (rEntry.bQueryByString) + return false; + + if (!rMat.IsValueOrEmpty(nCol, nRow)) + return false; + + return true; +} + +bool lcl_isQueryByString(const ScQueryEntry& rEntry, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow) +{ + switch (rEntry.eOp) + { + case SC_EQUAL: + case SC_NOT_EQUAL: + case SC_CONTAINS: + case SC_DOES_NOT_CONTAIN: + case SC_BEGINS_WITH: + case SC_ENDS_WITH: + case SC_DOES_NOT_BEGIN_WITH: + case SC_DOES_NOT_END_WITH: + return true; + default: + ; + } + + if (rEntry.bQueryByString && rMat.IsString(nCol, nRow)) + return true; + + return false; +} + +} + +bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow, const ScMatrix& rMat) const +{ + SCSIZE nEntryCount = mpParam->GetEntryCount(); + vector<bool> aResults; + aResults.reserve(nEntryCount); + + const CollatorWrapper& rCollator = + mpParam->bCaseSens ? *ScGlobal::GetCaseCollator() : *ScGlobal::GetCollator(); + + for (SCSIZE i = 0; i < nEntryCount; ++i) + { + const ScQueryEntry& rEntry = mpParam->GetEntry(i); + if (!rEntry.bDoQuery) + continue; + + switch (rEntry.eOp) + { + case SC_EQUAL: + case SC_LESS: + case SC_GREATER: + case SC_LESS_EQUAL: + case SC_GREATER_EQUAL: + case SC_NOT_EQUAL: + break; + default: + // Only the above operators are supported. + continue; + } + + bool bValid = false; + + SCSIZE nField = static_cast<SCSIZE>(rEntry.nField); + if (lcl_isQueryByValue(rEntry, rMat, nField, nRow)) + { + // By value + double fMatVal = rMat.GetDouble(nField, nRow); + bool bEqual = approxEqual(fMatVal, rEntry.nVal); + switch (rEntry.eOp) + { + case SC_EQUAL: + bValid = bEqual; + break; + case SC_LESS: + bValid = (fMatVal < rEntry.nVal) && !bEqual; + break; + case SC_GREATER: + bValid = (fMatVal > rEntry.nVal) && !bEqual; + break; + case SC_LESS_EQUAL: + bValid = (fMatVal < rEntry.nVal) || bEqual; + break; + case SC_GREATER_EQUAL: + bValid = (fMatVal > rEntry.nVal) || bEqual; + break; + case SC_NOT_EQUAL: + bValid = !bEqual; + break; + default: + ; + } + } + else if (lcl_isQueryByString(rEntry, rMat, nField, nRow)) + { + // By string + do + { + if (!rEntry.pStr) + break; + + // Equality check first. + + OUString aMatStr = rMat.GetString(nField, nRow); + lcl_toUpper(aMatStr); + OUString aQueryStr = *rEntry.pStr; + lcl_toUpper(aQueryStr); + bool bDone = false; + switch (rEntry.eOp) + { + case SC_EQUAL: + bValid = aMatStr.equals(aQueryStr); + bDone = true; + break; + case SC_NOT_EQUAL: + bValid = !aMatStr.equals(aQueryStr); + bDone = true; + break; + default: + ; + } + + if (bDone) + break; + + // Unequality check using collator. + + sal_Int32 nCompare = rCollator.compareString(aMatStr, aQueryStr); + switch (rEntry.eOp) + { + case SC_LESS : + bValid = (nCompare < 0); + break; + case SC_GREATER : + bValid = (nCompare > 0); + break; + case SC_LESS_EQUAL : + bValid = (nCompare <= 0); + break; + case SC_GREATER_EQUAL : + bValid = (nCompare >= 0); + break; + default: + ; + } + } + while (false); + } + else if (mpParam->bMixedComparison) + { + // Not used at the moment. + } + + if (aResults.empty()) + // First query entry. + aResults.push_back(bValid); + else if (rEntry.eConnect == SC_AND) + { + // For AND op, tuck the result into the last result value. + size_t n = aResults.size(); + aResults[n-1] = aResults[n-1] && bValid; + } + else + // For OR op, store its own result. + aResults.push_back(bValid); + } + + // Row is valid as long as there is at least one result being true. + vector<bool>::const_iterator itr = aResults.begin(), itrEnd = aResults.end(); + for (; itr != itrEnd; ++itr) + if (*itr) + return true; + + return false; +} + +// ---------------------------------------------------------------------------- + +ScDBQueryDataIterator::Value::Value() : + mnError(0), mbIsNumber(true) +{ + ::rtl::math::setNan(&mfValue); +} + +// ---------------------------------------------------------------------------- + +ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryParamBase* pParam) : + mpParam (pParam) +{ + switch (mpParam->GetType()) + { + case ScDBQueryParamBase::INTERNAL: + { + ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pParam); + mpData.reset(new DataAccessInternal(this, p, pDocument)); + } + break; + case ScDBQueryParamBase::MATRIX: + { + ScDBQueryParamMatrix* p = static_cast<ScDBQueryParamMatrix*>(pParam); + mpData.reset(new DataAccessMatrix(this, p)); + } + } +} + +bool ScDBQueryDataIterator::GetFirst(Value& rValue) +{ + return mpData->getFirst(rValue); +} + +bool ScDBQueryDataIterator::GetNext(Value& rValue) +{ + return mpData->getNext(rValue); +} + +// ============================================================================ + +ScCellIterator::ScCellIterator( ScDocument* pDocument, + SCCOL nSCol, SCROW nSRow, SCTAB nSTab, + SCCOL nECol, SCROW nERow, SCTAB nETab, BOOL bSTotal ) : + pDoc( pDocument ), + nStartCol( nSCol), + nStartRow( nSRow), + nStartTab( nSTab ), + nEndCol( nECol ), + nEndRow( nERow), + nEndTab( nETab ), + bSubTotal(bSTotal) + +{ + PutInOrder( nStartCol, nEndCol); + PutInOrder( nStartRow, nEndRow); + PutInOrder( nStartTab, nEndTab ); + + if (!ValidCol(nStartCol)) nStartCol = MAXCOL; + if (!ValidCol(nEndCol)) nEndCol = MAXCOL; + if (!ValidRow(nStartRow)) nStartRow = MAXROW; + if (!ValidRow(nEndRow)) nEndRow = MAXROW; + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + while (nEndTab>0 && !pDoc->pTab[nEndTab]) + --nEndTab; // nur benutzte Tabellen + if (nStartTab>nEndTab) + nStartTab = nEndTab; + + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + nColRow = 0; // wird bei GetFirst initialisiert + + if (!pDoc->pTab[nTab]) + { + DBG_ERROR("Tabelle nicht gefunden"); + nStartCol = nCol = MAXCOL+1; + nStartRow = nRow = MAXROW+1; + nStartTab = nTab = MAXTAB+1; // -> Abbruch bei GetFirst + } +} + +ScCellIterator::ScCellIterator + ( ScDocument* pDocument, const ScRange& rRange, BOOL bSTotal ) : + pDoc( pDocument ), + nStartCol( rRange.aStart.Col() ), + nStartRow( rRange.aStart.Row() ), + nStartTab( rRange.aStart.Tab() ), + nEndCol( rRange.aEnd.Col() ), + nEndRow( rRange.aEnd.Row() ), + nEndTab( rRange.aEnd.Tab() ), + bSubTotal(bSTotal) + +{ + PutInOrder( nStartCol, nEndCol); + PutInOrder( nStartRow, nEndRow); + PutInOrder( nStartTab, nEndTab ); + + if (!ValidCol(nStartCol)) nStartCol = MAXCOL; + if (!ValidCol(nEndCol)) nEndCol = MAXCOL; + if (!ValidRow(nStartRow)) nStartRow = MAXROW; + if (!ValidRow(nEndRow)) nEndRow = MAXROW; + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + while (nEndTab>0 && !pDoc->pTab[nEndTab]) + --nEndTab; // nur benutzte Tabellen + if (nStartTab>nEndTab) + nStartTab = nEndTab; + + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + nColRow = 0; // wird bei GetFirst initialisiert + + if (!pDoc->pTab[nTab]) + { + DBG_ERROR("Tabelle nicht gefunden"); + nStartCol = nCol = MAXCOL+1; + nStartRow = nRow = MAXROW+1; + nStartTab = nTab = MAXTAB+1; // -> Abbruch bei GetFirst + } +} + +ScBaseCell* ScCellIterator::GetThis() +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + for ( ;; ) + { + if ( nRow > nEndRow ) + { + nRow = nStartRow; + do + { + nCol++; + if ( nCol > nEndCol ) + { + nCol = nStartCol; + nTab++; + if ( nTab > nEndTab ) + return NULL; // Ende und Aus + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + } + + while ( (nColRow < pCol->nCount) && (pCol->pItems[nColRow].nRow < nRow) ) + nColRow++; + + if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow ) + { + nRow = pCol->pItems[nColRow].nRow; + if ( !bSubTotal || !pDoc->pTab[nTab]->RowFiltered( nRow ) ) + { + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + + if ( bSubTotal && pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->IsSubTotal() ) + nRow++; // Sub-Total-Zeilen nicht + else + return pCell; // gefunden + } + else + nRow++; + } + else + nRow = nEndRow + 1; // Naechste Spalte + } +} + +ScBaseCell* ScCellIterator::GetFirst() +{ + if ( !ValidTab(nTab) ) + return NULL; + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + return GetThis(); +} + +ScBaseCell* ScCellIterator::GetNext() +{ + ++nRow; + return GetThis(); +} + +//------------------------------------------------------------------------------- + +ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable, + const ScQueryParam& rParam, BOOL bMod ) : + aParam (rParam), + pDoc( pDocument ), + nTab( nTable), + nStopOnMismatch( nStopOnMismatchDisabled ), + nTestEqualCondition( nTestEqualConditionDisabled ), + bAdvanceQuery( FALSE ), + bIgnoreMismatchOnLeadingStrings( FALSE ) +{ + nCol = aParam.nCol1; + nRow = aParam.nRow1; + nColRow = 0; // wird bei GetFirst initialisiert + SCSIZE i; + if (bMod) // sonst schon eingetragen + { + for (i=0; (i<MAXQUERY) && (aParam.GetEntry(i).bDoQuery); i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pDoc->GetFormatTable()->IsNumberFormat(*rEntry.pStr, + nIndex, rEntry.nVal)); + } + } + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; +} + +ScBaseCell* ScQueryCellIterator::GetThis() +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + const ScQueryEntry& rEntry = aParam.GetEntry(0); + SCCOLROW nFirstQueryField = rEntry.nField; + bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && + !rEntry.bQueryByString; + bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && + !aParam.bHasHeader && rEntry.bQueryByString && + ((aParam.bByRow && nRow == aParam.nRow1) || + (!aParam.bByRow && nCol == aParam.nCol1)); + for ( ;; ) + { + if ( nRow > aParam.nRow2 ) + { + nRow = aParam.nRow1; + if (aParam.bHasHeader && aParam.bByRow) + nRow++; + do + { + if ( ++nCol > aParam.nCol2 ) + return NULL; // Ende und Aus + if ( bAdvanceQuery ) + { + AdvanceQueryParamEntryField(); + nFirstQueryField = rEntry.nField; + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && + !aParam.bHasHeader && rEntry.bQueryByString && + aParam.bByRow; + } + + while ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow < nRow ) + nColRow++; + + if ( nColRow < pCol->nCount && + (nRow = pCol->pItems[nColRow].nRow) <= aParam.nRow2 ) + { + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + if ( pCell->GetCellType() == CELLTYPE_NOTE ) + ++nRow; + else if (bAllStringIgnore && pCell->HasStringData()) + ++nRow; + else + { + BOOL bTestEqualCondition; + if ( (pDoc->pTab[nTab])->ValidQuery( nRow, aParam, NULL, + (nCol == static_cast<SCCOL>(nFirstQueryField) ? pCell : NULL), + (nTestEqualCondition ? &bTestEqualCondition : NULL) ) ) + { + if ( nTestEqualCondition && bTestEqualCondition ) + nTestEqualCondition |= nTestEqualConditionMatched; + return pCell; // found + } + else if ( nStopOnMismatch ) + { + // Yes, even a mismatch may have a fulfilled equal + // condition if regular expressions were involved and + // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried. + if ( nTestEqualCondition && bTestEqualCondition ) + { + nTestEqualCondition |= nTestEqualConditionMatched; + nStopOnMismatch |= nStopOnMismatchOccured; + return NULL; + } + bool bStop; + if (bFirstStringIgnore) + { + if (pCell->HasStringData()) + { + ++nRow; + bStop = false; + } + else + bStop = true; + } + else + bStop = true; + if (bStop) + { + nStopOnMismatch |= nStopOnMismatchOccured; + return NULL; + } + } + else + nRow++; + } + } + else + nRow = aParam.nRow2 + 1; // Naechste Spalte + bFirstStringIgnore = false; + } +} + +ScBaseCell* ScQueryCellIterator::GetFirst() +{ + nCol = aParam.nCol1; + nRow = aParam.nRow1; + if (aParam.bHasHeader) + nRow++; +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + return GetThis(); +} + +ScBaseCell* ScQueryCellIterator::GetNext() +{ + ++nRow; + if ( nStopOnMismatch ) + nStopOnMismatch = nStopOnMismatchEnabled; + if ( nTestEqualCondition ) + nTestEqualCondition = nTestEqualConditionEnabled; + return GetThis(); +} + +void ScQueryCellIterator::AdvanceQueryParamEntryField() +{ + SCSIZE nEntries = aParam.GetEntryCount(); + for ( SCSIZE j = 0; j < nEntries; j++ ) + { + ScQueryEntry& rEntry = aParam.GetEntry( j ); + if ( rEntry.bDoQuery ) + { + if ( rEntry.nField < MAXCOL ) + rEntry.nField++; + else + { + DBG_ERRORFILE( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" ); + } + } + else + break; // for + } +} + + +BOOL ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol, + SCROW& nFoundRow, BOOL bSearchForEqualAfterMismatch, + BOOL bIgnoreMismatchOnLeadingStringsP ) +{ + nFoundCol = MAXCOL+1; + nFoundRow = MAXROW+1; + SetStopOnMismatch( TRUE ); // assume sorted keys + SetTestEqualCondition( TRUE ); + bIgnoreMismatchOnLeadingStrings = bIgnoreMismatchOnLeadingStringsP; + bool bRegExp = aParam.bRegExp && aParam.GetEntry(0).bQueryByString; + bool bBinary = !bRegExp && aParam.bByRow && (aParam.GetEntry(0).eOp == + SC_LESS_EQUAL || aParam.GetEntry(0).eOp == SC_GREATER_EQUAL); + if (bBinary ? (BinarySearch() ? GetThis() : 0) : GetFirst()) + { + // First equal entry or last smaller than (greater than) entry. + SCSIZE nColRowSave; + ScBaseCell* pNext = 0; + do + { + nFoundCol = GetCol(); + nFoundRow = GetRow(); + nColRowSave = nColRow; + } while ( !IsEqualConditionFulfilled() && (pNext = GetNext()) != NULL ); + // There may be no pNext but equal condition fulfilled if regular + // expressions are involved. Keep the found entry and proceed. + if (!pNext && !IsEqualConditionFulfilled()) + { + // Step back to last in range and adjust position markers for + // GetNumberFormat() or similar. + nCol = nFoundCol; + nRow = nFoundRow; + nColRow = nColRowSave; + } + } + if ( IsEqualConditionFulfilled() ) + { + // Position on last equal entry. + SCSIZE nEntries = aParam.GetEntryCount(); + for ( SCSIZE j = 0; j < nEntries; j++ ) + { + ScQueryEntry& rEntry = aParam.GetEntry( j ); + if ( rEntry.bDoQuery ) + { + switch ( rEntry.eOp ) + { + case SC_LESS_EQUAL : + case SC_GREATER_EQUAL : + rEntry.eOp = SC_EQUAL; + break; + default: + { + // added to avoid warnings + } + } + } + else + break; // for + } + SCSIZE nColRowSave; + bIgnoreMismatchOnLeadingStrings = FALSE; + SetTestEqualCondition( FALSE ); + do + { + nFoundCol = GetCol(); + nFoundRow = GetRow(); + nColRowSave = nColRow; + } while (GetNext()); + // Step back conditions same as above + nCol = nFoundCol; + nRow = nFoundRow; + nColRow = nColRowSave; + return TRUE; + } + if ( (bSearchForEqualAfterMismatch || aParam.bRegExp) && + StoppedOnMismatch() ) + { + // Assume found entry to be the last value less than respectively + // greater than the query. But keep on searching for an equal match. + SCSIZE nEntries = aParam.GetEntryCount(); + for ( SCSIZE j = 0; j < nEntries; j++ ) + { + ScQueryEntry& rEntry = aParam.GetEntry( j ); + if ( rEntry.bDoQuery ) + { + switch ( rEntry.eOp ) + { + case SC_LESS_EQUAL : + case SC_GREATER_EQUAL : + rEntry.eOp = SC_EQUAL; + break; + default: + { + // added to avoid warnings + } + } + } + else + break; // for + } + SetStopOnMismatch( FALSE ); + SetTestEqualCondition( FALSE ); + if (GetNext()) + { + // Last of a consecutive area, avoid searching the entire parameter + // range as it is a real performance bottleneck in case of regular + // expressions. + SCSIZE nColRowSave; + do + { + nFoundCol = GetCol(); + nFoundRow = GetRow(); + nColRowSave = nColRow; + SetStopOnMismatch( TRUE ); + } while (GetNext()); + nCol = nFoundCol; + nRow = nFoundRow; + nColRow = nColRowSave; + } + } + return (nFoundCol <= MAXCOL) && (nFoundRow <= MAXROW); +} + + +ScBaseCell* ScQueryCellIterator::BinarySearch() +{ + nCol = aParam.nCol1; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + if (!pCol->nCount) + return 0; + + ScBaseCell* pCell; + SCSIZE nHi, nLo; + CollatorWrapper* pCollator = (aParam.bCaseSens ? ScGlobal::GetCaseCollator() : + ScGlobal::GetCollator()); + SvNumberFormatter& rFormatter = *(pDoc->GetFormatTable()); + const ScQueryEntry& rEntry = aParam.GetEntry(0); + bool bLessEqual = rEntry.eOp == SC_LESS_EQUAL; + bool bByString = rEntry.bQueryByString; + bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !bByString; + bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && + !aParam.bHasHeader && bByString; + + nRow = aParam.nRow1; + if (aParam.bHasHeader) + nRow++; + const ColEntry* pItems = pCol->pItems; + if (pCol->Search( nRow, nLo ) && bFirstStringIgnore && + pItems[nLo].pCell->HasStringData()) + { + String aCellStr; + ULONG nFormat = pCol->GetNumberFormat( pItems[nLo].nRow); + ScCellFormat::GetInputString( pItems[nLo].pCell, nFormat, aCellStr, + rFormatter); + sal_Int32 nTmp = pCollator->compareString( aCellStr, *rEntry.pStr); + if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) || + (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) || + (rEntry.eOp == SC_EQUAL && nTmp != 0)) + ++nLo; + } + if (!pCol->Search( aParam.nRow2, nHi ) && nHi>0) + --nHi; + while (bAllStringIgnore && nLo <= nHi && nLo < pCol->nCount && + pItems[nLo].pCell->HasStringData()) + ++nLo; + + // Bookkeeping values for breaking up the binary search in case the data + // range isn't strictly sorted. + SCSIZE nLastInRange = nLo; + SCSIZE nFirstLastInRange = nLastInRange; + double fLastInRangeValue = bLessEqual ? + -(::std::numeric_limits<double>::max()) : + ::std::numeric_limits<double>::max(); + String aLastInRangeString; + if (!bLessEqual) + aLastInRangeString.Assign( sal_Unicode(0xFFFF)); + if (nLastInRange < pCol->nCount) + { + pCell = pItems[nLastInRange].pCell; + if (pCell->HasStringData()) + { + ULONG nFormat = pCol->GetNumberFormat( pItems[nLastInRange].nRow); + ScCellFormat::GetInputString( pCell, nFormat, aLastInRangeString, + rFormatter); + } + else + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + fLastInRangeValue = + static_cast<ScValueCell*>(pCell)->GetValue(); + break; + case CELLTYPE_FORMULA : + fLastInRangeValue = + static_cast<ScFormulaCell*>(pCell)->GetValue(); + break; + default: + { + // added to avoid warnings + } + } + } + } + + sal_Int32 nRes = 0; + bool bFound = false; + bool bDone = false; + while (nLo <= nHi && !bDone) + { + SCSIZE nMid = (nLo+nHi)/2; + SCSIZE i = nMid; + while (i <= nHi && pItems[i].pCell->GetCellType() == CELLTYPE_NOTE) + ++i; + if (i > nHi) + { + if (nMid > 0) + nHi = nMid - 1; + else + bDone = true; + continue; // while + } + BOOL bStr = pItems[i].pCell->HasStringData(); + nRes = 0; + // compares are content<query:-1, content>query:1 + // Cell value comparison similar to ScTable::ValidQuery() + if (!bStr && !bByString) + { + double nCellVal; + pCell = pItems[i].pCell; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + nCellVal = static_cast<ScValueCell*>(pCell)->GetValue(); + break; + case CELLTYPE_FORMULA : + nCellVal = static_cast<ScFormulaCell*>(pCell)->GetValue(); + break; + default: + nCellVal = 0.0; + } + if ((nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( + nCellVal, rEntry.nVal)) + { + nRes = -1; + if (bLessEqual) + { + if (fLastInRangeValue < nCellVal) + { + fLastInRangeValue = nCellVal; + nLastInRange = i; + } + else if (fLastInRangeValue > nCellVal) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + } + else if ((nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( + nCellVal, rEntry.nVal)) + { + nRes = 1; + if (!bLessEqual) + { + if (fLastInRangeValue > nCellVal) + { + fLastInRangeValue = nCellVal; + nLastInRange = i; + } + else if (fLastInRangeValue < nCellVal) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + } + } + else if (bStr && bByString) + { + String aCellStr; + ULONG nFormat = pCol->GetNumberFormat( pItems[i].nRow); + ScCellFormat::GetInputString( pItems[i].pCell, nFormat, aCellStr, + rFormatter); + nRes = pCollator->compareString( aCellStr, *rEntry.pStr); + if (nRes < 0 && bLessEqual) + { + sal_Int32 nTmp = pCollator->compareString( aLastInRangeString, + aCellStr); + if (nTmp < 0) + { + aLastInRangeString = aCellStr; + nLastInRange = i; + } + else if (nTmp > 0) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + else if (nRes > 0 && !bLessEqual) + { + sal_Int32 nTmp = pCollator->compareString( aLastInRangeString, + aCellStr); + if (nTmp > 0) + { + aLastInRangeString = aCellStr; + nLastInRange = i; + } + else if (nTmp < 0) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + } + else if (!bStr && bByString) + { + nRes = -1; // numeric < string + if (bLessEqual) + nLastInRange = i; + } + else // if (bStr && !bByString) + { + nRes = 1; // string > numeric + if (!bLessEqual) + nLastInRange = i; + } + if (nRes < 0) + { + if (bLessEqual) + nLo = nMid + 1; + else // assumed to be SC_GREATER_EQUAL + { + if (nMid > 0) + nHi = nMid - 1; + else + bDone = true; + } + } + else if (nRes > 0) + { + if (bLessEqual) + { + if (nMid > 0) + nHi = nMid - 1; + else + bDone = true; + } + else // assumed to be SC_GREATER_EQUAL + nLo = nMid + 1; + } + else + { + nLo = i; + bDone = bFound = true; + } + } + if (!bFound) + { + // If all hits didn't result in a moving limit there's something + // strange, e.g. data range not properly sorted, or only identical + // values encountered, which doesn't mean there aren't any others in + // between.. leave it to GetThis(). The condition for this would be + // if (nLastInRange == nFirstLastInRange) nLo = nFirstLastInRange; + // Else, in case no exact match was found, we step back for a + // subsequent GetThis() to find the last in range. Effectively this is + // --nLo with nLastInRange == nLo-1. Both conditions combined yield: + nLo = nLastInRange; + } + if (nLo < pCol->nCount && pCol->pItems[nLo].nRow <= aParam.nRow2) + { + nRow = pItems[nLo].nRow; + pCell = pItems[nLo].pCell; + nColRow = nLo; + } + else + { + nRow = aParam.nRow2 + 1; + pCell = 0; + nColRow = pCol->nCount - 1; + } + return pCell; +} + + +//------------------------------------------------------------------------------- + +ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + pDoc( pDocument ), + nTab( nTable ), + nStartCol( nCol1 ), + nEndCol( nCol2 ), + nEndRow( nRow2 ), + nCol( nCol1 ), + nRow( nRow1 ), + bMore( TRUE ) +{ + SCCOL i; + SCSIZE nIndex; + + pNextRows = new SCROW[ nCol2-nCol1+1 ]; + pNextIndices = new SCSIZE[ nCol2-nCol1+1 ]; + + for (i=nStartCol; i<=nEndCol; i++) + { + ScColumn* pCol = &pDoc->pTab[nTab]->aCol[i]; + + pCol->Search( nRow1, nIndex ); + if ( nIndex < pCol->nCount ) + { + pNextRows[i-nStartCol] = pCol->pItems[nIndex].nRow; + pNextIndices[i-nStartCol] = nIndex; + } + else + { + pNextRows[i-nStartCol] = MAXROWCOUNT; // nichts gefunden + pNextIndices[i-nStartCol] = MAXROWCOUNT; + } + } + + if (pNextRows[0] != nRow1) + Advance(); +} + +ScHorizontalCellIterator::~ScHorizontalCellIterator() +{ + delete [] pNextRows; + delete [] pNextIndices; +} + +ScBaseCell* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow ) +{ + if ( bMore ) + { + rCol = nCol; + rRow = nRow; + + ScColumn* pCol = &pDoc->pTab[nTab]->aCol[nCol]; + SCSIZE nIndex = pNextIndices[nCol-nStartCol]; + DBG_ASSERT( nIndex < pCol->nCount, "ScHorizontalCellIterator::GetNext: nIndex out of range" ); + ScBaseCell* pCell = pCol->pItems[nIndex].pCell; + if ( ++nIndex < pCol->nCount ) + { + pNextRows[nCol-nStartCol] = pCol->pItems[nIndex].nRow; + pNextIndices[nCol-nStartCol] = nIndex; + } + else + { + pNextRows[nCol-nStartCol] = MAXROWCOUNT; // nichts gefunden + pNextIndices[nCol-nStartCol] = MAXROWCOUNT; + } + + Advance(); + return pCell; + } + else + return NULL; +} + +BOOL ScHorizontalCellIterator::ReturnNext( SCCOL& rCol, SCROW& rRow ) +{ + rCol = nCol; + rRow = nRow; + return bMore; +} + +void ScHorizontalCellIterator::Advance() +{ + BOOL bFound = FALSE; + SCCOL i; + + for (i=nCol+1; i<=nEndCol && !bFound; i++) + if (pNextRows[i-nStartCol] == nRow) + { + nCol = i; + bFound = TRUE; + } + + if (!bFound) + { + SCROW nMinRow = MAXROW+1; + for (i=nStartCol; i<=nEndCol; i++) + if (pNextRows[i-nStartCol] < nMinRow) + { + nCol = i; + nMinRow = pNextRows[i-nStartCol]; + } + + if (nMinRow <= nEndRow) + { + nRow = nMinRow; + bFound = TRUE; + } + } + + if ( !bFound ) + bMore = FALSE; +} + +//------------------------------------------------------------------------------- + +ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + pDoc( pDocument ), + nTab( nTable ), + nStartCol( nCol1 ), + nStartRow( nRow1 ), + nEndCol( nCol2 ), + nEndRow( nRow2 ) +{ + DBG_ASSERT( pDoc->pTab[nTab], "Tabelle nicht da" ); + + SCCOL i; + + nRow = nStartRow; + nCol = nStartCol; + bRowEmpty = FALSE; + + pIndices = new SCSIZE[nEndCol-nStartCol+1]; + pNextEnd = new SCROW[nEndCol-nStartCol+1]; + ppPatterns = new const ScPatternAttr*[nEndCol-nStartCol+1]; + + SCROW nSkipTo = MAXROW; + BOOL bEmpty = TRUE; + for (i=nStartCol; i<=nEndCol; i++) + { + SCCOL nPos = i - nStartCol; + ScAttrArray* pArray = pDoc->pTab[nTab]->aCol[i].pAttrArray; + DBG_ASSERT( pArray, "pArray == 0" ); + + SCSIZE nIndex; + pArray->Search( nStartRow, nIndex ); + + const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern; + SCROW nThisEnd = pArray->pData[nIndex].nRow; + if ( IsDefaultItem( pPattern ) ) + { + pPattern = NULL; + if ( nThisEnd < nSkipTo ) + nSkipTo = nThisEnd; // nSkipTo kann gleich hier gesetzt werden + } + else + bEmpty = FALSE; // Attribute gefunden + + pIndices[nPos] = nIndex; + pNextEnd[nPos] = nThisEnd; + ppPatterns[nPos] = pPattern; + } + + if (bEmpty) + nRow = nSkipTo; // bis zum naechsten Bereichsende ueberspringen + bRowEmpty = bEmpty; +} + +ScHorizontalAttrIterator::~ScHorizontalAttrIterator() +{ + delete[] (ScPatternAttr**)ppPatterns; + delete[] pNextEnd; + delete[] pIndices; +} + +const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow ) +{ + for (;;) + { + if (!bRowEmpty) + { + // in dieser Zeile suchen + + while ( nCol <= nEndCol && !ppPatterns[nCol-nStartCol] ) + ++nCol; + + if ( nCol <= nEndCol ) + { + const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol]; + rRow = nRow; + rCol1 = nCol; + while ( nCol < nEndCol && ppPatterns[nCol+1-nStartCol] == pPat ) + ++nCol; + rCol2 = nCol; + ++nCol; // hochzaehlen fuer naechsten Aufruf + return pPat; // gefunden + } + } + + // naechste Zeile + + ++nRow; + if ( nRow > nEndRow ) // schon am Ende? + return NULL; // nichts gefunden + + BOOL bEmpty = TRUE; + SCCOL i; + + for ( i = nStartCol; i <= nEndCol; i++) + { + SCCOL nPos = i-nStartCol; + if ( pNextEnd[nPos] < nRow ) + { + ScAttrArray* pArray = pDoc->pTab[nTab]->aCol[i].pAttrArray; + + SCSIZE nIndex = ++pIndices[nPos]; + if ( nIndex < pArray->nCount ) + { + const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern; + SCROW nThisEnd = pArray->pData[nIndex].nRow; + if ( IsDefaultItem( pPattern ) ) + pPattern = NULL; + else + bEmpty = FALSE; // Attribute gefunden + + pNextEnd[nPos] = nThisEnd; + ppPatterns[nPos] = pPattern; + + DBG_ASSERT( pNextEnd[nPos] >= nRow, "Reihenfolge durcheinander" ); + } + else + { + DBG_ERROR("AttrArray reicht nicht bis MAXROW"); + pNextEnd[nPos] = MAXROW; + ppPatterns[nPos] = NULL; + } + } + else if ( ppPatterns[nPos] ) + bEmpty = FALSE; // Bereich noch nicht zuende + } + + if (bEmpty) + { + SCCOL nCount = nEndCol-nStartCol+1; + SCROW nSkipTo = pNextEnd[0]; // naechstes Bereichsende suchen + for (i=1; i<nCount; i++) + if ( pNextEnd[i] < nSkipTo ) + nSkipTo = pNextEnd[i]; + nRow = nSkipTo; // leere Zeilen ueberspringen + } + bRowEmpty = bEmpty; + nCol = nStartCol; // wieder links anfangen + } + +// return NULL; +} + +//------------------------------------------------------------------------------- + +inline BOOL IsGreater( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + return ( nRow1 > nRow2 ) || ( nRow1 == nRow2 && nCol1 > nCol2 ); +} + +ScUsedAreaIterator::ScUsedAreaIterator( ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + aCellIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 ), + aAttrIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 ), + nNextCol( nCol1 ), + nNextRow( nRow1 ) +{ + pCell = aCellIter.GetNext( nCellCol, nCellRow ); + pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow ); +} + +ScUsedAreaIterator::~ScUsedAreaIterator() +{ +} + +BOOL ScUsedAreaIterator::GetNext() +{ + // Iteratoren weiterzaehlen + + if ( pCell && IsGreater( nNextCol, nNextRow, nCellCol, nCellRow ) ) + pCell = aCellIter.GetNext( nCellCol, nCellRow ); + + while ( pCell && pCell->IsBlank() ) + pCell = aCellIter.GetNext( nCellCol, nCellRow ); + + if ( pPattern && IsGreater( nNextCol, nNextRow, nAttrCol2, nAttrRow ) ) + pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow ); + + if ( pPattern && nAttrRow == nNextRow && nAttrCol1 < nNextCol ) + nAttrCol1 = nNextCol; + + // naechsten Abschnitt heraussuchen + + BOOL bFound = TRUE; + BOOL bUseCell = FALSE; + + if ( pCell && pPattern ) + { + if ( IsGreater( nCellCol, nCellRow, nAttrCol1, nAttrRow ) ) // vorne nur Attribute ? + { + pFoundCell = NULL; + pFoundPattern = pPattern; + nFoundRow = nAttrRow; + nFoundStartCol = nAttrCol1; + if ( nCellRow == nAttrRow && nCellCol <= nAttrCol2 ) // auch Zelle im Bereich ? + nFoundEndCol = nCellCol - 1; // nur bis vor der Zelle + else + nFoundEndCol = nAttrCol2; // alles + } + else + { + bUseCell = TRUE; + if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attribute auf der Zelle ? + pFoundPattern = pPattern; + else + pFoundPattern = NULL; + } + } + else if ( pCell ) // nur Zelle -> direkt uebernehmen + { + pFoundPattern = NULL; + bUseCell = TRUE; // Position von Zelle + } + else if ( pPattern ) // nur Attribute -> direkt uebernehmen + { + pFoundCell = NULL; + pFoundPattern = pPattern; + nFoundRow = nAttrRow; + nFoundStartCol = nAttrCol1; + nFoundEndCol = nAttrCol2; + } + else // gar nichts + bFound = FALSE; + + if ( bUseCell ) // Position von Zelle + { + pFoundCell = pCell; + nFoundRow = nCellRow; + nFoundStartCol = nFoundEndCol = nCellCol; + } + + if (bFound) + { + nNextRow = nFoundRow; + nNextCol = nFoundEndCol + 1; + } + + return bFound; +} + +//------------------------------------------------------------------------------- + +ScDocAttrIterator::ScDocAttrIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2) : + pDoc( pDocument ), + nTab( nTable ), + nEndCol( nCol2 ), + nStartRow( nRow1 ), + nEndRow( nRow2 ), + nCol( nCol1 ) +{ + if ( ValidTab(nTab) && pDoc->pTab[nTab] ) + pColIter = pDoc->pTab[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow ); + else + pColIter = NULL; +} + +ScDocAttrIterator::~ScDocAttrIterator() +{ + delete pColIter; +} + +const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 ) +{ + while ( pColIter ) + { + const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 ); + if ( pPattern ) + { + rCol = nCol; + return pPattern; + } + + delete pColIter; + ++nCol; + if ( nCol <= nEndCol ) + pColIter = pDoc->pTab[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow ); + else + pColIter = NULL; + } + return NULL; // is nix mehr +} + +//------------------------------------------------------------------------------- + +ScAttrRectIterator::ScAttrRectIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2) : + pDoc( pDocument ), + nTab( nTable ), + nEndCol( nCol2 ), + nStartRow( nRow1 ), + nEndRow( nRow2 ), + nIterStartCol( nCol1 ), + nIterEndCol( nCol1 ) +{ + if ( ValidTab(nTab) && pDoc->pTab[nTab] ) + { + pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow ); + while ( nIterEndCol < nEndCol && + pDoc->pTab[nTab]->aCol[nIterEndCol].IsAllAttrEqual( + pDoc->pTab[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) ) + ++nIterEndCol; + } + else + pColIter = NULL; +} + +ScAttrRectIterator::~ScAttrRectIterator() +{ + delete pColIter; +} + +void ScAttrRectIterator::DataChanged() +{ + if (pColIter) + { + SCROW nNextRow = pColIter->GetNextRow(); + delete pColIter; + pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nNextRow, nEndRow ); + } +} + +const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, + SCROW& rRow1, SCROW& rRow2 ) +{ + while ( pColIter ) + { + const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 ); + if ( pPattern ) + { + rCol1 = nIterStartCol; + rCol2 = nIterEndCol; + return pPattern; + } + + delete pColIter; + nIterStartCol = nIterEndCol+1; + if ( nIterStartCol <= nEndCol ) + { + nIterEndCol = nIterStartCol; + pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow ); + while ( nIterEndCol < nEndCol && + pDoc->pTab[nTab]->aCol[nIterEndCol].IsAllAttrEqual( + pDoc->pTab[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) ) + ++nIterEndCol; + } + else + pColIter = NULL; + } + return NULL; // is nix mehr +} + +// ============================================================================ + +SCROW ScRowBreakIterator::NOT_FOUND = -1; + +ScRowBreakIterator::ScRowBreakIterator(set<SCROW>& rBreaks) : + mrBreaks(rBreaks), + maItr(rBreaks.begin()), maEnd(rBreaks.end()) +{ +} + +SCROW ScRowBreakIterator::first() +{ + maItr = mrBreaks.begin(); + return maItr == maEnd ? NOT_FOUND : *maItr; +} + +SCROW ScRowBreakIterator::next() +{ + ++maItr; + return maItr == maEnd ? NOT_FOUND : *maItr; +} |