/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: StockDataInterpreter.cxx,v $ * $Revision: 1.3 $ * * 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 * * 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 #include // #include #include #include #include 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; nLabeledSeqIdxgetValues(), 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 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 ); } } } const Sequence< Reference< data::XLabeledDataSequence > > aUnusedData;//@todo remove the unused data concept completely return InterpretedData( aResultSeries, xCategories, aUnusedData ); } // 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, aInterpretedData.UnusedData ))) 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 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