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 5 : StockDataInterpreter::StockDataInterpreter(
46 : StockChartTypeTemplate::StockVariant eVariant,
47 : const Reference< uno::XComponentContext > & xContext ) :
48 : DataInterpreter( xContext ),
49 5 : m_eStockVariant( eVariant )
50 5 : {}
51 :
52 10 : StockDataInterpreter::~StockDataInterpreter()
53 10 : {}
54 :
55 : // ____ XDataInterpreter ____
56 5 : 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 5 : if( ! xSource.is())
63 1 : return InterpretedData();
64 :
65 4 : Reference< data::XLabeledDataSequence > xCategories;
66 8 : Sequence< Reference< data::XLabeledDataSequence > > aData( xSource->getDataSequences() );
67 4 : const sal_Int32 nDataCount( aData.getLength());
68 :
69 : // sub-type properties
70 4 : const StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
71 4 : const bool bHasOpenValues (( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
72 4 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
73 4 : const bool bHasVolume (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
74 4 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
75 4 : const bool bHasCategories( HasCategories( rArguments, aData ));
76 :
77 : // necessary roles for "full series"
78 : // low/high/close
79 4 : sal_Int32 nNumberOfNecessarySequences( 3 );
80 4 : if( bHasOpenValues )
81 2 : ++nNumberOfNecessarySequences;
82 4 : if( bHasVolume )
83 1 : ++nNumberOfNecessarySequences;
84 :
85 : // calculate number of full series (nNumOfFullSeries) and the number of remaining
86 : // sequences used for additional "incomplete series" (nRemaining)
87 4 : sal_Int32 nNumOfFullSeries( 0 );
88 4 : sal_Int32 nRemaining( 0 );
89 : {
90 4 : sal_Int32 nAvailableSequences( nDataCount );
91 4 : if( bHasCategories )
92 3 : --nAvailableSequences;
93 4 : nNumOfFullSeries = nAvailableSequences / nNumberOfNecessarySequences;
94 4 : nRemaining = nAvailableSequences % nNumberOfNecessarySequences;
95 : }
96 4 : sal_Int32 nCandleStickSeries = nNumOfFullSeries;
97 4 : sal_Int32 nVolumeSeries = nNumOfFullSeries;
98 :
99 4 : sal_Int32 nNumberOfGroups( bHasVolume ? 2 : 1 );
100 : // sequences of data::XLabeledDataSequence per series per group
101 8 : Sequence< Sequence< Sequence< Reference< data::XLabeledDataSequence > > > > aSequences( nNumberOfGroups );
102 4 : sal_Int32 nBarGroupIndex( 0 );
103 4 : sal_Int32 nCandleStickGroupIndex( nNumberOfGroups - 1 );
104 :
105 : // allocate space for labeled sequences
106 4 : if( nRemaining > 0 )
107 2 : ++nCandleStickSeries;
108 4 : aSequences[nCandleStickGroupIndex].realloc( nCandleStickSeries );
109 4 : 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 1 : if( nRemaining > 1 )
115 0 : ++nVolumeSeries;
116 1 : aSequences[nBarGroupIndex].realloc( nVolumeSeries );
117 : }
118 :
119 : // create data
120 4 : sal_Int32 nSourceIndex = 0; // index into aData sequence
121 :
122 : // 1. categories
123 4 : if( bHasCategories )
124 : {
125 3 : xCategories.set( aData[nSourceIndex] );
126 3 : ++nSourceIndex;
127 : }
128 :
129 : // 2. create "full" series
130 13 : for( sal_Int32 nLabeledSeqIdx=0; nLabeledSeqIdx<nNumOfFullSeries; ++nLabeledSeqIdx )
131 : {
132 : // bar
133 9 : if( bHasVolume )
134 : {
135 1 : aSequences[nBarGroupIndex][nLabeledSeqIdx].realloc( 1 );
136 1 : aSequences[nBarGroupIndex][nLabeledSeqIdx][0].set( aData[nSourceIndex] );
137 1 : if( aData[nSourceIndex].is())
138 1 : SetRole( aData[nSourceIndex]->getValues(), "values-y");
139 1 : ++nSourceIndex;
140 : }
141 :
142 9 : sal_Int32 nSeqIdx = 0;
143 9 : if( bHasOpenValues )
144 : {
145 1 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 4 );
146 1 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
147 1 : if( aData[nSourceIndex].is())
148 1 : SetRole( aData[nSourceIndex]->getValues(), "values-first");
149 1 : ++nSourceIndex, ++nSeqIdx;
150 : }
151 : else
152 8 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 3 );
153 :
154 9 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
155 9 : if( aData[nSourceIndex].is())
156 9 : SetRole( aData[nSourceIndex]->getValues(), "values-min");
157 9 : ++nSourceIndex, ++nSeqIdx;
158 :
159 9 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
160 9 : if( aData[nSourceIndex].is())
161 9 : SetRole( aData[nSourceIndex]->getValues(), "values-max");
162 9 : ++nSourceIndex, ++nSeqIdx;
163 :
164 9 : aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
165 9 : if( aData[nSourceIndex].is())
166 9 : SetRole( aData[nSourceIndex]->getValues(), "values-last");
167 9 : ++nSourceIndex, ++nSeqIdx;
168 : }
169 :
170 : // 3. create series with remaining sequences
171 4 : 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 4 : if( nRemaining > 0 )
186 : {
187 : OSL_ASSERT( nCandleStickSeries > nNumOfFullSeries );
188 2 : const sal_Int32 nSeriesIndex = nCandleStickSeries - 1;
189 2 : aSequences[nCandleStickGroupIndex][nSeriesIndex].realloc( nRemaining );
190 : OSL_ASSERT( nDataCount > nSourceIndex );
191 :
192 : // 1. low
193 2 : sal_Int32 nSeqIdx( 0 );
194 2 : aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
195 2 : if( aData[nSourceIndex].is())
196 2 : SetRole( aData[nSourceIndex]->getValues(), "values-min");
197 2 : ++nSourceIndex, ++nSeqIdx;
198 :
199 : // 2. high
200 2 : 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 2 : 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 8 : Sequence< Sequence< Reference< XDataSeries > > > aResultSeries( nNumberOfGroups );
224 4 : sal_Int32 nGroupIndex, nReUsedSeriesIdx = 0;
225 9 : for( nGroupIndex=0; nGroupIndex<nNumberOfGroups; ++nGroupIndex )
226 : {
227 5 : const sal_Int32 nNumSeriesData = aSequences[nGroupIndex].getLength();
228 5 : aResultSeries[nGroupIndex].realloc( nNumSeriesData );
229 17 : for( sal_Int32 nSeriesIdx = 0; nSeriesIdx < nNumSeriesData; ++nSeriesIdx, ++nReUsedSeriesIdx )
230 : {
231 : try
232 : {
233 12 : Reference< XDataSeries > xSeries;
234 12 : if( nReUsedSeriesIdx < rSeriesToReUse.getLength())
235 12 : xSeries.set( rSeriesToReUse[nReUsedSeriesIdx] );
236 : else
237 0 : xSeries.set( new DataSeries( GetComponentContext() ) );
238 : OSL_ASSERT( xSeries.is() );
239 24 : Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY_THROW );
240 : OSL_ASSERT( xSink.is() );
241 12 : xSink->setData( aSequences[nGroupIndex][nSeriesIdx] );
242 24 : aResultSeries[nGroupIndex][nSeriesIdx].set( xSeries );
243 : }
244 0 : catch( const uno::Exception & ex )
245 : {
246 : ASSERT_EXCEPTION( ex );
247 : }
248 : }
249 : }
250 :
251 8 : 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 3 : sal_Bool SAL_CALL StockDataInterpreter::isDataCompatible(
260 : const InterpretedData& aInterpretedData )
261 : throw (uno::RuntimeException, std::exception)
262 : {
263 : // high/low/close
264 3 : sal_Int32 nNumberOfNecessarySequences = 3;
265 : // open
266 3 : StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
267 3 : if( ( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
268 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ))
269 1 : ++nNumberOfNecessarySequences;
270 : // volume
271 3 : bool bHasVolume = (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
272 3 : ( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
273 :
274 : // 1. correct number of sub-types
275 3 : if( aInterpretedData.Series.getLength() < (bHasVolume ? 2 : 1 ))
276 0 : return sal_False;
277 :
278 : // 2. a. volume -- use default check
279 3 : 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 3 : Sequence< Reference< XDataSeries > > aSeries( aInterpretedData.Series[(bHasVolume ? 1 : 0)] );
292 3 : if(!aSeries.getLength())
293 1 : return sal_False;
294 2 : for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
295 : {
296 : try
297 : {
298 2 : Reference< data::XDataSource > xSrc( aSeries[i], uno::UNO_QUERY_THROW );
299 2 : Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
300 2 : if( aSeq.getLength() != nNumberOfNecessarySequences )
301 2 : 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: */
|