summaryrefslogtreecommitdiffstats
path: root/chart2/source/model/template/StockDataInterpreter.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'chart2/source/model/template/StockDataInterpreter.cxx')
-rw-r--r--chart2/source/model/template/StockDataInterpreter.cxx319
1 files changed, 319 insertions, 0 deletions
diff --git a/chart2/source/model/template/StockDataInterpreter.cxx b/chart2/source/model/template/StockDataInterpreter.cxx
new file mode 100644
index 000000000..15c289197
--- /dev/null
+++ b/chart2/source/model/template/StockDataInterpreter.cxx
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "StockDataInterpreter.hxx"
+#include <DataSeries.hxx>
+#include <com/sun/star/chart2/data/XDataSink.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using namespace ::std;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+// explicit
+StockDataInterpreter::StockDataInterpreter(
+ StockChartTypeTemplate::StockVariant eVariant ) :
+ DataInterpreter(),
+ m_eStockVariant( eVariant )
+{}
+
+StockDataInterpreter::~StockDataInterpreter()
+{}
+
+// ____ XDataInterpreter ____
+InterpretedData SAL_CALL StockDataInterpreter::interpretDataSource(
+ const Reference< data::XDataSource >& xSource,
+ const Sequence< beans::PropertyValue >& rArguments,
+ const Sequence< Reference< XDataSeries > >& rSeriesToReUse )
+{
+ if( ! xSource.is())
+ return InterpretedData();
+
+ Reference< data::XLabeledDataSequence > xCategories;
+ Sequence< Reference< data::XLabeledDataSequence > > aData( xSource->getDataSequences() );
+ const sal_Int32 nDataCount( aData.getLength());
+
+ // sub-type properties
+ const StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
+ const bool bHasOpenValues (( eVar == StockChartTypeTemplate::StockVariant::Open ) ||
+ ( eVar == StockChartTypeTemplate::StockVariant::VolumeOpen ));
+ const bool bHasVolume (( eVar == StockChartTypeTemplate::StockVariant::Volume ) ||
+ ( eVar == StockChartTypeTemplate::StockVariant::VolumeOpen ));
+ const bool bHasCategories( HasCategories( rArguments, aData ));
+
+ // necessary roles for "full series"
+ // low/high/close
+ sal_Int32 nNumberOfNecessarySequences( 3 );
+ if( bHasOpenValues )
+ ++nNumberOfNecessarySequences;
+ if( bHasVolume )
+ ++nNumberOfNecessarySequences;
+
+ // calculate number of full series (nNumOfFullSeries) and the number of remaining
+ // sequences used for additional "incomplete series" (nRemaining)
+ sal_Int32 nNumOfFullSeries( 0 );
+ sal_Int32 nRemaining( 0 );
+ {
+ sal_Int32 nAvailableSequences( nDataCount );
+ if( bHasCategories )
+ --nAvailableSequences;
+ nNumOfFullSeries = nAvailableSequences / nNumberOfNecessarySequences;
+ nRemaining = nAvailableSequences % nNumberOfNecessarySequences;
+ }
+ sal_Int32 nCandleStickSeries = nNumOfFullSeries;
+ sal_Int32 nVolumeSeries = nNumOfFullSeries;
+
+ sal_Int32 nNumberOfGroups( bHasVolume ? 2 : 1 );
+ // sequences of data::XLabeledDataSequence per series per group
+ Sequence< Sequence< Sequence< Reference< data::XLabeledDataSequence > > > > aSequences( nNumberOfGroups );
+ sal_Int32 nBarGroupIndex( 0 );
+ sal_Int32 nCandleStickGroupIndex( nNumberOfGroups - 1 );
+
+ // allocate space for labeled sequences
+ if( nRemaining > 0 )
+ ++nCandleStickSeries;
+ aSequences[nCandleStickGroupIndex].realloc( nCandleStickSeries );
+ if( bHasVolume )
+ {
+ // if there are remaining sequences, the first one is taken for
+ // additional close values, the second one is taken as volume, if volume
+ // is used
+ if( nRemaining > 1 )
+ ++nVolumeSeries;
+ aSequences[nBarGroupIndex].realloc( nVolumeSeries );
+ }
+
+ // create data
+ sal_Int32 nSourceIndex = 0; // index into aData sequence
+
+ // 1. categories
+ if( bHasCategories )
+ {
+ xCategories.set( aData[nSourceIndex] );
+ ++nSourceIndex;
+ }
+
+ // 2. create "full" series
+ for( sal_Int32 nLabeledSeqIdx=0; nLabeledSeqIdx<nNumOfFullSeries; ++nLabeledSeqIdx )
+ {
+ // bar
+ if( bHasVolume )
+ {
+ aSequences[nBarGroupIndex][nLabeledSeqIdx].realloc( 1 );
+ aSequences[nBarGroupIndex][nLabeledSeqIdx][0].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-y");
+ ++nSourceIndex;
+ }
+
+ sal_Int32 nSeqIdx = 0;
+ if( bHasOpenValues )
+ {
+ aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 4 );
+ aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-first");
+ ++nSourceIndex;
+ ++nSeqIdx;
+ }
+ else
+ aSequences[nCandleStickGroupIndex][nLabeledSeqIdx].realloc( 3 );
+
+ aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-min");
+ ++nSourceIndex;
+ ++nSeqIdx;
+
+ aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-max");
+ ++nSourceIndex;
+ ++nSeqIdx;
+
+ aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-last");
+ ++nSourceIndex;
+ ++nSeqIdx;
+ }
+
+ // 3. create series with remaining sequences
+ if( bHasVolume && nRemaining > 1 )
+ {
+ OSL_ASSERT( nVolumeSeries > nNumOfFullSeries );
+ aSequences[nBarGroupIndex][nVolumeSeries - 1].realloc( 1 );
+ OSL_ASSERT( nDataCount > nSourceIndex );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-y");
+ aSequences[nBarGroupIndex][nVolumeSeries - 1][0].set( aData[nSourceIndex] );
+ ++nSourceIndex;
+ --nRemaining;
+ OSL_ENSURE( nRemaining, "additional bar should only be used if there is at least one more sequence for a candle stick" );
+ }
+
+ // candle-stick
+ if( nRemaining > 0 )
+ {
+ OSL_ASSERT( nCandleStickSeries > nNumOfFullSeries );
+ const sal_Int32 nSeriesIndex = nCandleStickSeries - 1;
+ aSequences[nCandleStickGroupIndex][nSeriesIndex].realloc( nRemaining );
+ OSL_ASSERT( nDataCount > nSourceIndex );
+
+ // 1. low
+ sal_Int32 nSeqIdx( 0 );
+ aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-min");
+ ++nSourceIndex;
+ ++nSeqIdx;
+
+ // 2. high
+ if( nSeqIdx < nRemaining )
+ {
+ aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-max");
+ ++nSourceIndex;
+ ++nSeqIdx;
+ }
+
+ // 3. close
+ OSL_ENSURE( bHasOpenValues || nSeqIdx >= nRemaining, "could have created full series" );
+ if( nSeqIdx < nRemaining )
+ {
+ aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
+ if( aData[nSourceIndex].is())
+ SetRole( aData[nSourceIndex]->getValues(), "values-last");
+ ++nSourceIndex;
+ ++nSeqIdx;
+ }
+
+ // 4. open
+ OSL_ENSURE( nSeqIdx >= nRemaining, "could have created full series" );
+ }
+
+ // create DataSeries
+ Sequence< Sequence< Reference< XDataSeries > > > aResultSeries( nNumberOfGroups );
+ sal_Int32 nGroupIndex, nReUsedSeriesIdx = 0;
+ for( nGroupIndex=0; nGroupIndex<nNumberOfGroups; ++nGroupIndex )
+ {
+ const sal_Int32 nNumSeriesData = aSequences[nGroupIndex].getLength();
+ aResultSeries[nGroupIndex].realloc( nNumSeriesData );
+ for( sal_Int32 nSeriesIdx = 0; nSeriesIdx < nNumSeriesData; ++nSeriesIdx, ++nReUsedSeriesIdx )
+ {
+ try
+ {
+ Reference< XDataSeries > xSeries;
+ if( nReUsedSeriesIdx < rSeriesToReUse.getLength())
+ xSeries.set( rSeriesToReUse[nReUsedSeriesIdx] );
+ else
+ xSeries.set( new DataSeries );
+ OSL_ASSERT( xSeries.is() );
+ Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY_THROW );
+ xSink->setData( aSequences[nGroupIndex][nSeriesIdx] );
+ aResultSeries[nGroupIndex][nSeriesIdx].set( xSeries );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+
+ return InterpretedData( aResultSeries, xCategories );
+}
+
+// criterion: there must be two groups for stock-charts with volume and all
+// series must have the correct number of data::XLabeledDataSequences
+
+// todo: skip first criterion? (to allow easy switch from stock-chart without
+// volume to one with volume)
+sal_Bool SAL_CALL StockDataInterpreter::isDataCompatible(
+ const InterpretedData& aInterpretedData )
+{
+ // high/low/close
+ sal_Int32 nNumberOfNecessarySequences = 3;
+ // open
+ StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
+ if( ( eVar == StockChartTypeTemplate::StockVariant::Open ) ||
+ ( eVar == StockChartTypeTemplate::StockVariant::VolumeOpen ))
+ ++nNumberOfNecessarySequences;
+ // volume
+ bool bHasVolume = (( eVar == StockChartTypeTemplate::StockVariant::Volume ) ||
+ ( eVar == StockChartTypeTemplate::StockVariant::VolumeOpen ));
+
+ // 1. correct number of sub-types
+ if( aInterpretedData.Series.getLength() < (bHasVolume ? 2 : 1 ))
+ return false;
+
+ // 2. a. volume -- use default check
+ if( bHasVolume )
+ {
+ if( ! DataInterpreter::isDataCompatible(
+ InterpretedData( Sequence< Sequence< Reference< XDataSeries > > >(
+ aInterpretedData.Series.getConstArray(), 1 ),
+ aInterpretedData.Categories )))
+ return false;
+ }
+
+ // 2. b. candlestick
+ {
+ OSL_ASSERT( aInterpretedData.Series.getLength() > (bHasVolume ? 1 : 0));
+ const Sequence< Reference< XDataSeries > > aSeries( aInterpretedData.Series[(bHasVolume ? 1 : 0)] );
+ if(!aSeries.hasElements())
+ return false;
+ for( Reference< XDataSeries > const & dataSeries : aSeries )
+ {
+ try
+ {
+ Reference< data::XDataSource > xSrc( dataSeries, uno::UNO_QUERY_THROW );
+ Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
+ if( aSeq.getLength() != nNumberOfNecessarySequences )
+ return false;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+
+ // 2. c. additional series
+ // ignore
+
+ return true;
+}
+
+InterpretedData SAL_CALL StockDataInterpreter::reinterpretDataSeries(
+ const InterpretedData& aInterpretedData )
+{
+ // prerequisite: StockDataInterpreter::isDataCompatible() returned true
+ return aInterpretedData;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */