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