Bump for 3.6-28
[LibreOffice.git] / chart2 / source / model / template / StockDataInterpreter.cxx
blob9609710e2406431d8dbddb617f586e1c48b99a1a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include "StockDataInterpreter.hxx"
31 #include "DataSeries.hxx"
32 #include "macros.hxx"
33 #include "DataSeriesHelper.hxx"
34 #include "CommonConverters.hxx"
35 #include "ContainerHelper.hxx"
36 #include <com/sun/star/beans/XPropertySet.hpp>
37 #include <com/sun/star/chart2/data/XDataSink.hpp>
39 #include <vector>
40 #include <algorithm>
41 #include <iterator>
43 using namespace ::com::sun::star;
44 using namespace ::com::sun::star::chart2;
45 using namespace ::std;
47 using ::com::sun::star::uno::Reference;
48 using ::com::sun::star::uno::Sequence;
49 using ::rtl::OUString;
50 using namespace ::chart::ContainerHelper;
52 namespace chart
55 // explicit
56 StockDataInterpreter::StockDataInterpreter(
57 StockChartTypeTemplate::StockVariant eVariant,
58 const Reference< uno::XComponentContext > & xContext ) :
59 DataInterpreter( xContext ),
60 m_eStockVariant( eVariant )
63 StockDataInterpreter::~StockDataInterpreter()
66 StockChartTypeTemplate::StockVariant StockDataInterpreter::GetStockVariant() const
68 return m_eStockVariant;
71 // ____ XDataInterpreter ____
72 InterpretedData SAL_CALL StockDataInterpreter::interpretDataSource(
73 const Reference< data::XDataSource >& xSource,
74 const Sequence< beans::PropertyValue >& rArguments,
75 const Sequence< Reference< XDataSeries > >& rSeriesToReUse )
76 throw (uno::RuntimeException)
78 if( ! xSource.is())
79 return InterpretedData();
81 Reference< data::XLabeledDataSequence > xCategories;
82 Sequence< Reference< data::XLabeledDataSequence > > aData( xSource->getDataSequences() );
83 const sal_Int32 nDataCount( aData.getLength());
85 // sub-type properties
86 const StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
87 const bool bHasOpenValues (( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
88 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
89 const bool bHasVolume (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
90 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
91 const bool bHasCategories( HasCategories( rArguments, aData ));
93 // necessary roles for "full series"
94 // low/high/close
95 sal_Int32 nNumberOfNecessarySequences( 3 );
96 if( bHasOpenValues )
97 ++nNumberOfNecessarySequences;
98 if( bHasVolume )
99 ++nNumberOfNecessarySequences;
101 // calculate number of full series (nNumOfFullSeries) and the number of remaining
102 // sequences used for additional "incomplete series" (nRemaining)
103 sal_Int32 nNumOfFullSeries( 0 );
104 sal_Int32 nRemaining( 0 );
106 sal_Int32 nAvailableSequences( nDataCount );
107 if( bHasCategories )
108 --nAvailableSequences;
109 nNumOfFullSeries = nAvailableSequences / nNumberOfNecessarySequences;
110 nRemaining = nAvailableSequences % nNumberOfNecessarySequences;
112 sal_Int32 nCandleStickSeries = nNumOfFullSeries;
113 sal_Int32 nVolumeSeries = nNumOfFullSeries;
115 sal_Int32 nNumberOfGroups( bHasVolume ? 2 : 1 );
116 // sequences of data::XLabeledDataSequence per series per group
117 Sequence< Sequence< Sequence< Reference< data::XLabeledDataSequence > > > > aSequences( nNumberOfGroups );
118 sal_Int32 nBarGroupIndex( 0 );
119 sal_Int32 nCandleStickGroupIndex( nNumberOfGroups - 1 );
121 // allocate space for labeled sequences
122 if( nRemaining > 0 )
123 ++nCandleStickSeries;
124 aSequences[nCandleStickGroupIndex].realloc( nCandleStickSeries );
125 if( bHasVolume )
127 // if there are remaining sequences, the first one is taken for
128 // additional close values, the second one is taken as volume, if volume
129 // is used
130 if( nRemaining > 1 )
131 ++nVolumeSeries;
132 aSequences[nBarGroupIndex].realloc( nVolumeSeries );
136 // create data
137 sal_Int32 nSourceIndex = 0; // index into aData sequence
139 // 1. categories
140 if( bHasCategories )
142 xCategories.set( aData[nSourceIndex] );
143 ++nSourceIndex;
146 // 2. create "full" series
147 for( sal_Int32 nLabeledSeqIdx=0; nLabeledSeqIdx<nNumOfFullSeries; ++nLabeledSeqIdx )
149 // bar
150 if( bHasVolume )
152 aSequences[nBarGroupIndex][nLabeledSeqIdx].realloc( 1 );
153 aSequences[nBarGroupIndex][nLabeledSeqIdx][0].set( aData[nSourceIndex] );
154 if( aData[nSourceIndex].is())
155 SetRole( aData[nSourceIndex]->getValues(), C2U("values-y"));
156 ++nSourceIndex;
159 sal_Int32 nSeqIdx = 0;
160 if( bHasOpenValues )
162 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 4 );
163 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
164 if( aData[nSourceIndex].is())
165 SetRole( aData[nSourceIndex]->getValues(), C2U("values-first"));
166 ++nSourceIndex, ++nSeqIdx;
168 else
169 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 3 );
171 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
172 if( aData[nSourceIndex].is())
173 SetRole( aData[nSourceIndex]->getValues(), C2U("values-min"));
174 ++nSourceIndex, ++nSeqIdx;
176 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
177 if( aData[nSourceIndex].is())
178 SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
179 ++nSourceIndex, ++nSeqIdx;
181 aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
182 if( aData[nSourceIndex].is())
183 SetRole( aData[nSourceIndex]->getValues(), C2U("values-last"));
184 ++nSourceIndex, ++nSeqIdx;
187 // 3. create series with remaining sequences
188 if( bHasVolume && nRemaining > 1 )
190 OSL_ASSERT( nVolumeSeries > nNumOfFullSeries );
191 aSequences[nBarGroupIndex][nVolumeSeries - 1].realloc( 1 );
192 OSL_ASSERT( nDataCount > nSourceIndex );
193 if( aData[nSourceIndex].is())
194 SetRole( aData[nSourceIndex]->getValues(), C2U("values-y"));
195 aSequences[nBarGroupIndex][nVolumeSeries - 1][0].set( aData[nSourceIndex] );
196 ++nSourceIndex;
197 --nRemaining;
198 OSL_ENSURE( nRemaining, "additional bar should only be used if there is at least one more sequence for a candle stick" );
201 // candle-stick
202 if( nRemaining > 0 )
204 OSL_ASSERT( nCandleStickSeries > nNumOfFullSeries );
205 const sal_Int32 nSeriesIndex = nCandleStickSeries - 1;
206 aSequences[nCandleStickGroupIndex][nSeriesIndex].realloc( nRemaining );
207 OSL_ASSERT( nDataCount > nSourceIndex );
209 // 1. low
210 sal_Int32 nSeqIdx( 0 );
211 aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
212 if( aData[nSourceIndex].is())
213 SetRole( aData[nSourceIndex]->getValues(), C2U("values-min"));
214 ++nSourceIndex, ++nSeqIdx;
216 // 2. high
217 if( nSeqIdx < nRemaining )
219 aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
220 if( aData[nSourceIndex].is())
221 SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
222 ++nSourceIndex, ++nSeqIdx;
225 // 3. close
226 OSL_ENSURE( bHasOpenValues || nSeqIdx >= nRemaining, "could have created full series" );
227 if( nSeqIdx < nRemaining )
229 aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
230 if( aData[nSourceIndex].is())
231 SetRole( aData[nSourceIndex]->getValues(), C2U("values-last"));
232 ++nSourceIndex, ++nSeqIdx;
235 // 4. open
236 OSL_ENSURE( nSeqIdx >= nRemaining, "could have created full series" );
239 // create DataSeries
240 Sequence< Sequence< Reference< XDataSeries > > > aResultSeries( nNumberOfGroups );
241 sal_Int32 nGroupIndex, nReUsedSeriesIdx = 0;
242 for( nGroupIndex=0; nGroupIndex<nNumberOfGroups; ++nGroupIndex )
244 const sal_Int32 nNumSeriesData = aSequences[nGroupIndex].getLength();
245 aResultSeries[nGroupIndex].realloc( nNumSeriesData );
246 for( sal_Int32 nSeriesIdx = 0; nSeriesIdx < nNumSeriesData; ++nSeriesIdx, ++nReUsedSeriesIdx )
250 Reference< XDataSeries > xSeries;
251 if( nReUsedSeriesIdx < rSeriesToReUse.getLength())
252 xSeries.set( rSeriesToReUse[nReUsedSeriesIdx] );
253 else
254 xSeries.set( new DataSeries( GetComponentContext() ) );
255 OSL_ASSERT( xSeries.is() );
256 Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY_THROW );
257 OSL_ASSERT( xSink.is() );
258 xSink->setData( aSequences[nGroupIndex][nSeriesIdx] );
259 aResultSeries[nGroupIndex][nSeriesIdx].set( xSeries );
261 catch( const uno::Exception & ex )
263 ASSERT_EXCEPTION( ex );
268 return InterpretedData( aResultSeries, xCategories );
271 // criterion: there must be two groups for stock-charts with volume and all
272 // series must have the correct number of data::XLabeledDataSequences
274 // todo: skip first criterion? (to allow easy switch from stock-chart without
275 // volume to one with volume)
276 sal_Bool SAL_CALL StockDataInterpreter::isDataCompatible(
277 const InterpretedData& aInterpretedData )
278 throw (uno::RuntimeException)
280 // high/low/close
281 sal_Int32 nNumberOfNecessarySequences = 3;
282 // open
283 StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
284 if( ( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
285 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ))
286 ++nNumberOfNecessarySequences;
287 // volume
288 bool bHasVolume = (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
289 ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
291 // 1. correct number of sub-types
292 if( aInterpretedData.Series.getLength() < (bHasVolume ? 2 : 1 ))
293 return sal_False;
295 // 2. a. volume -- use default check
296 if( bHasVolume )
298 if( ! DataInterpreter::isDataCompatible(
299 InterpretedData( Sequence< Sequence< Reference< XDataSeries > > >(
300 aInterpretedData.Series.getConstArray(), 1 ),
301 aInterpretedData.Categories )))
302 return sal_False;
305 // 2. b. candlestick
307 OSL_ASSERT( aInterpretedData.Series.getLength() > (bHasVolume ? 1 : 0));
308 Sequence< Reference< XDataSeries > > aSeries( aInterpretedData.Series[(bHasVolume ? 1 : 0)] );
309 if(!aSeries.getLength())
310 return sal_False;
311 for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
315 Reference< data::XDataSource > xSrc( aSeries[i], uno::UNO_QUERY_THROW );
316 Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
317 if( aSeq.getLength() != nNumberOfNecessarySequences )
318 return sal_False;
320 catch( const uno::Exception & ex )
322 ASSERT_EXCEPTION( ex );
327 // 2. c. additional series
328 // ignore
330 return sal_True;
333 InterpretedData SAL_CALL StockDataInterpreter::reinterpretDataSeries(
334 const InterpretedData& aInterpretedData )
335 throw (uno::RuntimeException)
337 // prerequisite: StockDataInterpreter::isDataCompatible() returned true
338 return aInterpretedData;
341 } // namespace chart
343 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */