diff options
author | Kohei Yoshida <kohei.yoshida@suse.com> | 2012-01-04 15:45:09 -0500 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@suse.com> | 2012-01-04 21:12:23 -0500 |
commit | a2fa83d1844fde5e1a14ba813ca1087577a1434f (patch) | |
tree | cf22a21e136c66a3614b166017c601cfd953e060 | |
parent | 953b7f9e1ba665b4763fac8f83113f43b6d49b39 (diff) |
Register chart data ranges via tokens rather than string.
Doing it this way avoids having to re-generate the data ranges in
Calc A1 before passing it to the chart backend in Calc. We need this
in order to remove the silly restriction that forces us to always pass
data range strings in Calc A1 format, which is error-prone.
This is also necessary in order to fix the bug that prevents editing
data ranges of an existing chart when the formula syntax is something
other than Calc A1.
-rw-r--r-- | offapi/UnoApi_offapi.mk | 1 | ||||
-rw-r--r-- | offapi/com/sun/star/chart2/data/XSheetDataProvider.idl | 52 | ||||
-rw-r--r-- | oox/source/xls/excelchartconverter.cxx | 67 | ||||
-rw-r--r-- | sc/inc/chart2uno.hxx | 18 | ||||
-rw-r--r-- | sc/source/ui/unoobj/chart2uno.cxx | 131 |
5 files changed, 239 insertions, 30 deletions
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 0391056465a3..e44bf2375699 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -1985,6 +1985,7 @@ $(eval $(call gb_UnoApiTarget_add_idlfiles,offapi,offapi/com/sun/star/chart2/dat XNumericalDataSequence \ XRangeHighlighter \ XRangeXMLConversion \ + XSheetDataProvider \ XTextualDataSequence \ )) $(eval $(call gb_UnoApiTarget_add_idlfiles,offapi,offapi/com/sun/star/configuration,\ diff --git a/offapi/com/sun/star/chart2/data/XSheetDataProvider.idl b/offapi/com/sun/star/chart2/data/XSheetDataProvider.idl new file mode 100644 index 000000000000..db443c9edb70 --- /dev/null +++ b/offapi/com/sun/star/chart2/data/XSheetDataProvider.idl @@ -0,0 +1,52 @@ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Kohei Yoshida <kohei.yoshida@suse.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#ifndef com_sun_star_chart2_data_XSheetDataProvider_idl +#define com_sun_star_chart2_data_XSheetDataProvider_idl + +#include <com/sun/star/uno/XInterface.idl> +#include <com/sun/star/chart2/data/XDataSequence.idl> +#include <com/sun/star/sheet/FormulaToken.idl> + +module com { module sun { module star { module chart2 { module data { + +/** + * Interface specific to spreadsheet data provider backend. + */ +interface XSheetDataProvider : com::sun::star::uno::XInterface +{ + boolean createDataSequenceByFormulaTokensPossible( + [in] sequence< com::sun::star::sheet::FormulaToken > aTokens ); + + XDataSequence createDataSequenceByFormulaTokens( + [in] sequence< com::sun::star::sheet::FormulaToken > aTokens ) + raises( com::sun::star::lang::IllegalArgumentException ); +}; + +};};};};}; + +#endif diff --git a/oox/source/xls/excelchartconverter.cxx b/oox/source/xls/excelchartconverter.cxx index 5c63ee5ee701..2fb699dd46d3 100644 --- a/oox/source/xls/excelchartconverter.cxx +++ b/oox/source/xls/excelchartconverter.cxx @@ -31,6 +31,8 @@ #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/chart2/data/XDataProvider.hpp> #include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/chart2/data/XSheetDataProvider.hpp> + #include "oox/core/filterbase.hxx" #include "oox/drawingml/chart/datasourcemodel.hxx" #include "oox/helper/containerhelper.hxx" @@ -79,42 +81,53 @@ Reference< XDataSequence > ExcelChartConverter::createDataSequence( const Reference< XDataProvider >& rxDataProvider, const DataSequenceModel& rDataSeq ) { Reference< XDataSequence > xDataSeq; - if( rxDataProvider.is() ) + if (!rxDataProvider.is()) + return xDataSeq; + + Reference<XSheetDataProvider> xSheetProvider(rxDataProvider, UNO_QUERY); + if (!xSheetProvider.is()) + return xDataSeq; + + if (!rDataSeq.maFormula.isEmpty()) { - OUString aRangeRep; - if( rDataSeq.maFormula.getLength() > 0 ) - { - // parse the formula string, create a token sequence - FormulaParser& rParser = getFormulaParser(); - CellAddress aBaseAddr( getCurrentSheetIndex(), 0, 0 ); - ApiTokenSequence aTokens = rParser.importFormula( aBaseAddr, rDataSeq.maFormula ); - - // create a range list from the token sequence - ApiCellRangeList aRanges; - rParser.extractCellRangeList( aRanges, aTokens, false ); - aRangeRep = rParser.generateApiRangeListString( aRanges ); - } - else if( !rDataSeq.maData.empty() ) - { - // create a single-row array from constant source data - Matrix< Any > aMatrix( rDataSeq.maData.size(), 1 ); - Matrix< Any >::iterator aMIt = aMatrix.begin(); - // TODO: how to handle missing values in the map? - for( DataSequenceModel::AnyMap::const_iterator aDIt = rDataSeq.maData.begin(), aDEnd = rDataSeq.maData.end(); aDIt != aDEnd; ++aDIt, ++aMIt ) - *aMIt = aDIt->second; - aRangeRep = FormulaProcessorBase::generateApiArray( aMatrix ); - } + // parse the formula string, create a token sequence + FormulaParser& rParser = getFormulaParser(); + CellAddress aBaseAddr( getCurrentSheetIndex(), 0, 0 ); + ApiTokenSequence aTokens = rParser.importFormula( aBaseAddr, rDataSeq.maFormula ); - if( aRangeRep.getLength() > 0 ) try + try { // create the data sequence - xDataSeq = rxDataProvider->createDataSequenceByRangeRepresentation( aRangeRep ); + xDataSeq = xSheetProvider->createDataSequenceByFormulaTokens(aTokens); } - catch( Exception& ) + catch (Exception&) { OSL_FAIL( "ExcelChartConverter::createDataSequence - cannot create data sequence" ); } } + else if (!rDataSeq.maData.empty()) + { + // create a single-row array from constant source data + Matrix< Any > aMatrix( rDataSeq.maData.size(), 1 ); + Matrix< Any >::iterator aMIt = aMatrix.begin(); + // TODO: how to handle missing values in the map? + for( DataSequenceModel::AnyMap::const_iterator aDIt = rDataSeq.maData.begin(), aDEnd = rDataSeq.maData.end(); aDIt != aDEnd; ++aDIt, ++aMIt ) + *aMIt = aDIt->second; + OUString aRangeRep = FormulaProcessorBase::generateApiArray( aMatrix ); + + if (!aRangeRep.isEmpty()) + { + try + { + // create the data sequence + xDataSeq = rxDataProvider->createDataSequenceByRangeRepresentation( aRangeRep ); + } + catch (Exception&) + { + OSL_FAIL( "ExcelChartConverter::createDataSequence - cannot create data sequence" ); + } + } + } return xDataSeq; } diff --git a/sc/inc/chart2uno.hxx b/sc/inc/chart2uno.hxx index 544ab9b09a32..86b14b2c4b0c 100644 --- a/sc/inc/chart2uno.hxx +++ b/sc/inc/chart2uno.hxx @@ -38,6 +38,7 @@ #include <svl/lstner.hxx> #include <com/sun/star/chart/ChartDataRowSource.hpp> #include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/chart2/data/XSheetDataProvider.hpp> #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp> #include <com/sun/star/chart2/data/XDataSource.hpp> #include <com/sun/star/chart2/data/XDataSequence.hpp> @@ -50,8 +51,7 @@ #include <com/sun/star/util/XCloneable.hpp> #include <com/sun/star/util/XModifyBroadcaster.hpp> #include <cppuhelper/implbase2.hxx> -#include <cppuhelper/implbase4.hxx> -#include <cppuhelper/implbase6.hxx> +#include <cppuhelper/implbase5.hxx> #include <cppuhelper/implbase7.hxx> #include <rtl/ustring.hxx> #include <svl/itemprop.hxx> @@ -69,8 +69,9 @@ class ScDocument; // DataProvider ============================================================== class ScChart2DataProvider : public - ::cppu::WeakImplHelper4< + ::cppu::WeakImplHelper5< ::com::sun::star::chart2::data::XDataProvider, + ::com::sun::star::chart2::data::XSheetDataProvider, ::com::sun::star::chart2::data::XRangeXMLConversion, ::com::sun::star::beans::XPropertySet, ::com::sun::star::lang::XServiceInfo>, @@ -110,6 +111,17 @@ public: virtual ::com::sun::star::uno::Reference< ::com::sun::star::sheet::XRangeSelection > SAL_CALL getRangeSelection() throw (::com::sun::star::uno::RuntimeException); + // XSheetDataProvider ---------------------------------------------------- + + virtual sal_Bool SAL_CALL createDataSequenceByFormulaTokensPossible( + const ::com::sun::star::uno::Sequence< ::com::sun::star::sheet::FormulaToken >& aTokens ) + throw (::com::sun::star::uno::RuntimeException); + + virtual ::com::sun::star::uno::Reference< ::com::sun::star::chart2::data::XDataSequence > + SAL_CALL createDataSequenceByFormulaTokens( + const ::com::sun::star::uno::Sequence< ::com::sun::star::sheet::FormulaToken >& aTokens ) + throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + // XRangeXMLConversion --------------------------------------------------- virtual ::rtl::OUString SAL_CALL convertRangeToXML( const ::rtl::OUString& sRangeRepresentation ) diff --git a/sc/source/ui/unoobj/chart2uno.cxx b/sc/source/ui/unoobj/chart2uno.cxx index 9aabfffd75a9..dcdffbe13538 100644 --- a/sc/source/ui/unoobj/chart2uno.cxx +++ b/sc/source/ui/unoobj/chart2uno.cxx @@ -42,6 +42,9 @@ #include "reftokenhelper.hxx" #include "chartlis.hxx" #include "stlalgorithm.hxx" +#include "tokenuno.hxx" + +#include "formula/opcode.hxx" #include <sfx2/objsh.hxx> #include <tools/table.hxx> @@ -2108,6 +2111,134 @@ uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::getRange return xResult; } +sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByFormulaTokensPossible( + const Sequence<sheet::FormulaToken>& aTokens ) + throw (uno::RuntimeException) +{ + if (aTokens.getLength() <= 0) + return false; + + ScTokenArray aCode; + if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens)) + return false; + + sal_uInt16 n = aCode.GetLen(); + if (!n) + return false; + + const formula::FormulaToken* pFirst = aCode.First(); + const formula::FormulaToken* pLast = aCode.GetArray()[n-1]; + for (const formula::FormulaToken* p = aCode.First(); p; p = aCode.Next()) + { + switch (p->GetType()) + { + case svSep: + { + switch (p->GetOpCode()) + { + case ocSep: + // separators are allowed. + break; + case ocOpen: + if (p != pFirst) + // open paran is allowed only as the first token. + return false; + break; + case ocClose: + if (p != pLast) + // close paren is allowed only as the last token. + return false; + break; + default: + return false; + } + } + break; + case svSingleRef: + case svDoubleRef: + case svExternalSingleRef: + case svExternalDoubleRef: + break; + default: + return false; + } + } + + return true; +} + +Reference<chart2::data::XDataSequence> SAL_CALL +ScChart2DataProvider::createDataSequenceByFormulaTokens( + const Sequence<sheet::FormulaToken>& aTokens ) + throw (lang::IllegalArgumentException, uno::RuntimeException) +{ + Reference<chart2::data::XDataSequence> xResult; + if (aTokens.getLength() <= 0) + return xResult; + + ScTokenArray aCode; + if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens)) + return xResult; + + sal_uInt16 n = aCode.GetLen(); + if (!n) + return xResult; + + vector<ScTokenRef> aRefTokens; + const formula::FormulaToken* pFirst = aCode.First(); + const formula::FormulaToken* pLast = aCode.GetArray()[n-1]; + for (const formula::FormulaToken* p = aCode.First(); p; p = aCode.Next()) + { + switch (p->GetType()) + { + case svSep: + { + switch (p->GetOpCode()) + { + case ocSep: + // separators are allowed. + break; + case ocOpen: + if (p != pFirst) + // open paran is allowed only as the first token. + throw lang::IllegalArgumentException(); + break; + case ocClose: + if (p != pLast) + // close paren is allowed only as the last token. + throw lang::IllegalArgumentException(); + break; + default: + throw lang::IllegalArgumentException(); + } + } + break; + case svSingleRef: + case svDoubleRef: + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScTokenRef pNew(static_cast<const ScToken*>(p->Clone())); + aRefTokens.push_back(pNew); + } + break; + default: + throw lang::IllegalArgumentException(); + } + } + + if (aRefTokens.empty()) + return xResult; + + shrinkToDataRange(m_pDocument, aRefTokens); + + // ScChart2DataSequence manages the life cycle of pRefTokens. + vector<ScTokenRef>* pRefTokens = new vector<ScTokenRef>(); + pRefTokens->swap(aRefTokens); + xResult.set(new ScChart2DataSequence(m_pDocument, this, pRefTokens, m_bIncludeHiddenCells)); + return xResult; +} + // XRangeXMLConversion --------------------------------------------------- rtl::OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const rtl::OUString& sRangeRepresentation ) |