Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
5 : : * This Source Code Form is subject to the terms of the Mozilla Public
6 : : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
11 : : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : : * contributor license agreements. See the NOTICE file distributed
13 : : * with this work for additional information regarding copyright
14 : : * ownership. The ASF licenses this file to you under the Apache
15 : : * License, Version 2.0 (the "License"); you may not use this file
16 : : * except in compliance with the License. You may obtain a copy of
17 : : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : : */
19 : :
20 : :
21 : : #include "StockDataInterpreter.hxx"
22 : : #include "DataSeries.hxx"
23 : : #include "macros.hxx"
24 : : #include "DataSeriesHelper.hxx"
25 : : #include "CommonConverters.hxx"
26 : : #include "ContainerHelper.hxx"
27 : : #include <com/sun/star/beans/XPropertySet.hpp>
28 : : #include <com/sun/star/chart2/data/XDataSink.hpp>
29 : :
30 : : #include <vector>
31 : : #include <algorithm>
32 : : #include <iterator>
33 : :
34 : : using namespace ::com::sun::star;
35 : : using namespace ::com::sun::star::chart2;
36 : : using namespace ::std;
37 : :
38 : : using ::com::sun::star::uno::Reference;
39 : : using ::com::sun::star::uno::Sequence;
40 : : using ::rtl::OUString;
41 : : using namespace ::chart::ContainerHelper;
42 : :
43 : : namespace chart
44 : : {
45 : :
46 : : // explicit
47 : 4 : StockDataInterpreter::StockDataInterpreter(
48 : : StockChartTypeTemplate::StockVariant eVariant,
49 : : const Reference< uno::XComponentContext > & xContext ) :
50 : : DataInterpreter( xContext ),
51 : 4 : m_eStockVariant( eVariant )
52 : 4 : {}
53 : :
54 : 4 : StockDataInterpreter::~StockDataInterpreter()
55 [ - + ]: 8 : {}
56 : :
57 : 8 : StockChartTypeTemplate::StockVariant StockDataInterpreter::GetStockVariant() const
58 : : {
59 : 8 : return m_eStockVariant;
60 : : }
61 : :
62 : : // ____ XDataInterpreter ____
63 : 4 : InterpretedData SAL_CALL StockDataInterpreter::interpretDataSource(
64 : : const Reference< data::XDataSource >& xSource,
65 : : const Sequence< beans::PropertyValue >& rArguments,
66 : : const Sequence< Reference< XDataSeries > >& rSeriesToReUse )
67 : : throw (uno::RuntimeException)
68 : : {
69 [ - + ]: 4 : if( ! xSource.is())
70 [ # # ]: 0 : return InterpretedData();
71 : :
72 : 4 : Reference< data::XLabeledDataSequence > xCategories;
73 [ + - ][ + - ]: 4 : Sequence< Reference< data::XLabeledDataSequence > > aData( xSource->getDataSequences() );
74 : 4 : const sal_Int32 nDataCount( aData.getLength());
75 : :
76 : : // sub-type properties
77 : 4 : const StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
78 : : const bool bHasOpenValues (( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
79 [ - + ][ + - ]: 4 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
80 : : const bool bHasVolume (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
81 [ + - ][ - + ]: 4 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
82 [ + - ]: 4 : const bool bHasCategories( HasCategories( rArguments, aData ));
83 : :
84 : : // necessary roles for "full series"
85 : : // low/high/close
86 : 4 : sal_Int32 nNumberOfNecessarySequences( 3 );
87 [ - + ]: 4 : if( bHasOpenValues )
88 : 0 : ++nNumberOfNecessarySequences;
89 [ - + ]: 4 : if( bHasVolume )
90 : 0 : ++nNumberOfNecessarySequences;
91 : :
92 : : // calculate number of full series (nNumOfFullSeries) and the number of remaining
93 : : // sequences used for additional "incomplete series" (nRemaining)
94 : 4 : sal_Int32 nNumOfFullSeries( 0 );
95 : 4 : sal_Int32 nRemaining( 0 );
96 : : {
97 : 4 : sal_Int32 nAvailableSequences( nDataCount );
98 [ + - ]: 4 : if( bHasCategories )
99 : 4 : --nAvailableSequences;
100 : 4 : nNumOfFullSeries = nAvailableSequences / nNumberOfNecessarySequences;
101 : 4 : nRemaining = nAvailableSequences % nNumberOfNecessarySequences;
102 : : }
103 : 4 : sal_Int32 nCandleStickSeries = nNumOfFullSeries;
104 : 4 : sal_Int32 nVolumeSeries = nNumOfFullSeries;
105 : :
106 [ - + ]: 4 : sal_Int32 nNumberOfGroups( bHasVolume ? 2 : 1 );
107 : : // sequences of data::XLabeledDataSequence per series per group
108 [ + - ]: 4 : Sequence< Sequence< Sequence< Reference< data::XLabeledDataSequence > > > > aSequences( nNumberOfGroups );
109 : 4 : sal_Int32 nBarGroupIndex( 0 );
110 : 4 : sal_Int32 nCandleStickGroupIndex( nNumberOfGroups - 1 );
111 : :
112 : : // allocate space for labeled sequences
113 [ + - ]: 4 : if( nRemaining > 0 )
114 : 4 : ++nCandleStickSeries;
115 [ + - ][ + - ]: 4 : aSequences[nCandleStickGroupIndex].realloc( nCandleStickSeries );
116 [ - + ]: 4 : if( bHasVolume )
117 : : {
118 : : // if there are remaining sequences, the first one is taken for
119 : : // additional close values, the second one is taken as volume, if volume
120 : : // is used
121 [ # # ]: 0 : if( nRemaining > 1 )
122 : 0 : ++nVolumeSeries;
123 [ # # ][ # # ]: 0 : aSequences[nBarGroupIndex].realloc( nVolumeSeries );
124 : : }
125 : :
126 : :
127 : : // create data
128 : 4 : sal_Int32 nSourceIndex = 0; // index into aData sequence
129 : :
130 : : // 1. categories
131 [ + - ]: 4 : if( bHasCategories )
132 : : {
133 [ + - ][ + - ]: 4 : xCategories.set( aData[nSourceIndex] );
134 : 4 : ++nSourceIndex;
135 : : }
136 : :
137 : : // 2. create "full" series
138 [ + + ]: 20 : for( sal_Int32 nLabeledSeqIdx=0; nLabeledSeqIdx<nNumOfFullSeries; ++nLabeledSeqIdx )
139 : : {
140 : : // bar
141 [ - + ]: 16 : if( bHasVolume )
142 : : {
143 [ # # ][ # # ]: 0 : aSequences[nBarGroupIndex][nLabeledSeqIdx].realloc( 1 );
[ # # ]
144 [ # # ][ # # ]: 0 : aSequences[nBarGroupIndex][nLabeledSeqIdx][0].set( aData[nSourceIndex] );
[ # # ][ # # ]
[ # # ]
145 [ # # ][ # # ]: 0 : if( aData[nSourceIndex].is())
146 [ # # ][ # # ]: 0 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-y"));
[ # # ][ # # ]
[ # # ]
147 : 0 : ++nSourceIndex;
148 : : }
149 : :
150 : 16 : sal_Int32 nSeqIdx = 0;
151 [ - + ]: 16 : if( bHasOpenValues )
152 : : {
153 [ # # ][ # # ]: 0 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 4 );
[ # # ]
154 [ # # ][ # # ]: 0 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
[ # # ][ # # ]
[ # # ]
155 [ # # ][ # # ]: 0 : if( aData[nSourceIndex].is())
156 [ # # ][ # # ]: 0 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-first"));
[ # # ][ # # ]
[ # # ]
157 : 0 : ++nSourceIndex, ++nSeqIdx;
158 : : }
159 : : else
160 [ + - ][ + - ]: 16 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 3 );
[ + - ]
161 : :
162 [ + - ][ + - ]: 16 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
[ + - ][ + - ]
[ + - ]
163 [ + - ][ + - ]: 16 : if( aData[nSourceIndex].is())
164 [ + - ][ + - ]: 16 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-min"));
[ + - ][ + - ]
[ + - ]
165 : 16 : ++nSourceIndex, ++nSeqIdx;
166 : :
167 [ + - ][ + - ]: 16 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
[ + - ][ + - ]
[ + - ]
168 [ + - ][ + - ]: 16 : if( aData[nSourceIndex].is())
169 [ + - ][ + - ]: 16 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
[ + - ][ + - ]
[ + - ]
170 : 16 : ++nSourceIndex, ++nSeqIdx;
171 : :
172 [ + - ][ + - ]: 16 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
[ + - ][ + - ]
[ + - ]
173 [ + - ][ + - ]: 16 : if( aData[nSourceIndex].is())
174 [ + - ][ + - ]: 16 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-last"));
[ + - ][ + - ]
[ + - ]
175 : 16 : ++nSourceIndex, ++nSeqIdx;
176 : : }
177 : :
178 : : // 3. create series with remaining sequences
179 [ - + ][ # # ]: 4 : if( bHasVolume && nRemaining > 1 )
180 : : {
181 : : OSL_ASSERT( nVolumeSeries > nNumOfFullSeries );
182 [ # # ][ # # ]: 0 : aSequences[nBarGroupIndex][nVolumeSeries - 1].realloc( 1 );
[ # # ]
183 : : OSL_ASSERT( nDataCount > nSourceIndex );
184 [ # # ][ # # ]: 0 : if( aData[nSourceIndex].is())
185 [ # # ][ # # ]: 0 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-y"));
[ # # ][ # # ]
[ # # ]
186 [ # # ][ # # ]: 0 : aSequences[nBarGroupIndex][nVolumeSeries - 1][0].set( aData[nSourceIndex] );
[ # # ][ # # ]
[ # # ]
187 : 0 : ++nSourceIndex;
188 : 0 : --nRemaining;
189 : : OSL_ENSURE( nRemaining, "additional bar should only be used if there is at least one more sequence for a candle stick" );
190 : : }
191 : :
192 : : // candle-stick
193 [ + - ]: 4 : if( nRemaining > 0 )
194 : : {
195 : : OSL_ASSERT( nCandleStickSeries > nNumOfFullSeries );
196 : 4 : const sal_Int32 nSeriesIndex = nCandleStickSeries - 1;
197 [ + - ][ + - ]: 4 : aSequences[nCandleStickGroupIndex][nSeriesIndex].realloc( nRemaining );
[ + - ]
198 : : OSL_ASSERT( nDataCount > nSourceIndex );
199 : :
200 : : // 1. low
201 : 4 : sal_Int32 nSeqIdx( 0 );
202 [ + - ][ + - ]: 4 : aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
[ + - ][ + - ]
[ + - ]
203 [ + - ][ + - ]: 4 : if( aData[nSourceIndex].is())
204 [ + - ][ + - ]: 4 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-min"));
[ + - ][ + - ]
[ + - ]
205 : 4 : ++nSourceIndex, ++nSeqIdx;
206 : :
207 : : // 2. high
208 [ - + ]: 4 : if( nSeqIdx < nRemaining )
209 : : {
210 [ # # ][ # # ]: 0 : aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
[ # # ][ # # ]
[ # # ]
211 [ # # ][ # # ]: 0 : if( aData[nSourceIndex].is())
212 [ # # ][ # # ]: 0 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
[ # # ][ # # ]
[ # # ]
213 : 0 : ++nSourceIndex, ++nSeqIdx;
214 : : }
215 : :
216 : : // 3. close
217 : : OSL_ENSURE( bHasOpenValues || nSeqIdx >= nRemaining, "could have created full series" );
218 [ - + ]: 4 : if( nSeqIdx < nRemaining )
219 : : {
220 [ # # ][ # # ]: 0 : aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
[ # # ][ # # ]
[ # # ]
221 [ # # ][ # # ]: 0 : if( aData[nSourceIndex].is())
222 [ # # ][ # # ]: 0 : SetRole( aData[nSourceIndex]->getValues(), C2U("values-last"));
[ # # ][ # # ]
[ # # ]
223 : 0 : ++nSourceIndex, ++nSeqIdx;
224 : : }
225 : :
226 : : // 4. open
227 : : OSL_ENSURE( nSeqIdx >= nRemaining, "could have created full series" );
228 : : }
229 : :
230 : : // create DataSeries
231 [ + - ]: 4 : Sequence< Sequence< Reference< XDataSeries > > > aResultSeries( nNumberOfGroups );
232 : 4 : sal_Int32 nGroupIndex, nReUsedSeriesIdx = 0;
233 [ + + ]: 8 : for( nGroupIndex=0; nGroupIndex<nNumberOfGroups; ++nGroupIndex )
234 : : {
235 [ + - ]: 4 : const sal_Int32 nNumSeriesData = aSequences[nGroupIndex].getLength();
236 [ + - ][ + - ]: 4 : aResultSeries[nGroupIndex].realloc( nNumSeriesData );
237 [ + + ]: 24 : for( sal_Int32 nSeriesIdx = 0; nSeriesIdx < nNumSeriesData; ++nSeriesIdx, ++nReUsedSeriesIdx )
238 : : {
239 : : try
240 : : {
241 : 20 : Reference< XDataSeries > xSeries;
242 [ + - ]: 20 : if( nReUsedSeriesIdx < rSeriesToReUse.getLength())
243 [ + - ]: 20 : xSeries.set( rSeriesToReUse[nReUsedSeriesIdx] );
244 : : else
245 [ # # ][ # # ]: 0 : xSeries.set( new DataSeries( GetComponentContext() ) );
[ # # ][ # # ]
246 : : OSL_ASSERT( xSeries.is() );
247 [ + - ]: 20 : Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY_THROW );
248 : : OSL_ASSERT( xSink.is() );
249 [ + - ][ + - ]: 20 : xSink->setData( aSequences[nGroupIndex][nSeriesIdx] );
[ + - ][ + - ]
250 [ + - ][ + - ]: 20 : aResultSeries[nGroupIndex][nSeriesIdx].set( xSeries );
[ + - ][ # # ]
251 : : }
252 [ # # ]: 0 : catch( const uno::Exception & ex )
253 : : {
254 : : ASSERT_EXCEPTION( ex );
255 : : }
256 : : }
257 : : }
258 : :
259 [ + - ][ + - ]: 4 : return InterpretedData( aResultSeries, xCategories );
[ + - ][ + - ]
260 : : }
261 : :
262 : : // criterion: there must be two groups for stock-charts with volume and all
263 : : // series must have the correct number of data::XLabeledDataSequences
264 : :
265 : : // todo: skip first criterion? (to allow easy switch from stock-chart without
266 : : // volume to one with volume)
267 : 4 : sal_Bool SAL_CALL StockDataInterpreter::isDataCompatible(
268 : : const InterpretedData& aInterpretedData )
269 : : throw (uno::RuntimeException)
270 : : {
271 : : // high/low/close
272 : 4 : sal_Int32 nNumberOfNecessarySequences = 3;
273 : : // open
274 : 4 : StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
275 [ - + ][ + - ]: 4 : if( ( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
276 : : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ))
277 : 0 : ++nNumberOfNecessarySequences;
278 : : // volume
279 : : bool bHasVolume = (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
280 [ + - ][ - + ]: 4 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
281 : :
282 : : // 1. correct number of sub-types
283 [ - + ][ - + ]: 4 : if( aInterpretedData.Series.getLength() < (bHasVolume ? 2 : 1 ))
284 : 0 : return sal_False;
285 : :
286 : : // 2. a. volume -- use default check
287 [ - + ]: 4 : if( bHasVolume )
288 : : {
289 [ # # ]: 0 : if( ! DataInterpreter::isDataCompatible(
290 : : InterpretedData( Sequence< Sequence< Reference< XDataSeries > > >(
291 : : aInterpretedData.Series.getConstArray(), 1 ),
292 [ # # ][ # # ]: 0 : aInterpretedData.Categories )))
[ # # ]
293 : 0 : return sal_False;
294 : : }
295 : :
296 : : // 2. b. candlestick
297 : : {
298 : : OSL_ASSERT( aInterpretedData.Series.getLength() > (bHasVolume ? 1 : 0));
299 [ - + ][ + - ]: 4 : Sequence< Reference< XDataSeries > > aSeries( aInterpretedData.Series[(bHasVolume ? 1 : 0)] );
300 [ - + ]: 4 : if(!aSeries.getLength())
301 : 0 : return sal_False;
302 [ + - ]: 4 : for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
303 : : {
304 : : try
305 : : {
306 [ + - ][ + - ]: 4 : Reference< data::XDataSource > xSrc( aSeries[i], uno::UNO_QUERY_THROW );
307 [ + - ][ + - ]: 4 : Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
308 [ + - ]: 4 : if( aSeq.getLength() != nNumberOfNecessarySequences )
309 [ + - ][ + - ]: 4 : return sal_False;
[ - + ][ # # ]
310 : : }
311 [ # # ]: 0 : catch( const uno::Exception & ex )
312 : : {
313 : : ASSERT_EXCEPTION( ex );
314 : : }
315 [ + - ][ - + ]: 4 : }
316 : : }
317 : :
318 : : // 2. c. additional series
319 : : // ignore
320 : :
321 : 4 : return sal_True;
322 : : }
323 : :
324 : 0 : InterpretedData SAL_CALL StockDataInterpreter::reinterpretDataSeries(
325 : : const InterpretedData& aInterpretedData )
326 : : throw (uno::RuntimeException)
327 : : {
328 : : // prerequisite: StockDataInterpreter::isDataCompatible() returned true
329 : 0 : return aInterpretedData;
330 : : }
331 : :
332 : : } // namespace chart
333 : :
334 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|