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