Fixes #fdo30794 Based on bin/add-modelines script (originally posted in mail 1286706307.1871.1399280959@webmail.messagingengine.com) Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
346 lines
13 KiB
C++
346 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_chart2.hxx"
|
|
|
|
#include "StockDataInterpreter.hxx"
|
|
#include "DataSeries.hxx"
|
|
#include "macros.hxx"
|
|
#include "DataSeriesHelper.hxx"
|
|
#include "CommonConverters.hxx"
|
|
#include "ContainerHelper.hxx"
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/chart2/data/XDataSink.hpp>
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
|
|
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;
|
|
using ::rtl::OUString;
|
|
using namespace ::chart::ContainerHelper;
|
|
|
|
namespace chart
|
|
{
|
|
|
|
// explicit
|
|
StockDataInterpreter::StockDataInterpreter(
|
|
StockChartTypeTemplate::StockVariant eVariant,
|
|
const Reference< uno::XComponentContext > & xContext ) :
|
|
DataInterpreter( xContext ),
|
|
m_eStockVariant( eVariant )
|
|
{}
|
|
|
|
StockDataInterpreter::~StockDataInterpreter()
|
|
{}
|
|
|
|
StockChartTypeTemplate::StockVariant StockDataInterpreter::GetStockVariant() const
|
|
{
|
|
return m_eStockVariant;
|
|
}
|
|
|
|
// ____ XDataInterpreter ____
|
|
InterpretedData SAL_CALL StockDataInterpreter::interpretDataSource(
|
|
const Reference< data::XDataSource >& xSource,
|
|
const Sequence< beans::PropertyValue >& rArguments,
|
|
const Sequence< Reference< XDataSeries > >& rSeriesToReUse )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
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::OPEN_LOW_HI_CLOSE ) ||
|
|
( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
|
|
const bool bHasVolume (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
|
|
( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
|
|
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(), C2U("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(), C2U("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(), C2U("values-min"));
|
|
++nSourceIndex, ++nSeqIdx;
|
|
|
|
aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
|
|
if( aData[nSourceIndex].is())
|
|
SetRole( aData[nSourceIndex]->getValues(), C2U("values-max"));
|
|
++nSourceIndex, ++nSeqIdx;
|
|
|
|
aSequences[nCandleStickGroupIndex][nLabeledSeqIdx][nSeqIdx].set( aData[nSourceIndex] );
|
|
if( aData[nSourceIndex].is())
|
|
SetRole( aData[nSourceIndex]->getValues(), C2U("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(), C2U("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(), C2U("values-min"));
|
|
++nSourceIndex, ++nSeqIdx;
|
|
|
|
// 2. high
|
|
if( nSeqIdx < nRemaining )
|
|
{
|
|
aSequences[nCandleStickGroupIndex][nSeriesIndex][nSeqIdx].set( aData[nSourceIndex] );
|
|
if( aData[nSourceIndex].is())
|
|
SetRole( aData[nSourceIndex]->getValues(), C2U("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(), C2U("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( GetComponentContext() ) );
|
|
OSL_ASSERT( xSeries.is() );
|
|
Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY_THROW );
|
|
OSL_ASSERT( xSink.is() );
|
|
xSink->setData( aSequences[nGroupIndex][nSeriesIdx] );
|
|
aResultSeries[nGroupIndex][nSeriesIdx].set( xSeries );
|
|
}
|
|
catch( uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
}
|
|
}
|
|
|
|
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 )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
// high/low/close
|
|
sal_Int32 nNumberOfNecessarySequences = 3;
|
|
// open
|
|
StockChartTypeTemplate::StockVariant eVar( GetStockVariant());
|
|
if( ( eVar == StockChartTypeTemplate::OPEN_LOW_HI_CLOSE ) ||
|
|
( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ))
|
|
++nNumberOfNecessarySequences;
|
|
// volume
|
|
bool bHasVolume = (( eVar == StockChartTypeTemplate::VOL_LOW_HI_CLOSE ) ||
|
|
( eVar == StockChartTypeTemplate::VOL_OPEN_LOW_HI_CLOSE ));
|
|
|
|
// 1. correct number of sub-types
|
|
if( aInterpretedData.Series.getLength() < (bHasVolume ? 2 : 1 ))
|
|
return sal_False;
|
|
|
|
// 2. a. volume -- use default check
|
|
if( bHasVolume )
|
|
{
|
|
if( ! DataInterpreter::isDataCompatible(
|
|
InterpretedData( Sequence< Sequence< Reference< XDataSeries > > >(
|
|
aInterpretedData.Series.getConstArray(), 1 ),
|
|
aInterpretedData.Categories )))
|
|
return sal_False;
|
|
}
|
|
|
|
// 2. b. candlestick
|
|
{
|
|
OSL_ASSERT( aInterpretedData.Series.getLength() > (bHasVolume ? 1 : 0));
|
|
Sequence< Reference< XDataSeries > > aSeries( aInterpretedData.Series[(bHasVolume ? 1 : 0)] );
|
|
if(!aSeries.getLength())
|
|
return sal_False;
|
|
for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
|
|
{
|
|
try
|
|
{
|
|
Reference< data::XDataSource > xSrc( aSeries[i], uno::UNO_QUERY_THROW );
|
|
Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
|
|
if( aSeq.getLength() != nNumberOfNecessarySequences )
|
|
return sal_False;
|
|
}
|
|
catch( uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. c. additional series
|
|
// ignore
|
|
|
|
return sal_True;
|
|
}
|
|
|
|
InterpretedData SAL_CALL StockDataInterpreter::reinterpretDataSeries(
|
|
const InterpretedData& aInterpretedData )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
// prerequisite: StockDataInterpreter::isDataCompatible() returned true
|
|
return aInterpretedData;
|
|
}
|
|
|
|
} // namespace chart
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|