/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: SchXMLTableContext.cxx,v $ * * $Revision: 1.15 $ * * last change: $Author: hr $ $Date: 2006-06-19 18:02:09 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ #include "SchXMLTableContext.hxx" #include "SchXMLParagraphContext.hxx" #include "SchXMLImport.hxx" #include "transporttypes.hxx" #ifndef _TOOLS_DEBUG_HXX #include #endif #ifndef INCLUDED_RTL_MATH_HXX #include #endif #ifndef _XMLOFF_XMLNMSPE_HXX #include "xmlnmspe.hxx" #endif #ifndef _XMLOFF_XMLTOKEN_HXX #include "xmltoken.hxx" #endif #ifndef _XMLOFF_NMSPMAP_HXX #include "nmspmap.hxx" #endif #ifndef _XMLOFF_XMLUCONV_HXX #include "xmluconv.hxx" #endif #ifndef _COM_SUN_STAR_FRAME_XMODEL_HPP_ #include #endif #ifndef _COM_SUN_STAR_CHART_XCHARTDOCUMENT_HPP_ #include #endif #ifndef _COM_SUN_STAR_CHART_XCHARTDATAARRAY_HPP_ #include #endif #ifndef _COM_SUN_STAR_CHART_CHARTDATAROWSOURCE_HPP_ #include #endif #ifndef _COM_SUN_STAR_CHART_CHARTSERIESADDRESS_HPP_ #include #endif using namespace com::sun::star; using namespace ::xmloff::token; // ---------------------------------------- // class SchXMLTableContext // ---------------------------------------- SchXMLTableContext::SchXMLTableContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, const rtl::OUString& rLName, SchXMLTable& aTable ) : SvXMLImportContext( rImport, XML_NAMESPACE_TABLE, rLName ), mrImportHelper( rImpHelper ), mrTable( aTable ) { mrTable.nColumnIndex = -1; mrTable.nMaxColumnIndex = -1; mrTable.nRowIndex = -1; mrTable.aData.clear(); } SchXMLTableContext::~SchXMLTableContext() { } SvXMLImportContext *SchXMLTableContext::CreateChildContext( USHORT nPrefix, const rtl::OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList >& ) { SvXMLImportContext* pContext = 0; const SvXMLTokenMap& rTokenMap = mrImportHelper.GetTableElemTokenMap(); switch( rTokenMap.Get( nPrefix, rLocalName )) { case XML_TOK_TABLE_HEADER_COLS: case XML_TOK_TABLE_COLUMNS: pContext = new SchXMLTableColumnsContext( mrImportHelper, GetImport(), rLocalName, mrTable ); break; case XML_TOK_TABLE_COLUMN: pContext = new SchXMLTableColumnContext( mrImportHelper, GetImport(), rLocalName, mrTable ); break; case XML_TOK_TABLE_HEADER_ROWS: case XML_TOK_TABLE_ROWS: pContext = new SchXMLTableRowsContext( mrImportHelper, GetImport(), rLocalName, mrTable ); break; case XML_TOK_TABLE_ROW: pContext = new SchXMLTableRowContext( mrImportHelper, GetImport(), rLocalName, mrTable ); break; default: pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); } return pContext; } // ======================================== // classes for columns // ======================================== // ---------------------------------------- // class SchXMLTableColumnsContext // ---------------------------------------- SchXMLTableColumnsContext::SchXMLTableColumnsContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, const rtl::OUString& rLocalName, SchXMLTable& aTable ) : SvXMLImportContext( rImport, XML_NAMESPACE_TABLE, rLocalName ), mrImportHelper( rImpHelper ), mrTable( aTable ) { } SchXMLTableColumnsContext::~SchXMLTableColumnsContext() { } SvXMLImportContext* SchXMLTableColumnsContext::CreateChildContext( USHORT nPrefix, const rtl::OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList >& ) { SvXMLImportContext* pContext = 0; if( nPrefix == XML_NAMESPACE_TABLE && IsXMLToken( rLocalName, XML_TABLE_COLUMN ) ) { pContext = new SchXMLTableColumnContext( mrImportHelper, GetImport(), rLocalName, mrTable ); } else pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); return pContext; } // ---------------------------------------- // class SchXMLTableColumnContext // ---------------------------------------- SchXMLTableColumnContext::SchXMLTableColumnContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, const rtl::OUString& rLocalName, SchXMLTable& aTable ) : SvXMLImportContext( rImport, XML_NAMESPACE_TABLE, rLocalName ), mrImportHelper( rImpHelper ), mrTable( aTable ) { } void SchXMLTableColumnContext::StartElement( const uno::Reference< xml::sax::XAttributeList >& xAttrList ) { // get number-columns-repeated attribute sal_Int16 nAttrCount = xAttrList.is()? xAttrList->getLength(): 0; rtl::OUString aValue; for( sal_Int16 i = 0; i < nAttrCount; i++ ) { rtl::OUString sAttrName = xAttrList->getNameByIndex( i ); rtl::OUString aLocalName; USHORT nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName ); if( nPrefix == XML_NAMESPACE_TABLE && IsXMLToken( aLocalName, XML_NUMBER_COLUMNS_REPEATED ) ) { aValue = xAttrList->getValueByIndex( i ); break; // we only need this attribute } } if( aValue.getLength()) { sal_Int32 nRepeated = aValue.toInt32(); mrTable.nNumberOfColsEstimate += nRepeated; } else { mrTable.nNumberOfColsEstimate++; } } SchXMLTableColumnContext::~SchXMLTableColumnContext() { } // ======================================== // classes for rows // ======================================== // ---------------------------------------- // class SchXMLTableRowsContext // ---------------------------------------- SchXMLTableRowsContext::SchXMLTableRowsContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, const rtl::OUString& rLocalName, SchXMLTable& aTable ) : SvXMLImportContext( rImport, XML_NAMESPACE_TABLE, rLocalName ), mrImportHelper( rImpHelper ), mrTable( aTable ) { } SchXMLTableRowsContext::~SchXMLTableRowsContext() { } SvXMLImportContext* SchXMLTableRowsContext::CreateChildContext( USHORT nPrefix, const rtl::OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList >& ) { SvXMLImportContext* pContext = 0; if( nPrefix == XML_NAMESPACE_TABLE && IsXMLToken( rLocalName, XML_TABLE_ROW ) ) { pContext = new SchXMLTableRowContext( mrImportHelper, GetImport(), rLocalName, mrTable ); } else { pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); } return pContext; } // ---------------------------------------- // class SchXMLTableRowContext // ---------------------------------------- SchXMLTableRowContext::SchXMLTableRowContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, const rtl::OUString& rLocalName, SchXMLTable& aTable ) : SvXMLImportContext( rImport, XML_NAMESPACE_TABLE, rLocalName ), mrImportHelper( rImpHelper ), mrTable( aTable ) { mrTable.nColumnIndex = -1; mrTable.nRowIndex++; std::vector< SchXMLCell > aNewRow; aNewRow.reserve( mrTable.nNumberOfColsEstimate ); while( mrTable.aData.size() <= (unsigned long)mrTable.nRowIndex ) mrTable.aData.push_back( aNewRow ); } SchXMLTableRowContext::~SchXMLTableRowContext() { } SvXMLImportContext* SchXMLTableRowContext::CreateChildContext( USHORT nPrefix, const rtl::OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList >& ) { SvXMLImportContext* pContext = 0; // element if( nPrefix == XML_NAMESPACE_TABLE && IsXMLToken(rLocalName, XML_TABLE_CELL ) ) { pContext = new SchXMLTableCellContext( mrImportHelper, GetImport(), rLocalName, mrTable ); } else { pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); } return pContext; } // ======================================== // classes for cells and their content // ======================================== // ---------------------------------------- // class SchXMLTableCellContext // ---------------------------------------- SchXMLTableCellContext::SchXMLTableCellContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, const rtl::OUString& rLocalName, SchXMLTable& aTable ) : SvXMLImportContext( rImport, XML_NAMESPACE_TABLE, rLocalName ), mrImportHelper( rImpHelper ), mrTable( aTable ) { } SchXMLTableCellContext::~SchXMLTableCellContext() { } void SchXMLTableCellContext::StartElement( const uno::Reference< xml::sax::XAttributeList >& xAttrList ) { sal_Int16 nAttrCount = xAttrList.is()? xAttrList->getLength(): 0; rtl::OUString aValue; rtl::OUString aLocalName; rtl::OUString aCellContent; SchXMLCellType eValueType = SCH_CELL_TYPE_UNKNOWN; const SvXMLTokenMap& rAttrTokenMap = mrImportHelper.GetCellAttrTokenMap(); for( sal_Int16 i = 0; i < nAttrCount; i++ ) { rtl::OUString sAttrName = xAttrList->getNameByIndex( i ); USHORT nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName ); switch( rAttrTokenMap.Get( nPrefix, aLocalName )) { case XML_TOK_CELL_VAL_TYPE: aValue = xAttrList->getValueByIndex( i ); if( IsXMLToken( aValue, XML_FLOAT ) ) eValueType = SCH_CELL_TYPE_FLOAT; else if( IsXMLToken( aValue, XML_STRING ) ) eValueType = SCH_CELL_TYPE_STRING; break; case XML_TOK_CELL_VALUE: aCellContent = xAttrList->getValueByIndex( i ); break; } } mbReadPara = sal_True; SchXMLCell aCell; aCell.eType = eValueType; if( eValueType == SCH_CELL_TYPE_FLOAT ) { double fData; // the result may be false if a NaN is read, but that's ok SvXMLUnitConverter::convertDouble( fData, aCellContent ); aCell.fValue = fData; // dont read following element mbReadPara = sal_False; } mrTable.aData[ mrTable.nRowIndex ].push_back( aCell ); mrTable.nColumnIndex++; if( mrTable.nMaxColumnIndex < mrTable.nColumnIndex ) mrTable.nMaxColumnIndex = mrTable.nColumnIndex; } SvXMLImportContext* SchXMLTableCellContext::CreateChildContext( USHORT nPrefix, const rtl::OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList >& ) { SvXMLImportContext* pContext = 0; // element if( mbReadPara && nPrefix == XML_NAMESPACE_TEXT && IsXMLToken( rLocalName, XML_P ) ) { // we have to read a string here (not a float) pContext = new SchXMLParagraphContext( GetImport(), rLocalName, maCellContent ); } else { pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); } return pContext; } void SchXMLTableCellContext::EndElement() { if( mbReadPara && maCellContent.getLength()) mrTable.aData[ mrTable.nRowIndex ][ mrTable.nColumnIndex ].aString = maCellContent; } // ======================================== // just interpret the table in a linear way with no references used // (this is just a workaround for clipboard handling in EA2) void SchXMLTableHelper::applyTableSimple( const SchXMLTable& rTable, uno::Reference< chart::XChartDocument > xChartDoc ) { // interpret table like this: // // series ----+---+ // | | // categories | | // | | | // V V V // A B C ... // 1 x x <--- labels // 2 x 0 0 // 3 x 0 0 // ... if( xChartDoc.is()) { uno::Reference< chart::XChartDataArray > xData( xChartDoc->getData(), uno::UNO_QUERY ); if( xData.is()) { // get NaN double fSolarNaN; ::rtl::math::setNan( &fSolarNaN ); double fNaN = fSolarNaN; sal_Bool bConvertNaN = sal_False; uno::Reference< chart::XChartData > xChartData( xData, uno::UNO_QUERY ); if( xChartData.is()) { fNaN = xChartData->getNotANumber(); bConvertNaN = ( ! ::rtl::math::isNan( fNaN )); } sal_Int32 nRowCount = rTable.aData.size(); sal_Int32 nColumnCount = 0; sal_Int32 nCol = 0, nRow = 0; if( nRowCount ) nColumnCount = rTable.aData[ 0 ].size(); // #i27909# avoid illegal index access for empty tables if( nColumnCount == 0 || nRowCount == 0 ) return; uno::Sequence< ::rtl::OUString > aCategories( nRowCount - 1 ); uno::Sequence< ::rtl::OUString > aLabels( nColumnCount - 1 ); uno::Sequence< uno::Sequence< double > > aData( nRowCount - 1 ); for( nRow = 0; nRow < nRowCount - 1; nRow++ ) aData[ nRow ].realloc( nColumnCount - 1 ); // set labels ::std::vector< ::std::vector< SchXMLCell > >::const_iterator iRow = rTable.aData.begin(); for( nCol = 1; nCol < nColumnCount; nCol++ ) { aLabels[ nCol - 1 ] = (*iRow)[ nCol ].aString; } xData->setColumnDescriptions( aLabels ); double fVal; const sal_Bool bConstConvertNan = bConvertNaN; for( ++iRow, nRow = 0; iRow != rTable.aData.end(); iRow++, nRow++ ) { aCategories[ nRow ] = (*iRow)[ 0 ].aString; for( nCol = 1; nCol < nColumnCount; nCol++ ) { fVal = (*iRow)[ nCol ].fValue; if( bConstConvertNan && ::rtl::math::isNan( fVal )) aData[ nRow ][ nCol - 1 ] = fNaN; else aData[ nRow ][ nCol - 1 ] = fVal; } } xData->setRowDescriptions( aCategories ); xData->setData( aData ); } } } // ---------------------------------------- void SchXMLTableHelper::applyTable( const SchXMLTable& rTable, uno::Sequence< chart::ChartSeriesAddress >& rSeriesAddresses, rtl::OUString& rCategoriesAddress, uno::Reference< chart::XChartDocument > xChartDoc ) { // general note: series are always interpreted as columns in import // first check if data can be attached to an appropriate object if( rTable.nRowIndex > -1 && xChartDoc.is()) { uno::Reference< chart::XChartDataArray > xData( xChartDoc->getData(), uno::UNO_QUERY ); if( xData.is()) { sal_Int32 nNumSeriesAddresses = rSeriesAddresses.getLength(); sal_Int32 nDomainOffset = 0; sal_Int32 nNumAddrSize = nNumSeriesAddresses; uno::Reference< chart::XChartData > xChartData( xData, uno::UNO_QUERY ); if( xChartData.is()) { sal_Int32 nColumns = 0; sal_Int32 nRows = 0; sal_Int32 i, j; // set data if( nNumSeriesAddresses ) { // get NaN double fSolarNaN; ::rtl::math::setNan( &fSolarNaN ); double fNaN = fSolarNaN; fNaN = xChartData->getNotANumber(); // convert data from std::vector to uno::Sequence // ---------------------------------------------- // determine size of data std::vector< SchNumericCellRangeAddress > aNumericAddresses( nNumSeriesAddresses ); for( i = 0; i < nNumSeriesAddresses; i++ ) { if( rSeriesAddresses[ i ].DomainRangeAddresses.getLength()) { GetCellRangeAddress( rSeriesAddresses[ i ].DomainRangeAddresses[ 0 ], aNumericAddresses[ i + nDomainOffset ] ); AdjustMax( aNumericAddresses[ i + nDomainOffset ], nRows, nColumns ); nDomainOffset++; aNumericAddresses.reserve( nNumSeriesAddresses + nDomainOffset ); } GetCellRangeAddress( rSeriesAddresses[ i ].DataRangeAddress, aNumericAddresses[ i + nDomainOffset ] ); AdjustMax( aNumericAddresses[ i + nDomainOffset ], nRows, nColumns ); } nNumAddrSize += nDomainOffset; // allocate memory for sequence uno::Sequence< uno::Sequence< double > > aSequence( nRows ); for( i = 0; i < nRows; i++ ) { aSequence[ i ].realloc( nColumns ); // initialize values with NaN for( j = 0; j < nColumns; j++ ) aSequence[ i ][ j ] = fNaN; } // copy data for( i = 0; i < nNumAddrSize; i++ ) PutTableContentIntoSequence( rTable, aNumericAddresses[ i ], i, aSequence ); // set data to XChartDataArray xData->setData( aSequence ); } // set labels uno::Sequence< rtl::OUString > aColumnLabels( nNumAddrSize ); sal_Int32 nRow, nCol; for( i = 0; i < nNumSeriesAddresses; i++ ) { if( rSeriesAddresses[ i ].LabelAddress.getLength()) { GetCellAddress( rSeriesAddresses[ i ].LabelAddress, nCol, nRow ); aColumnLabels[ i + nDomainOffset ] = rTable.aData[ nRow ][ nCol ].aString; } } xData->setColumnDescriptions( aColumnLabels ); if( rCategoriesAddress.getLength()) { SchNumericCellRangeAddress aAddress; if( GetCellRangeAddress( rCategoriesAddress, aAddress )) { uno::Sequence< rtl::OUString > aLabels; if( aAddress.nCol1 == aAddress.nCol2 ) { sal_Int32 nWidth = aAddress.nRow2 - aAddress.nRow1 + 1; aLabels.realloc( nWidth ); for( i = 0; i < nWidth; i++ ) { DBG_ASSERT( rTable.aData[ aAddress.nRow1 + i ][ aAddress.nCol1 ].eType == SCH_CELL_TYPE_STRING, "expecting string" ); aLabels[ i ] = rTable.aData[ aAddress.nRow1 + i ][ aAddress.nCol1 ].aString; } } else { DBG_ASSERT( aAddress.nRow1 == aAddress.nRow2, "range must be in one row or one column" ); sal_Int32 nWidth = aAddress.nCol2 - aAddress.nCol1 + 1; aLabels.realloc( nWidth ); for( i = 0; i < nWidth; i++ ) { DBG_ASSERT( rTable.aData[ aAddress.nRow1 ][ aAddress.nCol1 + i ].eType == SCH_CELL_TYPE_STRING, "expecting string" ); aLabels[ i ] = rTable.aData[ aAddress.nRow1 ][ aAddress.nCol1 + i ].aString; } } xData->setRowDescriptions( aLabels ); } } } } } } void SchXMLTableHelper::AdjustMax( const SchNumericCellRangeAddress& rAddr, sal_Int32& nRows, sal_Int32& nColumns ) { // rows and columns are both mapped to columns ( == series ) if( rAddr.nCol1 == rAddr.nCol2 ) { if( rAddr.nRow1 > nRows ) nRows = rAddr.nRow1; if( rAddr.nRow2 > nRows ) nRows = rAddr.nRow2; if( rAddr.nCol1 > nColumns ) nColumns = rAddr.nCol1; if( rAddr.nCol2 > nColumns ) nColumns = rAddr.nCol2; } else { DBG_ASSERT( rAddr.nRow1 == rAddr.nRow2, "row indexes should be equal" ); if( rAddr.nRow1 > nColumns ) nColumns = rAddr.nRow1; if( rAddr.nRow2 > nColumns ) nColumns = rAddr.nRow2; if( rAddr.nCol1 > nRows ) nRows = rAddr.nCol1; if( rAddr.nCol2 > nRows ) nRows = rAddr.nCol2; } } void SchXMLTableHelper::GetCellAddress( const rtl::OUString& rStr, sal_Int32& rCol, sal_Int32& rRow ) { sal_Int32 nPos = rStr.indexOf( sal_Unicode( '.' )); if( nPos != -1 ) { // currently just one letter is accepted sal_Unicode aLetter = rStr.getStr()[ nPos + 1 ]; if( 'a' <= aLetter && aLetter <= 'z' ) rCol = aLetter - 'a'; else rCol = aLetter - 'A'; rRow = (rStr.copy( nPos + 2 )).toInt32() - 1; } } sal_Bool SchXMLTableHelper::GetCellRangeAddress( const rtl::OUString& rStr, SchNumericCellRangeAddress& rResult ) { sal_Int32 nBreakAt = rStr.indexOf( sal_Unicode( ':' )); if( nBreakAt != -1 ) { GetCellAddress( rStr.copy( 0, nBreakAt ), rResult.nCol1, rResult.nRow1 ); GetCellAddress( rStr.copy( nBreakAt + 1 ), rResult.nCol2, rResult.nRow2 ); return sal_True; } return sal_False; } // returns true if datarange was inside one column void SchXMLTableHelper::PutTableContentIntoSequence( const SchXMLTable& rTable, SchNumericCellRangeAddress& rAddress, sal_Int32 nSeriesIndex, uno::Sequence< uno::Sequence< double > >& aSequence ) { if( rAddress.nCol2 > rTable.nMaxColumnIndex + 1 || rAddress.nRow2 > rTable.nRowIndex + 1 ) { DBG_ERROR( "Invalid references" ); //ToDo: strip the range return; } // currently only ranges that span one row or one column are supported sal_Int32 nSeqPos = 0; uno::Sequence< double >* pSeqArray = aSequence.getArray(); double fValue; // same column if( rAddress.nCol1 == rAddress.nCol2 ) { if( rAddress.nRow1 <= rAddress.nRow2 ) { for( sal_Int32 nRow = rAddress.nRow1; nRow <= rAddress.nRow2; nRow++, nSeqPos++ ) { DBG_ASSERT( rTable.aData[ nRow ][ rAddress.nCol1 ].eType != SCH_CELL_TYPE_UNKNOWN, "trying to refer to unknown cell" ); fValue = rTable.aData[ nRow ][ rAddress.nCol1 ].fValue; if( ! ::rtl::math::isNan( fValue )) pSeqArray[ nSeqPos ][ nSeriesIndex ] = fValue; } } else // reverse { for( sal_Int32 nRow = rAddress.nRow1; nRow >= rAddress.nRow2; nRow--, nSeqPos++ ) { DBG_ASSERT( rTable.aData[ nRow ][ rAddress.nCol1 ].eType != SCH_CELL_TYPE_UNKNOWN, "trying to refer to unknown cell" ); fValue = rTable.aData[ nRow ][ rAddress.nCol1 ].fValue; if( ! ::rtl::math::isNan( fValue )) pSeqArray[ nSeqPos ][ nSeriesIndex ] = fValue; } } } else // same row { DBG_ASSERT( rAddress.nRow1 == rAddress.nRow2, "range must be in one row or one column" ); if( rAddress.nCol1 <= rAddress.nCol2 ) { for( sal_Int32 nCol = rAddress.nCol1; nCol <= rAddress.nCol2; nCol++, nSeqPos++ ) { DBG_ASSERT( rTable.aData[ rAddress.nRow1 ][ nCol ].eType != SCH_CELL_TYPE_UNKNOWN, "trying to refer to unknown cell" ); fValue = rTable.aData[ rAddress.nRow1 ][ nCol ].fValue; if( ! ::rtl::math::isNan( fValue )) pSeqArray[ nSeqPos ][ nSeriesIndex ] = fValue; } } else // reverse { for( sal_Int32 nCol = rAddress.nCol1; nCol >= rAddress.nCol2; nCol--, nSeqPos++ ) { DBG_ASSERT( rTable.aData[ rAddress.nRow1 ][ nCol ].eType != SCH_CELL_TYPE_UNKNOWN, "trying to refer to unknown cell" ); fValue = rTable.aData[ rAddress.nRow1 ][ nCol ].fValue; if( ! ::rtl::math::isNan( fValue )) pSeqArray[ nSeqPos ][ nSeriesIndex ] = fValue; } } } }