1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
24 #include "StockDataInterpreter.hxx"
25 #include "StockChartTypeTemplate.hxx"
26 #include <DataSeries.hxx>
27 #include <comphelper/diagnose_ex.hxx>
29 using namespace ::com::sun::star
;
30 using namespace ::com::sun::star::chart2
;
32 using ::com::sun::star::uno::Reference
;
33 using ::com::sun::star::uno::Sequence
;
39 StockDataInterpreter::StockDataInterpreter(
40 StockChartTypeTemplate::StockVariant eVariant
) :
41 m_eStockVariant( eVariant
)
44 StockDataInterpreter::~StockDataInterpreter()
47 // ____ XDataInterpreter ____
48 InterpretedData
StockDataInterpreter::interpretDataSource(
49 const Reference
< data::XDataSource
>& xSource
,
50 const Sequence
< beans::PropertyValue
>& rArguments
,
51 const std::vector
< rtl::Reference
< ::chart::DataSeries
> >& rSeriesToReUse
)
54 return InterpretedData();
56 uno::Reference
< chart2::data::XLabeledDataSequence
> xCategories
;
57 std::vector
< uno::Reference
< chart2::data::XLabeledDataSequence
> > aData
= DataInterpreter::getDataSequences(xSource
);
58 const sal_Int32
nDataCount( aData
.size());
60 // sub-type properties
61 const StockChartTypeTemplate::StockVariant
eVar( GetStockVariant());
62 const bool bHasOpenValues (( eVar
== StockChartTypeTemplate::StockVariant::Open
) ||
63 ( eVar
== StockChartTypeTemplate::StockVariant::VolumeOpen
));
64 const bool bHasVolume (( eVar
== StockChartTypeTemplate::StockVariant::Volume
) ||
65 ( eVar
== StockChartTypeTemplate::StockVariant::VolumeOpen
));
66 const bool bHasCategories( HasCategories( rArguments
, aData
));
68 // necessary roles for "full series"
70 sal_Int32
nNumberOfNecessarySequences( 3 );
72 ++nNumberOfNecessarySequences
;
74 ++nNumberOfNecessarySequences
;
76 // calculate number of full series (nNumOfFullSeries) and the number of remaining
77 // sequences used for additional "incomplete series" (nRemaining)
78 sal_Int32
nNumOfFullSeries( 0 );
79 sal_Int32
nRemaining( 0 );
81 sal_Int32
nAvailableSequences( nDataCount
);
83 --nAvailableSequences
;
84 nNumOfFullSeries
= nAvailableSequences
/ nNumberOfNecessarySequences
;
85 nRemaining
= nAvailableSequences
% nNumberOfNecessarySequences
;
87 sal_Int32 nCandleStickSeries
= nNumOfFullSeries
;
88 sal_Int32 nVolumeSeries
= nNumOfFullSeries
;
90 sal_Int32
nNumberOfGroups( bHasVolume
? 2 : 1 );
91 // sequences of data::XLabeledDataSequence per series per group
92 std::vector
< std::vector
< std::vector
< uno::Reference
< chart2::data::XLabeledDataSequence
> > > > aSequences( nNumberOfGroups
);
93 const sal_Int32
nBarGroupIndex( 0 );
94 const sal_Int32
nCandleStickGroupIndex( nNumberOfGroups
- 1 );
96 // allocate space for labeled sequences
99 aSequences
[nCandleStickGroupIndex
].resize( nCandleStickSeries
);
100 auto & pCandleStickGroup
= aSequences
[nCandleStickGroupIndex
];
103 // if there are remaining sequences, the first one is taken for
104 // additional close values, the second one is taken as volume, if volume
108 aSequences
[nBarGroupIndex
].resize( nVolumeSeries
);
110 auto & pBarGroup
= aSequences
[nBarGroupIndex
];
113 sal_Int32 nSourceIndex
= 0; // index into aData sequence
118 xCategories
= aData
[nSourceIndex
];
122 // 2. create "full" series
123 for( sal_Int32 nLabeledSeqIdx
=0; nLabeledSeqIdx
<nNumOfFullSeries
; ++nLabeledSeqIdx
)
128 pBarGroup
[nLabeledSeqIdx
].resize( 1 );
129 pBarGroup
[nLabeledSeqIdx
][0] = aData
[nSourceIndex
];
130 if( aData
[nSourceIndex
].is())
131 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-y"_ustr
);
135 sal_Int32 nSeqIdx
= 0;
138 pCandleStickGroup
[nLabeledSeqIdx
].resize( 4 );
139 pCandleStickGroup
[nLabeledSeqIdx
][nSeqIdx
] = aData
[nSourceIndex
];
140 if( aData
[nSourceIndex
].is())
141 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-first"_ustr
);
146 pCandleStickGroup
[nLabeledSeqIdx
].resize( 3 );
147 auto & pLabeledSeq
= pCandleStickGroup
[nLabeledSeqIdx
];
149 pLabeledSeq
[nSeqIdx
] = aData
[nSourceIndex
];
150 if( aData
[nSourceIndex
].is())
151 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-min"_ustr
);
155 pLabeledSeq
[nSeqIdx
] = aData
[nSourceIndex
];
156 if( aData
[nSourceIndex
].is())
157 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-max"_ustr
);
161 pLabeledSeq
[nSeqIdx
] = aData
[nSourceIndex
];
162 if( aData
[nSourceIndex
].is())
163 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-last"_ustr
);
168 // 3. create series with remaining sequences
169 if( bHasVolume
&& nRemaining
> 1 )
171 OSL_ASSERT( nVolumeSeries
> nNumOfFullSeries
);
172 pBarGroup
[nVolumeSeries
- 1].resize( 1 );
173 OSL_ASSERT( nDataCount
> nSourceIndex
);
174 if( aData
[nSourceIndex
].is())
175 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-y"_ustr
);
176 pBarGroup
[nVolumeSeries
- 1][0] = aData
[nSourceIndex
];
179 OSL_ENSURE( nRemaining
, "additional bar should only be used if there is at least one more sequence for a candle stick" );
185 OSL_ASSERT( nCandleStickSeries
> nNumOfFullSeries
);
186 const sal_Int32 nSeriesIndex
= nCandleStickSeries
- 1;
187 pCandleStickGroup
[nSeriesIndex
].resize( nRemaining
);
188 auto & pLabeledSeq
= pCandleStickGroup
[nSeriesIndex
];
189 OSL_ASSERT( nDataCount
> nSourceIndex
);
192 sal_Int32
nSeqIdx( 0 );
193 pLabeledSeq
[nSeqIdx
] = aData
[nSourceIndex
];
194 if( aData
[nSourceIndex
].is())
195 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-min"_ustr
);
200 if( nSeqIdx
< nRemaining
)
202 pLabeledSeq
[nSeqIdx
] = aData
[nSourceIndex
];
203 if( aData
[nSourceIndex
].is())
204 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-max"_ustr
);
210 OSL_ENSURE( bHasOpenValues
|| nSeqIdx
>= nRemaining
, "could have created full series" );
211 if( nSeqIdx
< nRemaining
)
213 pLabeledSeq
[nSeqIdx
] = aData
[nSourceIndex
];
214 if( aData
[nSourceIndex
].is())
215 SetRole( aData
[nSourceIndex
]->getValues(), u
"values-last"_ustr
);
221 OSL_ENSURE( nSeqIdx
>= nRemaining
, "could have created full series" );
225 std::vector
< std::vector
< rtl::Reference
< DataSeries
> > > aResultSeries( nNumberOfGroups
);
226 sal_Int32 nGroupIndex
;
227 std::size_t nReUsedSeriesIdx
= 0;
228 for( nGroupIndex
=0; nGroupIndex
<nNumberOfGroups
; ++nGroupIndex
)
230 const sal_Int32 nNumSeriesData
= aSequences
[nGroupIndex
].size();
231 aResultSeries
[nGroupIndex
].resize( nNumSeriesData
);
232 auto & pResultSerie
= aResultSeries
[nGroupIndex
];
233 for( sal_Int32 nSeriesIdx
= 0; nSeriesIdx
< nNumSeriesData
; ++nSeriesIdx
, ++nReUsedSeriesIdx
)
237 rtl::Reference
< DataSeries
> xSeries
;
238 if( nReUsedSeriesIdx
< rSeriesToReUse
.size())
239 xSeries
= rSeriesToReUse
[nReUsedSeriesIdx
];
241 xSeries
= new DataSeries
;
242 assert( xSeries
.is() );
243 xSeries
->setData( aSequences
[nGroupIndex
][nSeriesIdx
] );
244 pResultSerie
[nSeriesIdx
] = std::move(xSeries
);
246 catch( const uno::Exception
& )
248 DBG_UNHANDLED_EXCEPTION("chart2");
253 return { std::move(aResultSeries
), xCategories
};
256 // criterion: there must be two groups for stock-charts with volume and all
257 // series must have the correct number of data::XLabeledDataSequences
259 // todo: skip first criterion? (to allow easy switch from stock-chart without
260 // volume to one with volume)
261 bool StockDataInterpreter::isDataCompatible(
262 const InterpretedData
& aInterpretedData
)
265 std::size_t nNumberOfNecessarySequences
= 3;
267 StockChartTypeTemplate::StockVariant
eVar( GetStockVariant());
268 if( ( eVar
== StockChartTypeTemplate::StockVariant::Open
) ||
269 ( eVar
== StockChartTypeTemplate::StockVariant::VolumeOpen
))
270 ++nNumberOfNecessarySequences
;
272 bool bHasVolume
= (( eVar
== StockChartTypeTemplate::StockVariant::Volume
) ||
273 ( eVar
== StockChartTypeTemplate::StockVariant::VolumeOpen
));
275 // 1. correct number of sub-types
276 if( aInterpretedData
.Series
.size() < (bHasVolume
? 2U : 1U ))
279 // 2. a. volume -- use default check
282 if( ! DataInterpreter::isDataCompatible(
283 { std::vector
< std::vector
< rtl::Reference
< DataSeries
> > >{
284 aInterpretedData
.Series
[0] },
285 aInterpretedData
.Categories
}))
291 OSL_ASSERT( aInterpretedData
.Series
.size() > (bHasVolume
? 1U : 0U));
292 const std::vector
< rtl::Reference
< DataSeries
> > & aSeries
= aInterpretedData
.Series
[(bHasVolume
? 1 : 0)];
295 for( rtl::Reference
< DataSeries
> const & dataSeries
: aSeries
)
299 if( dataSeries
->getDataSequences2().size() != nNumberOfNecessarySequences
)
302 catch( const uno::Exception
& )
304 DBG_UNHANDLED_EXCEPTION("chart2");
309 // 2. c. additional series
315 InterpretedData
StockDataInterpreter::reinterpretDataSeries(
316 const InterpretedData
& aInterpretedData
)
318 // prerequisite: StockDataInterpreter::isDataCompatible() returned true
319 return aInterpretedData
;
322 uno::Any
StockDataInterpreter::getChartTypeSpecificData(
323 const OUString
& sKey
)
325 if( sKey
== "stock variant" )
327 StockChartTypeTemplate::StockVariant
eStockVariant( GetStockVariant());
328 std::map
< StockChartTypeTemplate::StockVariant
, sal_Int32
> aTranslation
{
329 { StockChartTypeTemplate::StockVariant::NONE
, 0 },
330 { StockChartTypeTemplate::StockVariant::Open
, 1 },
331 { StockChartTypeTemplate::StockVariant::Volume
, 2 },
332 { StockChartTypeTemplate::StockVariant::VolumeOpen
, 3 }
334 return uno::Any( aTranslation
[eStockVariant
] );
341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */