| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| // INCLUDE --------------------------------------------------------------- |
| #include "dptablecache.hxx" |
| #include "dptabdat.hxx" |
| #include "document.hxx" |
| #include "cell.hxx" |
| #include "globstr.hrc" |
| |
| #include <rtl/math.hxx> |
| #include "queryparam.hxx" |
| #include "dpglobal.hxx" |
| |
| #include "docoptio.hxx" //for ValidQuery |
| #include <unotools/textsearch.hxx> //for ValidQuery |
| |
| #include <com/sun/star/sdbc/DataType.hpp> |
| #include <com/sun/star/sdbc/XRow.hpp> |
| #include <com/sun/star/sdbc/XRowSet.hpp> |
| #include <com/sun/star/sdbc/XResultSetMetaData.hpp> |
| #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> |
| const double D_TIMEFACTOR = 86400.0; |
| |
| using namespace ::com::sun::star; |
| |
| using ::com::sun::star::uno::Exception; |
| using ::com::sun::star::uno::Reference; |
| using ::com::sun::star::uno::UNO_QUERY; |
| using ::com::sun::star::uno::UNO_QUERY_THROW; |
| |
| // ----------------------------------------------------------------------- |
| namespace |
| { |
| sal_Bool lcl_isDate( sal_uLong nNumType ) |
| { |
| return ( (nNumType & NUMBERFORMAT_DATE) != 0 )? 1:0 ; |
| } |
| |
| sal_Bool lcl_Search( const std::vector<ScDPItemData*>& list, const ::std::vector<SCROW>& rOrder, const ScDPItemData& item, SCROW& rIndex) |
| { |
| rIndex = list.size(); |
| sal_Bool bFound = sal_False; |
| SCROW nLo = 0; |
| SCROW nHi = list.size() - 1; |
| SCROW nIndex; |
| long nCompare; |
| while (nLo <= nHi) |
| { |
| nIndex = (nLo + nHi) / 2; |
| nCompare = ScDPItemData::Compare( *list[rOrder[nIndex]], item ); |
| if (nCompare < 0) |
| nLo = nIndex + 1; |
| else |
| { |
| nHi = nIndex - 1; |
| if (nCompare == 0) |
| { |
| bFound = sal_True; |
| nLo = nIndex; |
| } |
| } |
| } |
| rIndex = nLo; |
| return bFound; |
| } |
| |
| ScDPItemData* lcl_GetItemValue(const Reference<sdbc::XRow>& xRow, sal_Int32 nType, long nCol, |
| const Date& rNullDate ) |
| { |
| short nNumType = NUMBERFORMAT_NUMBER; |
| try |
| { |
| String rStr = xRow->getString(nCol); |
| double fValue = 0.0; |
| switch (nType) |
| { |
| case sdbc::DataType::BIT: |
| case sdbc::DataType::BOOLEAN: |
| { |
| nNumType = NUMBERFORMAT_LOGICAL; |
| fValue = xRow->getBoolean(nCol) ? 1 : 0; |
| return new ScDPItemData( rStr, fValue,sal_True,nNumType); |
| } |
| //break; |
| |
| case sdbc::DataType::TINYINT: |
| case sdbc::DataType::SMALLINT: |
| case sdbc::DataType::INTEGER: |
| case sdbc::DataType::BIGINT: |
| case sdbc::DataType::FLOAT: |
| case sdbc::DataType::REAL: |
| case sdbc::DataType::DOUBLE: |
| case sdbc::DataType::NUMERIC: |
| case sdbc::DataType::DECIMAL: |
| { |
| //! do the conversion here? |
| fValue = xRow->getDouble(nCol); |
| return new ScDPItemData( rStr, fValue,sal_True); |
| } |
| //break; |
| |
| case sdbc::DataType::DATE: |
| { |
| nNumType = NUMBERFORMAT_DATE; |
| |
| util::Date aDate = xRow->getDate(nCol); |
| fValue = Date(aDate.Day, aDate.Month, aDate.Year) - rNullDate; |
| return new ScDPItemData( rStr, fValue, sal_True, nNumType ); |
| } |
| //break; |
| |
| case sdbc::DataType::TIME: |
| { |
| nNumType = NUMBERFORMAT_TIME; |
| |
| util::Time aTime = xRow->getTime(nCol); |
| fValue = ( aTime.Hours * 3600 + aTime.Minutes * 60 + |
| aTime.Seconds + aTime.HundredthSeconds / 100.0 ) / D_TIMEFACTOR; |
| return new ScDPItemData( rStr,fValue, sal_True, nNumType ); |
| } |
| //break; |
| |
| case sdbc::DataType::TIMESTAMP: |
| { |
| nNumType = NUMBERFORMAT_DATETIME; |
| |
| util::DateTime aStamp = xRow->getTimestamp(nCol); |
| fValue = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) - rNullDate ) + |
| ( aStamp.Hours * 3600 + aStamp.Minutes * 60 + |
| aStamp.Seconds + aStamp.HundredthSeconds / 100.0 ) / D_TIMEFACTOR; |
| return new ScDPItemData( rStr,fValue, sal_True, nNumType ); |
| } |
| //break; |
| case sdbc::DataType::CHAR: |
| case sdbc::DataType::VARCHAR: |
| case sdbc::DataType::LONGVARCHAR: |
| case sdbc::DataType::SQLNULL: |
| case sdbc::DataType::BINARY: |
| case sdbc::DataType::VARBINARY: |
| case sdbc::DataType::LONGVARBINARY: |
| default: |
| return new ScDPItemData ( rStr ); |
| //break; |
| } |
| } |
| catch (uno::Exception&) |
| { |
| } |
| catch ( ... ) |
| { |
| |
| } |
| return NULL; |
| } |
| } |
| // Wang Xu Ming -- 12/23/2008 |
| //Refactor cache data |
| ScDPItemData::ScDPItemData( const String& rS, double fV/* = 0.0*/, sal_Bool bHV/* = sal_False*/, const sal_uLong nNumFormatP /*= 0*/ , sal_Bool bData/* = sal_True*/) : |
| nNumFormat( nNumFormatP ), aString(rS), fValue(fV), |
| mbFlag( (MK_VAL*!!bHV) | (MK_DATA*!!bData) | (MK_ERR*!!sal_False) | (MK_DATE*!!lcl_isDate( nNumFormat ) ) ) |
| { |
| } |
| |
| ScDPItemData::ScDPItemData( ScDocument* pDoc, SCROW nRow, sal_uInt16 nCol, sal_uInt16 nDocTab ): |
| nNumFormat( 0 ), fValue(0.0), mbFlag( 0 ) |
| { |
| String aDocStr; |
| pDoc->GetString( nCol, nRow, nDocTab, aDocStr ); |
| |
| SvNumberFormatter* pFormatter = pDoc->GetFormatTable(); |
| |
| ScAddress aPos( nCol, nRow, nDocTab ); |
| ScBaseCell* pCell = pDoc->GetCell( aPos ); |
| |
| if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell)->GetErrCode() ) |
| { |
| SetString ( aDocStr ); |
| //bErr = sal_True; |
| mbFlag |= MK_ERR; |
| } |
| else if ( pDoc->HasValueData( nCol, nRow, nDocTab ) ) |
| { |
| double fVal = pDoc->GetValue(ScAddress(nCol, nRow, nDocTab)); |
| nNumFormat = pDoc->GetNumberFormat( ScAddress( nCol, nRow, nDocTab ) ); |
| sal_uLong nFormat = NUMBERFORMAT_NUMBER; |
| if ( pFormatter ) |
| nFormat = pFormatter->GetType( nNumFormat ); |
| aString = aDocStr; |
| fValue = fVal; |
| mbFlag |= MK_VAL|MK_DATA; |
| lcl_isDate( nFormat ) ? ( mbFlag |= MK_DATE ) : (mbFlag &= ~MK_DATE); |
| } |
| else if ( pDoc->HasData( nCol,nRow, nDocTab ) ) |
| SetString ( aDocStr ); |
| } |
| // End Comments |
| |
| sal_Bool ScDPItemData::IsCaseInsEqual( const ScDPItemData& r ) const |
| { //TODO: indified Date? |
| //! pass Transliteration? |
| //! inline? |
| return IsValue() ? ( r.IsValue() && rtl::math::approxEqual( fValue, r.fValue ) ) : |
| ( !r.IsValue() && |
| ScGlobal::GetpTransliteration()->isEqual( aString, r.aString ) ); |
| } |
| |
| size_t ScDPItemData::Hash() const |
| { |
| if ( IsValue() ) |
| return (size_t) rtl::math::approxFloor( fValue ); |
| else |
| // If we do unicode safe case insensitive hash we can drop |
| // ScDPItemData::operator== and use ::IsCasInsEqual |
| return rtl_ustr_hashCode_WithLength( aString.GetBuffer(), aString.Len() ); |
| } |
| |
| sal_Bool ScDPItemData::operator==( const ScDPItemData& r ) const |
| { |
| if ( IsValue() ) |
| { |
| if( (HasDatePart() != r.HasDatePart()) || (HasDatePart() && mnDatePart != r.mnDatePart) ) |
| return sal_False; |
| |
| // Wang Xu Ming -- 1/9/2009 |
| // Add Data Cache Support. |
| // Identify date |
| if ( IsDate() != r.IsDate() ) |
| return sal_False; |
| else |
| if ( r.IsValue() ) |
| return rtl::math::approxEqual( fValue, r.fValue ); |
| else |
| return sal_False; |
| // End Comments |
| } |
| else if ( r.IsValue() ) |
| return sal_False; |
| else |
| // need exact equality until we have a safe case insensitive string hash |
| return aString == r.aString; |
| } |
| |
| sal_Int32 ScDPItemData::Compare( const ScDPItemData& rA, |
| const ScDPItemData& rB ) |
| { |
| if ( rA.IsValue() ) |
| { |
| if ( rB.IsValue() ) |
| { |
| if ( rtl::math::approxEqual( rA.fValue, rB.fValue ) ) |
| { |
| // Wang Xu Ming -- 1/9/2009 |
| // Add Data Cache Support. |
| // Date > number |
| if ( rA.IsDate() == rB.IsDate() ) |
| return 0; |
| else |
| return rA.IsDate() ? 1: -1; |
| // End Comments |
| } |
| else if ( rA.fValue < rB.fValue ) |
| return -1; |
| else |
| return 1; |
| } |
| else |
| return -1; // values first |
| } |
| else if ( rB.IsValue() ) |
| return 1; // values first |
| else |
| return ScGlobal::GetCollator()->compareString( rA.aString, rB.aString ); |
| } |
| |
| #ifdef DEBUG |
| void ScDPItemData::dump() const |
| { |
| DBG_TRACE1( "Numberformat= %o", nNumFormat ); |
| DBG_TRACESTR(aString ); |
| DBG_TRACE1( "fValue= %f", fValue ); |
| DBG_TRACE1( "mbFlag= %d", mbFlag); |
| } |
| #endif |
| |
| TypedStrData* ScDPItemData::CreateTypeString( ) |
| { |
| if ( IsValue() ) |
| return new TypedStrData( aString, fValue, SC_STRTYPE_VALUE ); |
| else |
| return new TypedStrData( aString ); |
| } |
| |
| sal_uInt8 ScDPItemData::GetType() const |
| { |
| |
| if ( IsHasErr() ) |
| return SC_VALTYPE_ERROR; |
| else if ( !IsHasData() ) |
| return SC_VALTYPE_EMPTY; |
| else if ( IsValue()) |
| return SC_VALTYPE_VALUE; |
| else |
| return SC_VALTYPE_STRING; |
| |
| } |
| |
| sal_Bool ScDPItemData::IsHasData() const |
| { |
| return !!(mbFlag&MK_DATA); |
| } |
| |
| sal_Bool ScDPItemData::IsHasErr() const |
| { |
| return !!(mbFlag&MK_ERR); |
| } |
| |
| sal_Bool ScDPItemData::IsValue() const |
| { |
| return !!(mbFlag&MK_VAL); |
| } |
| |
| String ScDPItemData::GetString() const |
| { |
| |
| return aString; |
| } |
| |
| double ScDPItemData::GetValue() const |
| { |
| return fValue; |
| } |
| sal_uLong ScDPItemData::GetNumFormat() const |
| { |
| return nNumFormat; |
| } |
| |
| sal_Bool ScDPItemData::HasStringData() const |
| |
| { |
| return IsHasData()&&!IsHasErr()&&!IsValue(); |
| } |
| sal_Bool ScDPItemData::IsDate() const |
| { |
| return !!(mbFlag&MK_DATE); |
| } |
| sal_Bool ScDPItemData::HasDatePart() const |
| { |
| return !!(mbFlag&MK_DATEPART); |
| } |
| void ScDPItemData::SetDate( sal_Bool b ) |
| { |
| b ? ( mbFlag |= MK_DATE ) : ( mbFlag &= ~MK_DATE ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| //class ScDPTableDataCache |
| //To cache the pivot table data source |
| |
| sal_Bool ScDPTableDataCache::operator== ( const ScDPTableDataCache& r ) const |
| { |
| if ( GetColumnCount() == r.GetColumnCount() ) |
| { |
| for ( SCCOL i = 0 ; i < GetColumnCount(); i++ ) |
| { //check dim names |
| if ( GetDimensionName( i ) != r.GetDimensionName( i ) ) |
| return sal_False; |
| //check rows count |
| if ( GetRowCount() != r.GetRowCount() ) |
| return sal_False; |
| //check dim member values |
| size_t nMembersCount = GetDimMemberValues( i ).size(); |
| if ( GetDimMemberValues( i ).size() == r. GetDimMemberValues( i ).size() ) |
| { |
| for ( size_t j = 0; j < nMembersCount; j++ ) |
| { |
| if ( *( GetDimMemberValues( i )[j] ) == *( r.GetDimMemberValues( i )[j] ) ) |
| continue; |
| else |
| return sal_False; |
| } |
| } |
| else |
| return sal_False; |
| //check source table index |
| for ( SCROW k=0 ; k < GetRowCount(); k ++ ) |
| { |
| if ( GetItemDataId( i, k, sal_False ) == r.GetItemDataId( i,k,sal_False) ) |
| continue; |
| else |
| return sal_False; |
| } |
| } |
| } |
| return sal_True; |
| } |
| |
| ScDPTableDataCache::ScDPTableDataCache( ScDocument* pDoc ) : |
| mpDoc( pDoc ), |
| mnColumnCount ( 0 ), |
| mpTableDataValues ( NULL ), |
| mpSourceData ( NULL ), |
| mpGlobalOrder( NULL ), |
| mpIndexOrder( NULL) |
| { |
| mnID = -1; |
| } |
| |
| ScDPTableDataCache::~ScDPTableDataCache() |
| { |
| if ( IsValid() ) |
| { |
| // Wang Xu Ming -- 2/17/2009 |
| // Performance issue |
| sal_uInt16 nCol; |
| for ( nCol=0; nCol < GetColumnCount() ; nCol++ ) |
| { |
| for ( sal_uLong row = 0 ; row < mpTableDataValues[nCol].size(); row++ ) |
| delete mpTableDataValues[nCol][row]; |
| } |
| for ( nCol =0; nCol < mrLabelNames.size(); nCol++ ) |
| delete mrLabelNames[nCol]; |
| // End Comments |
| |
| mnColumnCount = 0; |
| delete [] mpTableDataValues; |
| mpTableDataValues = NULL; |
| delete [] mpSourceData; |
| mpSourceData = NULL; |
| delete [] mpGlobalOrder; |
| mpGlobalOrder = NULL; |
| delete [] mpIndexOrder; |
| mpIndexOrder = NULL; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| void ScDPTableDataCache::AddRow( ScDPItemData* pRow, sal_uInt16 nCount ) |
| { |
| DBG_ASSERT( pRow , " empty pointer" ); |
| if ( !mrLabelNames.size() ) |
| { |
| mnColumnCount= nCount; |
| mpTableDataValues = new std::vector<ScDPItemData*>[ mnColumnCount ]; |
| mpSourceData = new std::vector<SCROW>[ mnColumnCount ]; |
| mpGlobalOrder = new std::vector<SCROW>[ mnColumnCount ]; |
| mpIndexOrder = new std::vector<SCROW>[ mnColumnCount ]; |
| |
| for ( sal_uInt16 i = 0; i < nCount ; i ++ ) |
| AddLabel( new ScDPItemData( pRow[i] ) ); |
| } |
| else |
| { |
| for ( sal_uInt16 i = 0; i < nCount && i < mnColumnCount; i ++ ) |
| AddData( i, new ScDPItemData( pRow[i] ) ); |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| bool ScDPTableDataCache::IsValid() const |
| { //TODO: continue check valid |
| return mpTableDataValues!=NULL && mpSourceData!= NULL && mnColumnCount>0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| namespace { |
| |
| /** |
| * While the macro interpret level is incremented, the formula cells are |
| * (semi-)guaranteed to be interpreted. |
| */ |
| class MacroInterpretIncrementer |
| { |
| public: |
| MacroInterpretIncrementer(ScDocument* pDoc) : |
| mpDoc(pDoc) |
| { |
| mpDoc->IncMacroInterpretLevel(); |
| } |
| ~MacroInterpretIncrementer() |
| { |
| mpDoc->DecMacroInterpretLevel(); |
| } |
| private: |
| ScDocument* mpDoc; |
| }; |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| bool ScDPTableDataCache::InitFromDoc( ScDocument* pDoc, const ScRange& rRange ) |
| { |
| // Make sure the formula cells within the data range are interpreted |
| // during this call, for this method may be called from the interpretation |
| // of GETPIVOTDATA, which disables nested formula interpretation without |
| // increasing the macro level. |
| MacroInterpretIncrementer aMacroInc(pDoc); |
| |
| // |
| SCROW nStartRow = rRange.aStart.Row(); // start of data |
| SCROW nEndRow = rRange.aEnd.Row(); |
| sal_uInt16 nStartCol = rRange.aStart.Col(); |
| sal_uInt16 nEndCol = rRange.aEnd.Col(); |
| sal_uInt16 nDocTab = rRange.aStart.Tab(); |
| |
| //init |
| long nOldColumCount = mnColumnCount; |
| mnColumnCount = nEndCol - nStartCol + 1; |
| if ( IsValid() ) |
| { |
| for ( sal_uInt16 nCol=0; nCol < nOldColumCount ; nCol++ ) |
| { |
| for ( sal_uLong row = 0 ; row < mpTableDataValues[nCol].size(); row++ ) |
| delete mpTableDataValues[nCol][row]; |
| delete mrLabelNames[nCol]; |
| } |
| delete [] mpTableDataValues; |
| delete [] mpSourceData; |
| delete [] mpGlobalOrder; |
| delete [] mpIndexOrder; |
| mrLabelNames.clear(); |
| } |
| |
| mpTableDataValues = new std::vector<ScDPItemData*>[ mnColumnCount ]; |
| mpSourceData = new std::vector<SCROW>[ mnColumnCount ]; |
| mpGlobalOrder = new std::vector<SCROW>[ mnColumnCount ]; |
| mpIndexOrder = new std::vector<SCROW>[ mnColumnCount ]; |
| pDoc->FillDPCache( this, nDocTab, nStartCol, nEndCol, nStartRow, nEndRow ); |
| return sal_True; |
| } |
| |
| // ----------------------------------------------------------------------- |
| bool ScDPTableDataCache::InitFromDataBase (const Reference<sdbc::XRowSet>& xRowSet, const Date& rNullDate) |
| { |
| if (!xRowSet.is()) |
| // Dont' even waste time to go any further. |
| return false; |
| try |
| { |
| Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp(xRowSet, UNO_QUERY_THROW); |
| Reference<sdbc::XResultSetMetaData> xMeta = xMetaSupp->getMetaData(); |
| if (!xMeta.is()) |
| return false; |
| |
| long nOldColumCount = mnColumnCount; |
| mnColumnCount = xMeta->getColumnCount(); |
| if ( IsValid() ) |
| { |
| for ( sal_uInt16 nCol=0; nCol < nOldColumCount ; nCol++ ) |
| { |
| for ( sal_uLong row = 0 ; row < mpTableDataValues[nCol].size(); row++ ) |
| delete mpTableDataValues[nCol][row]; |
| delete mrLabelNames[nCol]; |
| } |
| delete [] mpTableDataValues; |
| delete [] mpSourceData; |
| delete [] mpGlobalOrder; |
| delete [] mpIndexOrder; |
| mrLabelNames.clear(); |
| } |
| // Get column titles and types. |
| mrLabelNames.reserve(mnColumnCount); |
| mpTableDataValues = new std::vector<ScDPItemData*>[ mnColumnCount ]; |
| mpSourceData = new std::vector<SCROW>[ mnColumnCount ]; |
| mpGlobalOrder = new std::vector<SCROW>[ mnColumnCount ]; |
| mpIndexOrder = new std::vector<SCROW>[ mnColumnCount ]; |
| |
| std::vector<sal_Int32> aColTypes(mnColumnCount); |
| |
| for (sal_Int32 nCol = 0; nCol < mnColumnCount; ++nCol) |
| { |
| String aColTitle = xMeta->getColumnLabel(nCol+1); |
| aColTypes[nCol] = xMeta->getColumnType(nCol+1); |
| AddLabel( new ScDPItemData( aColTitle) ); |
| } |
| |
| // Now get the data rows. |
| Reference<sdbc::XRow> xRow(xRowSet, UNO_QUERY_THROW); |
| xRowSet->first(); |
| do |
| { |
| for (sal_Int32 nCol = 0; nCol < mnColumnCount; ++nCol) |
| { |
| ScDPItemData * pNew = lcl_GetItemValue( xRow, aColTypes[nCol], nCol+1, rNullDate ); |
| if ( pNew ) |
| AddData( nCol , pNew ); |
| } |
| } |
| while (xRowSet->next()); |
| |
| xRowSet->beforeFirst(); |
| |
| return true; |
| } |
| catch (const Exception&) |
| { |
| return false; |
| } |
| } |
| // ----------------------------------------------------------------------- |
| sal_uLong ScDPTableDataCache::GetDimNumType( SCCOL nDim) const |
| { |
| DBG_ASSERT( IsValid(), " IsValid() == false " ); |
| DBG_ASSERT( nDim < mnColumnCount && nDim >=0, " dimension out of bound " ); |
| if ( mpTableDataValues[nDim].size()==0 ) |
| return NUMBERFORMAT_UNDEFINED; |
| else |
| return GetNumType(mpTableDataValues[nDim][0]->nNumFormat); |
| } |
| |
| // ----------------------------------------------------------------------- |
| bool ScDPTableDataCache::ValidQuery( SCROW nRow, const ScQueryParam &rParam, sal_Bool *pSpecial) |
| { //Copied and modified from ScTable::ValidQuery |
| if (!rParam.GetEntry(0).bDoQuery) |
| return sal_True; |
| sal_Bool bMatchWholeCell = mpDoc->GetDocOptions().IsMatchWholeCell(); |
| |
| //--------------------------------------------------------------- |
| |
| const SCSIZE nFixedBools = 32; |
| sal_Bool aBool[nFixedBools]; |
| sal_Bool aTest[nFixedBools]; |
| SCSIZE nEntryCount = rParam.GetEntryCount(); |
| sal_Bool* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new sal_Bool[nEntryCount] ); |
| sal_Bool* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new sal_Bool[nEntryCount] ); |
| |
| long nPos = -1; |
| SCSIZE i = 0; |
| CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::GetCaseCollator() : |
| ScGlobal::GetCollator() ); |
| ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ? |
| ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration()); |
| |
| while ( (i < nEntryCount) && rParam.GetEntry(i).bDoQuery ) |
| { |
| ScQueryEntry& rEntry = rParam.GetEntry(i); |
| // we can only handle one single direct query |
| // #i115431# nField in QueryParam is the sheet column, not the field within the source range |
| SCCOL nQueryCol = (SCCOL)rEntry.nField; |
| if ( nQueryCol < rParam.nCol1 ) |
| nQueryCol = rParam.nCol1; |
| if ( nQueryCol > rParam.nCol2 ) |
| nQueryCol = rParam.nCol2; |
| SCCOL nSourceField = nQueryCol - rParam.nCol1; |
| SCROW nId = GetItemDataId( nSourceField, nRow, sal_False ); |
| const ScDPItemData* pCellData = GetItemDataById( nSourceField, nId ); |
| |
| sal_Bool bOk = sal_False; |
| sal_Bool bTestEqual = sal_False; |
| |
| if ( pSpecial && pSpecial[i] ) |
| { |
| if (rEntry.nVal == SC_EMPTYFIELDS) |
| bOk = ! pCellData->IsHasData(); |
| else // if (rEntry.nVal == SC_NONEMPTYFIELDS) |
| bOk = pCellData->IsHasData(); |
| } |
| else if ( !rEntry.bQueryByString && pCellData->IsValue() ) |
| { // by Value |
| double nCellVal = pCellData->GetValue(); |
| |
| switch (rEntry.eOp) |
| { |
| case SC_EQUAL : |
| bOk = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); |
| break; |
| case SC_LESS : |
| bOk = (nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); |
| break; |
| case SC_GREATER : |
| bOk = (nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); |
| break; |
| case SC_LESS_EQUAL : |
| bOk = (nCellVal < rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); |
| break; |
| case SC_GREATER_EQUAL : |
| bOk = (nCellVal > rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); |
| break; |
| case SC_NOT_EQUAL : |
| bOk = !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); |
| break; |
| default: |
| bOk= sal_False; |
| break; |
| } |
| } |
| else if ( (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) |
| || (rEntry.bQueryByString |
| && pCellData->HasStringData() ) |
| ) |
| { // by String |
| String aCellStr = pCellData->GetString(); |
| |
| sal_Bool bRealRegExp = (rParam.bRegExp && ((rEntry.eOp == SC_EQUAL) |
| || (rEntry.eOp == SC_NOT_EQUAL))); |
| sal_Bool bTestRegExp = sal_False; |
| if ( bRealRegExp || bTestRegExp ) |
| { |
| xub_StrLen nStart = 0; |
| xub_StrLen nEnd = aCellStr.Len(); |
| sal_Bool bMatch = (sal_Bool) rEntry.GetSearchTextPtr( rParam.bCaseSens ) |
| ->SearchFrwrd( aCellStr, &nStart, &nEnd ); |
| // from 614 on, nEnd is behind the found text |
| if ( bMatch && bMatchWholeCell |
| && (nStart != 0 || nEnd != aCellStr.Len()) ) |
| bMatch = sal_False; // RegExp must match entire cell string |
| if ( bRealRegExp ) |
| bOk = ((rEntry.eOp == SC_NOT_EQUAL) ? !bMatch : bMatch); |
| else |
| bTestEqual = bMatch; |
| } |
| if ( !bRealRegExp ) |
| { |
| if ( rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL ) |
| { |
| if ( bMatchWholeCell ) |
| { |
| bOk = pTransliteration->isEqual( aCellStr, *rEntry.pStr ); |
| |
| String aStr = *rEntry.pStr;//"f*" |
| //use another way to find "*" in aStr |
| sal_Bool bHasStar = sal_False; |
| xub_StrLen nIndex; |
| if( ( nIndex = aStr.Search('*') ) != STRING_NOTFOUND ) |
| bHasStar = sal_True; |
| if(bHasStar && (nIndex>0)) |
| { |
| for(i=0;(i<nIndex) && (i< aCellStr.Len()) ; i++) |
| { |
| if(aCellStr.GetChar( (sal_uInt16)i ) == aStr.GetChar((sal_uInt16) i )) |
| { |
| bOk=1; |
| } |
| else |
| { |
| bOk=0; |
| break; |
| } |
| } |
| } |
| //end modified |
| //Added end,20060808 |
| } |
| else |
| { |
| ::com::sun::star::uno::Sequence< sal_Int32 > xOff; |
| String aCell( pTransliteration->transliterate( |
| aCellStr, ScGlobal::eLnge, 0, aCellStr.Len(), |
| &xOff ) ); |
| String aQuer( pTransliteration->transliterate( |
| *rEntry.pStr, ScGlobal::eLnge, 0, rEntry.pStr->Len(), |
| &xOff ) ); |
| bOk = (aCell.Search( aQuer ) != STRING_NOTFOUND); |
| } |
| if ( rEntry.eOp == SC_NOT_EQUAL ) |
| bOk = !bOk; |
| } |
| else |
| { // use collator here because data was probably sorted |
| sal_Int32 nCompare = pCollator->compareString( |
| aCellStr, *rEntry.pStr ); |
| switch (rEntry.eOp) |
| { |
| case SC_LESS : |
| bOk = (nCompare < 0); |
| break; |
| case SC_GREATER : |
| bOk = (nCompare > 0); |
| break; |
| case SC_LESS_EQUAL : |
| bOk = (nCompare <= 0); |
| break; |
| case SC_GREATER_EQUAL : |
| bOk = (nCompare >= 0); |
| break; |
| case SC_NOT_EQUAL: |
| DBG_ASSERT( false , "SC_NOT_EQUAL"); |
| break; |
| case SC_TOPVAL: |
| case SC_BOTVAL: |
| case SC_TOPPERC: |
| case SC_BOTPERC: |
| default: |
| break; |
| } |
| } |
| } |
| } |
| |
| if (nPos == -1) |
| { |
| nPos++; |
| pPasst[nPos] = bOk; |
| pTest[nPos] = bTestEqual; |
| } |
| else |
| { |
| if (rEntry.eConnect == SC_AND) |
| { |
| pPasst[nPos] = pPasst[nPos] && bOk; |
| pTest[nPos] = pTest[nPos] && bTestEqual; |
| } |
| else |
| { |
| nPos++; |
| pPasst[nPos] = bOk; |
| pTest[nPos] = bTestEqual; |
| } |
| } |
| i++; |
| } |
| |
| for ( long j=1; j <= nPos; j++ ) |
| { |
| pPasst[0] = pPasst[0] || pPasst[j]; |
| pTest[0] = pTest[0] || pTest[j]; |
| } |
| |
| sal_Bool bRet = pPasst[0]; |
| if ( pPasst != &aBool[0] ) |
| delete [] pPasst; |
| if ( pTest != &aTest[0] ) |
| delete [] pTest; |
| |
| return bRet; |
| } |
| |
| // ----------------------------------------------------------------------- |
| bool ScDPTableDataCache::IsRowEmpty( SCROW nRow ) const |
| { |
| return mbEmptyRow[ nRow ]; |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| bool ScDPTableDataCache::IsEmptyMember( SCROW nRow, sal_uInt16 nColumn ) const |
| { |
| return !GetItemDataById( nColumn, GetItemDataId( nColumn, nRow, sal_False ) )->IsHasData(); |
| } |
| |
| sal_Bool ScDPTableDataCache::AddData(long nDim, ScDPItemData* pitemData, bool bCheckDate ) |
| { |
| DBG_ASSERT( IsValid(), " IsValid() == false " ); |
| DBG_ASSERT( nDim < mnColumnCount && nDim >=0 , "dimension out of bound" ); |
| SCROW nIndex = 0; |
| |
| sal_Bool bInserted = sal_False; |
| if( true == bCheckDate) |
| pitemData->SetDate( lcl_isDate( GetNumType( pitemData->nNumFormat ) ) ); |
| if ( !lcl_Search( mpTableDataValues[nDim], mpGlobalOrder[nDim], *pitemData, nIndex ) ) |
| { |
| mpTableDataValues[nDim].push_back( pitemData ); |
| mpGlobalOrder[nDim].insert( mpGlobalOrder[nDim].begin()+nIndex, mpTableDataValues[nDim].size()-1 ); |
| DBG_ASSERT( (size_t) mpGlobalOrder[nDim][nIndex] == mpTableDataValues[nDim].size()-1 ,"ScDPTableDataCache::AddData "); |
| mpSourceData[nDim].push_back( mpTableDataValues[nDim].size()-1 ); |
| bInserted = sal_True; |
| } |
| else |
| mpSourceData[nDim].push_back( mpGlobalOrder[nDim][nIndex] ); |
| //init empty row tag |
| size_t nCurRow = mpSourceData[nDim].size() -1 ; |
| |
| while ( mbEmptyRow.size() <= nCurRow ) |
| mbEmptyRow.push_back( sal_True ); |
| |
| if ( pitemData->IsHasData() ) |
| mbEmptyRow[ nCurRow ] = sal_False; |
| |
| if ( !bInserted ) |
| delete pitemData; |
| |
| return sal_True; |
| } |
| |
| String ScDPTableDataCache::GetDimensionName( sal_uInt16 nColumn ) const |
| { |
| DBG_ASSERT( /* nColumn>=0 && */ nColumn < mrLabelNames.size()-1 , "ScDPTableDataCache::GetDimensionName"); |
| DBG_ASSERT( mrLabelNames.size() == static_cast <sal_uInt16> (mnColumnCount+1), "ScDPTableDataCache::GetDimensionName"); |
| if ( static_cast<size_t>(nColumn+1) < mrLabelNames.size() ) |
| { |
| return mrLabelNames[nColumn+1]->aString; |
| } |
| else |
| return String(); |
| } |
| |
| |
| void ScDPTableDataCache::AddLabel(ScDPItemData *pData) |
| { |
| DBG_ASSERT( IsValid(), " IsValid() == false " ); |
| |
| if ( mrLabelNames.size() == 0 ) |
| mrLabelNames.push_back( new ScDPItemData( ScGlobal::GetRscString(STR_PIVOT_DATA) ) ); |
| |
| |
| //reset name if needed |
| String strNewName = pData->aString; |
| |
| // #i116457# don't modify empty column titles |
| if ( strNewName.Len() ) |
| { |
| sal_Bool bFound = sal_False; |
| long nIndex = 1; |
| do |
| { |
| for ( long i= mrLabelNames.size()-1; i>=0; i-- ) |
| { |
| if( mrLabelNames[i]->aString == strNewName ) |
| { |
| strNewName = pData->aString; |
| strNewName += String::CreateFromInt32( nIndex ); |
| nIndex ++ ; |
| bFound = sal_True; |
| } |
| } |
| bFound = !bFound; |
| } |
| while ( !bFound ); |
| } |
| |
| pData->aString = strNewName; |
| mrLabelNames.push_back( pData ); |
| } |
| |
| SCROW ScDPTableDataCache::GetItemDataId(sal_uInt16 nDim, SCROW nRow, sal_Bool bRepeatIfEmpty) const |
| { // |
| DBG_ASSERT( IsValid(), " IsValid() == false " ); |
| DBG_ASSERT( /* nDim >= 0 && */ nDim < mnColumnCount, "ScDPTableDataCache::GetItemDataId " ); |
| |
| if ( bRepeatIfEmpty ) |
| { |
| while ( nRow >0 && !mpTableDataValues[nDim][ mpSourceData[nDim][nRow] ]->IsHasData() ) |
| --nRow; |
| } |
| |
| return mpSourceData[nDim][nRow]; |
| } |
| |
| const ScDPItemData* ScDPTableDataCache::GetItemDataById(long nDim, SCROW nId) const |
| { |
| if ( nId >= GetRowCount() ) |
| return maAdditionalDatas.getData( nId - GetRowCount() ); |
| |
| if ( (size_t)nId >= mpTableDataValues[nDim].size() || nDim >= mnColumnCount || nId < 0 ) |
| return NULL; |
| else |
| return mpTableDataValues[nDim][nId]; |
| } |
| |
| SCROW ScDPTableDataCache::GetRowCount() const |
| { |
| if ( IsValid() ) |
| return mpSourceData[0].size(); |
| else |
| return 0; |
| } |
| |
| const std::vector<ScDPItemData*>& ScDPTableDataCache::GetDimMemberValues(SCCOL nDim) const |
| { |
| DBG_ASSERT( nDim>=0 && nDim < mnColumnCount ," nDim < mnColumnCount "); |
| return mpTableDataValues[nDim]; |
| } |
| |
| SCROW ScDPTableDataCache::GetSortedItemDataId(SCCOL nDim, SCROW nOrder) const |
| { |
| DBG_ASSERT ( IsValid(), "IsValid"); |
| DBG_ASSERT( nDim>=0 && nDim < mnColumnCount, "nDim < mnColumnCount"); |
| DBG_ASSERT( nOrder >= 0 && (size_t) nOrder < mpGlobalOrder[nDim].size(), "nOrder < mpGlobalOrder[nDim].size()" ); |
| |
| return mpGlobalOrder[nDim][nOrder]; |
| } |
| |
| sal_uLong ScDPTableDataCache::GetNumType(sal_uLong nFormat) const |
| { |
| SvNumberFormatter* pFormatter = mpDoc->GetFormatTable(); |
| sal_uLong nType = NUMBERFORMAT_NUMBER; |
| if ( pFormatter ) |
| nType = pFormatter->GetType( nFormat ); |
| return nType; |
| } |
| |
| sal_uLong ScDPTableDataCache::GetNumberFormat( long nDim ) const |
| { |
| if ( nDim >= mnColumnCount ) |
| return 0; |
| |
| // #i113411# take the number format from the first value entry |
| size_t nSize = mpTableDataValues[nDim].size(); |
| size_t nPos = 0; |
| while ( nPos < nSize && mpTableDataValues[nDim][nPos]->GetType() != SC_VALTYPE_VALUE ) |
| ++nPos; |
| if ( nPos < nSize ) |
| return mpTableDataValues[nDim][nPos]->nNumFormat; |
| return 0; |
| } |
| |
| sal_Bool ScDPTableDataCache::IsDateDimension( long nDim ) const |
| { |
| if ( nDim >= mnColumnCount ) |
| return false; |
| else if ( mpTableDataValues[nDim].size()==0 ) |
| return false; |
| else |
| return mpTableDataValues[nDim][0]->IsDate(); |
| |
| } |
| |
| SCROW ScDPTableDataCache::GetDimMemberCount( SCCOL nDim ) const |
| { |
| DBG_ASSERT( nDim>=0 && nDim < mnColumnCount ," ScDPTableDataCache::GetDimMemberCount : out of bound "); |
| return mpTableDataValues[nDim].size(); |
| } |
| |
| const ScDPItemData* ScDPTableDataCache::GetSortedItemData(SCCOL nDim, SCROW nOrder) const |
| { |
| SCROW n = GetSortedItemDataId( nDim, nOrder ); |
| return GetItemDataById( nDim, n ); |
| } |
| |
| SCCOL ScDPTableDataCache::GetDimensionIndex(String sName) const |
| { |
| for ( size_t n = 1; n < mrLabelNames.size(); n ++ ) //defects, label name map wrong |
| { |
| if ( mrLabelNames[n]->GetString() == sName ) |
| return (SCCOL)(n-1); |
| } |
| return -1; |
| } |
| |
| SCROW ScDPTableDataCache::GetIdByItemData(long nDim, String sItemData ) const |
| { |
| if ( nDim < mnColumnCount && nDim >=0 ) |
| { |
| for ( size_t n = 0; n< mpTableDataValues[nDim].size(); n++ ) |
| { |
| if ( mpTableDataValues[nDim][n]->GetString() == sItemData ) |
| return n; |
| } |
| } |
| |
| ScDPItemData rData ( sItemData ); |
| return GetRowCount() +maAdditionalDatas.getDataId(rData); |
| } |
| |
| SCROW ScDPTableDataCache::GetIdByItemData( long nDim, const ScDPItemData& rData ) const |
| { |
| if ( nDim < mnColumnCount && nDim >=0 ) |
| { |
| for ( size_t n = 0; n< mpTableDataValues[nDim].size(); n++ ) |
| { |
| if ( *mpTableDataValues[nDim][n] == rData ) |
| return n; |
| } |
| } |
| return GetRowCount() + maAdditionalDatas.getDataId(rData); |
| } |
| |
| SCROW ScDPTableDataCache::GetAdditionalItemID ( String sItemData ) |
| { |
| ScDPItemData rData ( sItemData ); |
| return GetAdditionalItemID( rData ); |
| } |
| |
| SCROW ScDPTableDataCache::GetAdditionalItemID( const ScDPItemData& rData ) |
| { |
| return GetRowCount() + maAdditionalDatas.insertData( rData ); |
| } |
| |
| |
| SCROW ScDPTableDataCache::GetOrder(long nDim, SCROW nIndex) const |
| { |
| DBG_ASSERT( IsValid(), " IsValid() == false " ); |
| DBG_ASSERT( nDim >=0 && nDim < mnColumnCount, "ScDPTableDataCache::GetOrder : out of bound" ); |
| |
| if ( mpIndexOrder[nDim].size() != mpGlobalOrder[nDim].size() ) |
| { //not inited |
| SCROW i = 0; |
| mpIndexOrder[nDim].resize( mpGlobalOrder[nDim].size(), 0 ); |
| for ( size_t n = 0 ; n< mpGlobalOrder[nDim].size(); n++ ) |
| { |
| i = mpGlobalOrder[nDim][n]; |
| mpIndexOrder[nDim][ i ] = n; |
| } |
| } |
| |
| DBG_ASSERT( nIndex>=0 && (size_t)nIndex < mpIndexOrder[nDim].size() , "ScDPTableDataCache::GetOrder"); |
| return mpIndexOrder[nDim][nIndex]; |
| } |
| |
| ScDocument* ScDPTableDataCache::GetDoc() const |
| { |
| return mpDoc; |
| }; |
| |
| long ScDPTableDataCache::GetColumnCount() const |
| { |
| return mnColumnCount; |
| } |
| long ScDPTableDataCache::GetId() const |
| { |
| return mnID; |
| } |