1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
30 #include "DataSeries.hxx"
31 #include "DataSeriesProperties.hxx"
32 #include "DataPointProperties.hxx"
33 #include "CharacterProperties.hxx"
34 #include "UserDefinedProperties.hxx"
35 #include "DataPoint.hxx"
36 #include "macros.hxx"
37 #include "DataSeriesHelper.hxx"
38 #include "ContainerHelper.hxx"
39 #include "CloneHelper.hxx"
40 #include "ModifyListenerHelper.hxx"
41 #include "EventListenerHelper.hxx"
42 
43 #include <algorithm>
44 
45 using namespace ::com::sun::star;
46 
47 using ::com::sun::star::beans::Property;
48 using ::com::sun::star::uno::Sequence;
49 using ::com::sun::star::uno::Reference;
50 using ::com::sun::star::uno::Any;
51 using ::rtl::OUString;
52 using ::osl::MutexGuard;
53 
54 // ----------------------------------------
55 
56 namespace
57 {
58 
59 struct StaticDataSeriesDefaults_Initializer
60 {
61     ::chart::tPropertyValueMap* operator()()
62     {
63         static ::chart::tPropertyValueMap aStaticDefaults;
64         lcl_AddDefaultsToMap( aStaticDefaults );
65         return &aStaticDefaults;
66     }
67 private:
68     void lcl_AddDefaultsToMap( ::chart::tPropertyValueMap & rOutMap )
69     {
70         ::chart::DataSeriesProperties::AddDefaultsToMap( rOutMap );
71         ::chart::CharacterProperties::AddDefaultsToMap( rOutMap );
72 
73         float fDefaultCharHeight = 10.0;
74         ::chart::PropertyHelper::setPropertyValue( rOutMap, ::chart::CharacterProperties::PROP_CHAR_CHAR_HEIGHT, fDefaultCharHeight );
75         ::chart::PropertyHelper::setPropertyValue( rOutMap, ::chart::CharacterProperties::PROP_CHAR_ASIAN_CHAR_HEIGHT, fDefaultCharHeight );
76         ::chart::PropertyHelper::setPropertyValue( rOutMap, ::chart::CharacterProperties::PROP_CHAR_COMPLEX_CHAR_HEIGHT, fDefaultCharHeight );
77     }
78 };
79 
80 struct StaticDataSeriesDefaults : public rtl::StaticAggregate< ::chart::tPropertyValueMap, StaticDataSeriesDefaults_Initializer >
81 {
82 };
83 
84 struct StaticDataSeriesInfoHelper_Initializer
85 {
86     ::cppu::OPropertyArrayHelper* operator()()
87     {
88         static ::cppu::OPropertyArrayHelper aPropHelper( lcl_GetPropertySequence() );
89         return &aPropHelper;
90     }
91 
92 private:
93     uno::Sequence< Property > lcl_GetPropertySequence()
94     {
95         ::std::vector< ::com::sun::star::beans::Property > aProperties;
96         ::chart::DataSeriesProperties::AddPropertiesToVector( aProperties );
97         ::chart::CharacterProperties::AddPropertiesToVector( aProperties );
98         ::chart::UserDefinedProperties::AddPropertiesToVector( aProperties );
99 
100         ::std::sort( aProperties.begin(), aProperties.end(),
101                      ::chart::PropertyNameLess() );
102 
103         return ::chart::ContainerHelper::ContainerToSequence( aProperties );
104     }
105 
106 };
107 
108 struct StaticDataSeriesInfoHelper : public rtl::StaticAggregate< ::cppu::OPropertyArrayHelper, StaticDataSeriesInfoHelper_Initializer >
109 {
110 };
111 
112 struct StaticDataSeriesInfo_Initializer
113 {
114     uno::Reference< beans::XPropertySetInfo >* operator()()
115     {
116         static uno::Reference< beans::XPropertySetInfo > xPropertySetInfo(
117             ::cppu::OPropertySetHelper::createPropertySetInfo(*StaticDataSeriesInfoHelper::get() ) );
118         return &xPropertySetInfo;
119     }
120 };
121 
122 struct StaticDataSeriesInfo : public rtl::StaticAggregate< uno::Reference< beans::XPropertySetInfo >, StaticDataSeriesInfo_Initializer >
123 {
124 };
125 
126 void lcl_SetParent(
127     const uno::Reference< uno::XInterface > & xChildInterface,
128     const uno::Reference< uno::XInterface > & xParentInterface )
129 {
130     uno::Reference< container::XChild > xChild( xChildInterface, uno::UNO_QUERY );
131     if( xChild.is())
132         xChild->setParent( xParentInterface );
133 }
134 
135 typedef ::std::map< sal_Int32, ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > >
136     lcl_tDataPointMap;
137 
138 void lcl_CloneAttributedDataPoints(
139     const lcl_tDataPointMap & rSource, lcl_tDataPointMap & rDestination,
140     const uno::Reference< uno::XInterface > & xSeries )
141 {
142     for( lcl_tDataPointMap::const_iterator aIt( rSource.begin());
143          aIt != rSource.end(); ++aIt )
144     {
145         Reference< beans::XPropertySet > xPoint( (*aIt).second );
146         if( xPoint.is())
147         {
148             Reference< util::XCloneable > xCloneable( xPoint, uno::UNO_QUERY );
149             if( xCloneable.is())
150             {
151                 xPoint.set( xCloneable->createClone(), uno::UNO_QUERY );
152                 if( xPoint.is())
153                 {
154                     lcl_SetParent( xPoint, xSeries );
155                     rDestination.insert( lcl_tDataPointMap::value_type( (*aIt).first, xPoint ));
156                 }
157             }
158         }
159     }
160 }
161 
162 } // anonymous namespace
163 
164 // ----------------------------------------
165 
166 namespace chart
167 {
168 
169 DataSeries::DataSeries( const uno::Reference< uno::XComponentContext > & xContext ) :
170         ::property::OPropertySet( m_aMutex ),
171         m_xContext( xContext ),
172         m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder())
173 {
174 }
175 
176 DataSeries::DataSeries( const DataSeries & rOther ) :
177         MutexContainer(),
178         impl::DataSeries_Base(),
179         ::property::OPropertySet( rOther, m_aMutex ),
180     m_xContext( rOther.m_xContext ),
181     m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder())
182 {
183     if( ! rOther.m_aDataSequences.empty())
184     {
185         CloneHelper::CloneRefVector< tDataSequenceContainer::value_type >(
186             rOther.m_aDataSequences, m_aDataSequences );
187         ModifyListenerHelper::addListenerToAllElements( m_aDataSequences, m_xModifyEventForwarder );
188     }
189 
190     CloneHelper::CloneRefVector< Reference< chart2::XRegressionCurve > >( rOther.m_aRegressionCurves, m_aRegressionCurves );
191     ModifyListenerHelper::addListenerToAllElements( m_aRegressionCurves, m_xModifyEventForwarder );
192 
193     // add as listener to XPropertySet properties
194     Reference< beans::XPropertySet > xPropertySet;
195     uno::Any aValue;
196 
197     getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X );
198     if( ( aValue >>= xPropertySet )
199         && xPropertySet.is())
200         ModifyListenerHelper::addListener( xPropertySet, m_xModifyEventForwarder );
201 
202     getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y );
203     if( ( aValue >>= xPropertySet )
204         && xPropertySet.is())
205         ModifyListenerHelper::addListener( xPropertySet, m_xModifyEventForwarder );
206 }
207 
208 // late initialization to call after copy-constructing
209 void DataSeries::Init( const DataSeries & rOther )
210 {
211     if( ! rOther.m_aDataSequences.empty())
212         EventListenerHelper::addListenerToAllElements( m_aDataSequences, this );
213 
214     Reference< uno::XInterface > xThisInterface( static_cast< ::cppu::OWeakObject * >( this ));
215     if( ! rOther.m_aAttributedDataPoints.empty())
216     {
217         lcl_CloneAttributedDataPoints(
218             rOther.m_aAttributedDataPoints, m_aAttributedDataPoints, xThisInterface );
219         ModifyListenerHelper::addListenerToAllMapElements( m_aAttributedDataPoints, m_xModifyEventForwarder );
220     }
221 
222     // add as parent to error bars
223     Reference< beans::XPropertySet > xPropertySet;
224     uno::Any aValue;
225 
226     getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X );
227     if( ( aValue >>= xPropertySet )
228         && xPropertySet.is())
229         lcl_SetParent( xPropertySet, xThisInterface );
230 
231     getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y );
232     if( ( aValue >>= xPropertySet )
233         && xPropertySet.is())
234         lcl_SetParent( xPropertySet, xThisInterface );
235 }
236 
237 DataSeries::~DataSeries()
238 {
239     try
240     {
241         ModifyListenerHelper::removeListenerFromAllMapElements( m_aAttributedDataPoints, m_xModifyEventForwarder );
242         ModifyListenerHelper::removeListenerFromAllElements( m_aRegressionCurves, m_xModifyEventForwarder );
243         ModifyListenerHelper::removeListenerFromAllElements( m_aDataSequences, m_xModifyEventForwarder );
244 
245         // remove listener from XPropertySet properties
246         Reference< beans::XPropertySet > xPropertySet;
247         uno::Any aValue;
248 
249         getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X );
250         if( ( aValue >>= xPropertySet )
251             && xPropertySet.is())
252             ModifyListenerHelper::removeListener( xPropertySet, m_xModifyEventForwarder );
253 
254         getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y );
255         if( ( aValue >>= xPropertySet )
256             && xPropertySet.is())
257             ModifyListenerHelper::removeListener( xPropertySet, m_xModifyEventForwarder );
258     }
259     catch( const uno::Exception & ex )
260     {
261         ASSERT_EXCEPTION( ex );
262     }
263 }
264 
265 // ____ XCloneable ____
266 uno::Reference< util::XCloneable > SAL_CALL DataSeries::createClone()
267     throw (uno::RuntimeException)
268 {
269     DataSeries * pNewSeries( new DataSeries( *this ));
270     // hold a reference to the clone
271     uno::Reference< util::XCloneable > xResult( pNewSeries );
272     // do initialization that uses uno references to the clone
273     pNewSeries->Init( *this );
274 
275     return xResult;
276 }
277 
278 Sequence< OUString > DataSeries::getSupportedServiceNames_Static()
279 {
280     Sequence< OUString > aServices( 3 );
281     aServices[ 0 ] = C2U( "com.sun.star.chart2.DataSeries" );
282     aServices[ 1 ] = C2U( "com.sun.star.chart2.DataPointProperties" );
283     aServices[ 2 ] = C2U( "com.sun.star.beans.PropertySet" );
284     return aServices;
285 }
286 
287 // ____ OPropertySet ____
288 uno::Any DataSeries::GetDefaultValue( sal_Int32 nHandle ) const
289     throw(beans::UnknownPropertyException)
290 {
291     const tPropertyValueMap& rStaticDefaults = *StaticDataSeriesDefaults::get();
292     tPropertyValueMap::const_iterator aFound( rStaticDefaults.find( nHandle ) );
293     if( aFound == rStaticDefaults.end() )
294         return uno::Any();
295     return (*aFound).second;
296 }
297 
298 // ____ OPropertySet ____
299 ::cppu::IPropertyArrayHelper & SAL_CALL DataSeries::getInfoHelper()
300 {
301     return *StaticDataSeriesInfoHelper::get();
302 }
303 
304 // ____ XPropertySet ____
305 uno::Reference< beans::XPropertySetInfo > SAL_CALL DataSeries::getPropertySetInfo()
306     throw (uno::RuntimeException)
307 {
308     return *StaticDataSeriesInfo::get();
309 }
310 
311 void SAL_CALL DataSeries::getFastPropertyValue
312     ( uno::Any& rValue,
313       sal_Int32 nHandle ) const
314 {
315     // special handling for get.  set is not possible for this property
316     if( nHandle == DataSeriesProperties::PROP_DATASERIES_ATTRIBUTED_DATA_POINTS )
317     {
318         // ToDo: only add those property sets that are really modified
319         uno::Sequence< sal_Int32 > aSeq( m_aAttributedDataPoints.size());
320         sal_Int32 * pIndexArray = aSeq.getArray();
321         sal_Int32 i = 0;
322 
323         for( tDataPointAttributeContainer::const_iterator aIt( m_aAttributedDataPoints.begin());
324              aIt != m_aAttributedDataPoints.end(); ++aIt )
325         {
326             pIndexArray[ i ] = (*aIt).first;
327             ++i;
328         }
329 
330         rValue <<= aSeq;
331     }
332     else
333         OPropertySet::getFastPropertyValue( rValue, nHandle );
334 }
335 
336 void SAL_CALL DataSeries::setFastPropertyValue_NoBroadcast(
337     sal_Int32 nHandle, const uno::Any& rValue )
338     throw (uno::Exception)
339 {
340     if(    nHandle == DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y
341         || nHandle == DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X )
342     {
343         uno::Any aOldValue;
344         Reference< util::XModifyBroadcaster > xBroadcaster;
345         this->getFastPropertyValue( aOldValue, nHandle );
346         if( aOldValue.hasValue() &&
347             (aOldValue >>= xBroadcaster) &&
348             xBroadcaster.is())
349         {
350             ModifyListenerHelper::removeListener( xBroadcaster, m_xModifyEventForwarder );
351         }
352 
353         OSL_ASSERT( rValue.getValueType().getTypeClass() == uno::TypeClass_INTERFACE );
354         if( rValue.hasValue() &&
355             (rValue >>= xBroadcaster) &&
356             xBroadcaster.is())
357         {
358             ModifyListenerHelper::addListener( xBroadcaster, m_xModifyEventForwarder );
359         }
360     }
361 
362     ::property::OPropertySet::setFastPropertyValue_NoBroadcast( nHandle, rValue );
363 }
364 
365 Reference< beans::XPropertySet >
366     SAL_CALL DataSeries::getDataPointByIndex( sal_Int32 nIndex )
367     throw (lang::IndexOutOfBoundsException,
368            uno::RuntimeException)
369 {
370     Reference< beans::XPropertySet > xResult;
371 
372     Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences;
373     {
374         MutexGuard aGuard( GetMutex() );
375         aSequences = ContainerHelper::ContainerToSequence( m_aDataSequences );
376     }
377 
378     ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aValuesSeries(
379         DataSeriesHelper::getAllDataSequencesByRole( aSequences , C2U("values"), true ) );
380     if( !aValuesSeries.empty() )
381     {
382         Reference< chart2::data::XDataSequence > xSeq( aValuesSeries.front()->getValues() );
383         if( 0 <= nIndex && nIndex < xSeq->getData().getLength() )
384         {
385             {
386                 MutexGuard aGuard( GetMutex() );
387                 tDataPointAttributeContainer::iterator aIt( m_aAttributedDataPoints.find( nIndex ) );
388                 if( aIt != m_aAttributedDataPoints.end() )
389                     xResult = (*aIt).second;
390             }
391             if( !xResult.is() )
392             {
393                 Reference< beans::XPropertySet > xParentProperties;
394                 Reference< util::XModifyListener > xModifyEventForwarder;
395                 {
396                     MutexGuard aGuard( GetMutex() );
397                     xParentProperties = this;
398                     xModifyEventForwarder = m_xModifyEventForwarder;
399                 }
400 
401                 // create a new XPropertySet for this data point
402                 xResult.set( new DataPoint( xParentProperties ) );
403                 {
404                     MutexGuard aGuard( GetMutex() );
405                     m_aAttributedDataPoints[ nIndex ] = xResult;
406                 }
407                 ModifyListenerHelper::addListener( xResult, xModifyEventForwarder );
408             }
409         }
410     }
411     else
412     {
413         throw lang::IndexOutOfBoundsException();
414     }
415 
416     return xResult;
417 }
418 
419 void SAL_CALL DataSeries::resetDataPoint( sal_Int32 nIndex )
420         throw (uno::RuntimeException)
421 {
422     Reference< beans::XPropertySet > xDataPointProp;
423     Reference< util::XModifyListener > xModifyEventForwarder;
424     {
425         MutexGuard aGuard( GetMutex() );
426         xModifyEventForwarder = m_xModifyEventForwarder;
427         tDataPointAttributeContainer::iterator aIt( m_aAttributedDataPoints.find( nIndex ));
428         if( aIt != m_aAttributedDataPoints.end())
429         {
430             xDataPointProp = (*aIt).second;
431             m_aAttributedDataPoints.erase(aIt);
432         }
433 
434     }
435     if( xDataPointProp.is() )
436     {
437         Reference< util::XModifyBroadcaster > xBroadcaster( xDataPointProp, uno::UNO_QUERY );
438         if( xBroadcaster.is() && xModifyEventForwarder.is())
439             xBroadcaster->removeModifyListener( xModifyEventForwarder );
440         fireModifyEvent();
441     }
442 }
443 
444 void SAL_CALL DataSeries::resetAllDataPoints()
445         throw (uno::RuntimeException)
446 {
447     tDataPointAttributeContainer  aOldAttributedDataPoints;
448     Reference< util::XModifyListener > xModifyEventForwarder;
449     {
450         MutexGuard aGuard( GetMutex() );
451         xModifyEventForwarder = m_xModifyEventForwarder;
452         std::swap( aOldAttributedDataPoints, m_aAttributedDataPoints );
453     }
454     ModifyListenerHelper::removeListenerFromAllMapElements( aOldAttributedDataPoints, xModifyEventForwarder );
455     aOldAttributedDataPoints.clear();
456     fireModifyEvent();
457 }
458 
459 // ____ XDataSink ____
460 void SAL_CALL DataSeries::setData( const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > >& aData )
461     throw (uno::RuntimeException)
462 {
463     tDataSequenceContainer aOldDataSequences;
464     tDataSequenceContainer aNewDataSequences;
465     Reference< util::XModifyListener > xModifyEventForwarder;
466     Reference< lang::XEventListener > xListener;
467     {
468         MutexGuard aGuard( GetMutex() );
469         xModifyEventForwarder = m_xModifyEventForwarder;
470         xListener = this;
471         std::swap( aOldDataSequences, m_aDataSequences );
472         aNewDataSequences = ContainerHelper::SequenceToVector( aData );
473         m_aDataSequences = aNewDataSequences;
474     }
475     ModifyListenerHelper::removeListenerFromAllElements( aOldDataSequences, xModifyEventForwarder );
476     EventListenerHelper::removeListenerFromAllElements( aOldDataSequences, xListener );
477     EventListenerHelper::addListenerToAllElements( aNewDataSequences, xListener );
478     ModifyListenerHelper::addListenerToAllElements( aNewDataSequences, xModifyEventForwarder );
479     fireModifyEvent();
480 }
481 
482 // ____ XDataSource ____
483 Sequence< Reference< chart2::data::XLabeledDataSequence > > SAL_CALL DataSeries::getDataSequences()
484     throw (uno::RuntimeException)
485 {
486     MutexGuard aGuard( GetMutex() );
487     return ContainerHelper::ContainerToSequence( m_aDataSequences );
488 }
489 
490 
491 // ____ XRegressionCurveContainer ____
492 void SAL_CALL DataSeries::addRegressionCurve(
493     const uno::Reference< chart2::XRegressionCurve >& xRegressionCurve )
494     throw (lang::IllegalArgumentException,
495            uno::RuntimeException)
496 {
497     Reference< util::XModifyListener > xModifyEventForwarder;
498     {
499         MutexGuard aGuard( GetMutex() );
500         xModifyEventForwarder = m_xModifyEventForwarder;
501         if( ::std::find( m_aRegressionCurves.begin(), m_aRegressionCurves.end(), xRegressionCurve )
502             != m_aRegressionCurves.end())
503             throw lang::IllegalArgumentException();
504         m_aRegressionCurves.push_back( xRegressionCurve );
505     }
506     ModifyListenerHelper::addListener( xRegressionCurve, xModifyEventForwarder );
507     fireModifyEvent();
508 }
509 
510 void SAL_CALL DataSeries::removeRegressionCurve(
511     const uno::Reference< chart2::XRegressionCurve >& xRegressionCurve )
512     throw (container::NoSuchElementException,
513            uno::RuntimeException)
514 {
515     if( !xRegressionCurve.is() )
516         throw container::NoSuchElementException();
517 
518     Reference< util::XModifyListener > xModifyEventForwarder;
519     {
520         MutexGuard aGuard( GetMutex() );
521         xModifyEventForwarder = m_xModifyEventForwarder;
522         tRegressionCurveContainerType::iterator aIt(
523             ::std::find( m_aRegressionCurves.begin(), m_aRegressionCurves.end(), xRegressionCurve ) );
524         if( aIt == m_aRegressionCurves.end())
525             throw container::NoSuchElementException(
526                 C2U( "The given regression curve is no element of this series" ),
527                 static_cast< uno::XWeak * >( this ));
528         m_aRegressionCurves.erase( aIt );
529     }
530 
531     ModifyListenerHelper::removeListener( xRegressionCurve, xModifyEventForwarder );
532     fireModifyEvent();
533 }
534 
535 uno::Sequence< uno::Reference< chart2::XRegressionCurve > > SAL_CALL DataSeries::getRegressionCurves()
536     throw (uno::RuntimeException)
537 {
538     MutexGuard aGuard( GetMutex() );
539     return ContainerHelper::ContainerToSequence( m_aRegressionCurves );
540 }
541 
542 void SAL_CALL DataSeries::setRegressionCurves(
543     const Sequence< Reference< chart2::XRegressionCurve > >& aRegressionCurves )
544     throw (uno::RuntimeException)
545 {
546     tRegressionCurveContainerType aOldCurves;
547     tRegressionCurveContainerType aNewCurves( ContainerHelper::SequenceToVector( aRegressionCurves ) );
548     Reference< util::XModifyListener > xModifyEventForwarder;
549     {
550         MutexGuard aGuard( GetMutex() );
551         xModifyEventForwarder = m_xModifyEventForwarder;
552         std::swap( aOldCurves, m_aRegressionCurves );
553         m_aRegressionCurves = aNewCurves;
554     }
555     ModifyListenerHelper::removeListenerFromAllElements( aOldCurves, xModifyEventForwarder );
556     ModifyListenerHelper::addListenerToAllElements( aNewCurves, xModifyEventForwarder );
557     fireModifyEvent();
558 }
559 
560 // ____ XModifyBroadcaster ____
561 void SAL_CALL DataSeries::addModifyListener( const Reference< util::XModifyListener >& aListener )
562     throw (uno::RuntimeException)
563 {
564     try
565     {
566         Reference< util::XModifyBroadcaster > xBroadcaster( m_xModifyEventForwarder, uno::UNO_QUERY_THROW );
567         xBroadcaster->addModifyListener( aListener );
568     }
569     catch( const uno::Exception & ex )
570     {
571         ASSERT_EXCEPTION( ex );
572     }
573 }
574 
575 void SAL_CALL DataSeries::removeModifyListener( const Reference< util::XModifyListener >& aListener )
576     throw (uno::RuntimeException)
577 {
578     try
579     {
580         Reference< util::XModifyBroadcaster > xBroadcaster( m_xModifyEventForwarder, uno::UNO_QUERY_THROW );
581         xBroadcaster->removeModifyListener( aListener );
582     }
583     catch( const uno::Exception & ex )
584     {
585         ASSERT_EXCEPTION( ex );
586     }
587 }
588 
589 // ____ XModifyListener ____
590 void SAL_CALL DataSeries::modified( const lang::EventObject& aEvent )
591     throw (uno::RuntimeException)
592 {
593     m_xModifyEventForwarder->modified( aEvent );
594 }
595 
596 // ____ XEventListener (base of XModifyListener) ____
597 void SAL_CALL DataSeries::disposing( const lang::EventObject& rEventObject )
598     throw (uno::RuntimeException)
599 {
600     // forget disposed data sequences
601     tDataSequenceContainer::iterator aIt(
602         ::std::find( m_aDataSequences.begin(), m_aDataSequences.end(), rEventObject.Source ));
603     if( aIt != m_aDataSequences.end())
604         m_aDataSequences.erase( aIt );
605 }
606 
607 // ____ OPropertySet ____
608 void DataSeries::firePropertyChangeEvent()
609 {
610     fireModifyEvent();
611 }
612 
613 void DataSeries::fireModifyEvent()
614 {
615     m_xModifyEventForwarder->modified( lang::EventObject( static_cast< uno::XWeak* >( this )));
616 }
617 
618 
619 // ================================================================================
620 
621 using impl::DataSeries_Base;
622 using ::property::OPropertySet;
623 
624 IMPLEMENT_FORWARD_XINTERFACE2( DataSeries, DataSeries_Base, OPropertySet )
625 IMPLEMENT_FORWARD_XTYPEPROVIDER2( DataSeries, DataSeries_Base, OPropertySet )
626 
627 // implement XServiceInfo methods basing upon getSupportedServiceNames_Static
628 APPHELPER_XSERVICEINFO_IMPL( DataSeries,
629                              C2U( "com.sun.star.comp.chart.DataSeries" ));
630 
631 }  // namespace chart
632