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