merged tag ooo/OOO330_m14
[LibreOffice.git] / chart2 / source / model / template / StockDataInterpreter.cxx
blob0d134516a1bb4259ab4b70c2a545152f3a98137c
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
31 #include "StockDataInterpreter.hxx"
32 #include "DataSeries.hxx"
33 #include "macros.hxx"
34 #include "DataSeriesHelper.hxx"
35 #include "CommonConverters.hxx"
36 #include "ContainerHelper.hxx"
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/chart2/data/XDataSink.hpp>
40 // #include <deque>
42 #include <vector>
43 #include <algorithm>
44 #include <iterator>
46 using namespace ::com::sun::star;
47 using namespace ::com::sun::star::chart2;
48 using namespace ::std;
50 using ::com::sun::star::uno::Reference;
51 using ::com::sun::star::uno::Sequence;
52 using ::rtl::OUString;
53 using namespace ::chart::ContainerHelper;
55 namespace chart
58 // explicit
59 StockDataInterpreter::StockDataInterpreter(
60 StockChartTypeTemplate::StockVariant eVariant,
61 const Reference< uno::XComponentContext > & xContext ) :
62 DataInterpreter( xContext ),
63 m_eStockVariant( eVariant )
66 StockDataInterpreter::~StockDataInterpreter()
69 StockChartTypeTemplate::StockVariant StockDataInterpreter::GetStockVariant() const
71 return m_eStockVariant;
74 // ____ XDataInterpreter ____
75 InterpretedData SAL_CALL StockDataInterpreter::interpretDataSource(
76 const Reference< data::XDataSource >& xSource,
77 const Sequence< beans::PropertyValue >& rArguments,
78 const Sequence< Reference< XDataSeries > >& rSeriesToReUse )
79 throw (uno::RuntimeException)
81 if( ! xSource.is())
82 return InterpretedData();
84 Reference< data::XLabeledDataSequence > xCategories;
85 Sequence< Reference< data::XLabeledDataSequence > > aData( xSource->getDataSequences() );
86 const sal_Int32 nDataCount( aData.getLength());
88 // sub-type properties
89 const StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
90 const bool bHasOpenValues (( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
91 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
92 const bool bHasVolume (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
93 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
94 const bool bHasCategories( HasCategories( rArguments, aData ));
96 // necessary roles for "full series"
97 // low/high/close
98 sal_Int32 nNumberOfNecessarySequences( 3 );
99 if( bHasOpenValues )
100 ++nNumberOfNecessarySequences;
101 if( bHasVolume )
102 ++nNumberOfNecessarySequences;
104 // calculate number of full series (nNumOfFullSeries) and the number of remaining
105 // sequences used for additional "incomplete series" (nRemaining)
106 sal_Int32 nNumOfFullSeries( 0 );
107 sal_Int32 nRemaining( 0 );
109 sal_Int32 nAvailableSequences( nDataCount );
110 if( bHasCategories )
111 --nAvailableSequences;
112 nNumOfFullSeries = nAvailableSequences / nNumberOfNecessarySequences;
113 nRemaining = nAvailableSequences % nNumberOfNecessarySequences;
115 sal_Int32 nCandleStickSeries = nNumOfFullSeries;
116 sal_Int32 nVolumeSeries = nNumOfFullSeries;
118 sal_Int32 nNumberOfGroups( bHasVolume ? 2 : 1 );
119 // sequences of data::XLabeledDataSequence per series per group
120 Sequence< Sequence< Sequence< Reference< data::XLabeledDataSequence > > > > aSequences( nNumberOfGroups );
121 sal_Int32 nBarGroupIndex( 0 );
122 sal_Int32 nCandleStickGroupIndex( nNumberOfGroups - 1 );
124 // allocate space for labeled sequences
125 if( nRemaining > 0 )
126 ++nCandleStickSeries;
127 aSequences[nCandleStickGroupIndex].realloc( nCandleStickSeries );
128 if( bHasVolume )
130 // if there are remaining sequences, the first one is taken for
131 // additional close values, the second one is taken as volume, if volume
132 // is used
133 if( nRemaining > 1 )
134 ++nVolumeSeries;
135 aSequences[nBarGroupIndex].realloc( nVolumeSeries );
139 // create data
140 sal_Int32 nSourceIndex = 0; // index into aData sequence
142 // 1. categories
143 if( bHasCategories )
145 xCategories.set( aData[nSourceIndex] );
146 ++nSourceIndex;
149 // 2. create "full" series
150 for( sal_Int32 nLabeledSeqIdx=0; nLabeledSeqIdx<nNumOfFullSeries; ++nLabeledSeqIdx )
152 // bar
153 if( bHasVolume )
155 aSequences[nBarGroupIndex][nLabeledSeqIdx].realloc( 1 );
156 aSequences[nBarGroupIndex][nLabeledSeqIdx][0].set( aData[nSourceIndex] );
157 if( aData[nSourceIndex].is())
158 SetRole( aData[nSourceIndex]->getValues(), C2U("values-y"));
159 ++nSourceIndex;
162 sal_Int32 nSeqIdx = 0;
163 if( bHasOpenValues )
165 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 4 );
166 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
167 if( aData[nSourceIndex].is())
168 SetRole( aData[nSourceIndex]->getValues(), C2U("values-first"));
169 ++nSourceIndex, ++nSeqIdx;
171 else
172 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 3 );
174 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
175 if( aData[nSourceIndex].is())
176 SetRole( aData[nSourceIndex]->getValues(), C2U("values-min"));
177 ++nSourceIndex, ++nSeqIdx;
179 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
180 if( aData[nSourceIndex].is())
181 SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
182 ++nSourceIndex, ++nSeqIdx;
184 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
185 if( aData[nSourceIndex].is())
186 SetRole( aData[nSourceIndex]->getValues(), C2U("values-last"));
187 ++nSourceIndex, ++nSeqIdx;
190 // 3. create series with remaining sequences
191 if( bHasVolume && nRemaining > 1 )
193 OSL_ASSERT( nVolumeSeries > nNumOfFullSeries );
194 aSequences[nBarGroupIndex][nVolumeSeries - 1].realloc( 1 );
195 OSL_ASSERT( nDataCount > nSourceIndex );
196 if( aData[nSourceIndex].is())
197 SetRole( aData[nSourceIndex]->getValues(), C2U("values-y"));
198 aSequences[nBarGroupIndex][nVolumeSeries - 1][0].set( aData[nSourceIndex] );
199 ++nSourceIndex;
200 --nRemaining;
201 OSL_ENSURE( nRemaining, "additional bar should only be used if there is at least one more sequence for a candle stick" );
204 // candle-stick
205 if( nRemaining > 0 )
207 OSL_ASSERT( nCandleStickSeries > nNumOfFullSeries );
208 const sal_Int32 nSeriesIndex = nCandleStickSeries - 1;
209 aSequences[nCandleStickGroupIndex][nSeriesIndex].realloc( nRemaining );
210 OSL_ASSERT( nDataCount > nSourceIndex );
212 // 1. low
213 sal_Int32 nSeqIdx( 0 );
214 aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
215 if( aData[nSourceIndex].is())
216 SetRole( aData[nSourceIndex]->getValues(), C2U("values-min"));
217 ++nSourceIndex, ++nSeqIdx;
219 // 2. high
220 if( nSeqIdx < nRemaining )
222 aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
223 if( aData[nSourceIndex].is())
224 SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
225 ++nSourceIndex, ++nSeqIdx;
228 // 3. close
229 OSL_ENSURE( bHasOpenValues || nSeqIdx >= nRemaining, "could have created full series" );
230 if( nSeqIdx < nRemaining )
232 aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
233 if( aData[nSourceIndex].is())
234 SetRole( aData[nSourceIndex]->getValues(), C2U("values-last"));
235 ++nSourceIndex, ++nSeqIdx;
238 // 4. open
239 OSL_ENSURE( nSeqIdx >= nRemaining, "could have created full series" );
242 // create DataSeries
243 Sequence< Sequence< Reference< XDataSeries > > > aResultSeries( nNumberOfGroups );
244 sal_Int32 nGroupIndex, nReUsedSeriesIdx = 0;
245 for( nGroupIndex=0; nGroupIndex<nNumberOfGroups; ++nGroupIndex )
247 const sal_Int32 nNumSeriesData = aSequences[nGroupIndex].getLength();
248 aResultSeries[nGroupIndex].realloc( nNumSeriesData );
249 for( sal_Int32 nSeriesIdx = 0; nSeriesIdx < nNumSeriesData; ++nSeriesIdx, ++nReUsedSeriesIdx )
253 Reference< XDataSeries > xSeries;
254 if( nReUsedSeriesIdx < rSeriesToReUse.getLength())
255 xSeries.set( rSeriesToReUse[nReUsedSeriesIdx] );
256 else
257 xSeries.set( new DataSeries( GetComponentContext() ) );
258 OSL_ASSERT( xSeries.is() );
259 Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY_THROW );
260 OSL_ASSERT( xSink.is() );
261 xSink->setData( aSequences[nGroupIndex][nSeriesIdx] );
262 aResultSeries[nGroupIndex][nSeriesIdx].set( xSeries );
264 catch( uno::Exception & ex )
266 ASSERT_EXCEPTION( ex );
271 return InterpretedData( aResultSeries, xCategories );
274 // criterion: there must be two groups for stock-charts with volume and all
275 // series must have the correct number of data::XLabeledDataSequences
277 // todo: skip first criterion? (to allow easy switch from stock-chart without
278 // volume to one with volume)
279 sal_Bool SAL_CALL StockDataInterpreter::isDataCompatible(
280 const InterpretedData& aInterpretedData )
281 throw (uno::RuntimeException)
283 // high/low/close
284 sal_Int32 nNumberOfNecessarySequences = 3;
285 // open
286 StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
287 if( ( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
288 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ))
289 ++nNumberOfNecessarySequences;
290 // volume
291 bool bHasVolume = (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
292 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
294 // 1. correct number of sub-types
295 if( aInterpretedData.Series.getLength() < (bHasVolume ? 2 : 1 ))
296 return sal_False;
298 // 2. a. volume -- use default check
299 if( bHasVolume )
301 if( ! DataInterpreter::isDataCompatible(
302 InterpretedData( Sequence< Sequence< Reference< XDataSeries > > >(
303 aInterpretedData.Series.getConstArray(), 1 ),
304 aInterpretedData.Categories )))
305 return sal_False;
308 // 2. b. candlestick
310 OSL_ASSERT( aInterpretedData.Series.getLength() > (bHasVolume ? 1 : 0));
311 Sequence< Reference< XDataSeries > > aSeries( aInterpretedData.Series[(bHasVolume ? 1 : 0)] );
312 if(!aSeries.getLength())
313 return sal_False;
314 for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
318 Reference< data::XDataSource > xSrc( aSeries[i], uno::UNO_QUERY_THROW );
319 Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
320 if( aSeq.getLength() != nNumberOfNecessarySequences )
321 return sal_False;
323 catch( uno::Exception & ex )
325 ASSERT_EXCEPTION( ex );
330 // 2. c. additional series
331 // ignore
333 return sal_True;
336 InterpretedData SAL_CALL StockDataInterpreter::reinterpretDataSeries(
337 const InterpretedData& aInterpretedData )
338 throw (uno::RuntimeException)
340 // prerequisite: StockDataInterpreter::isDataCompatible() returned true
341 return aInterpretedData;
344 } // namespace chart