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 "StatisticsHelper.hxx"
31 #include "DataSeriesHelper.hxx"
32 #include "ErrorBar.hxx"
33 #include "macros.hxx"
34 
35 #include <rtl/math.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <comphelper/processfactory.hxx>
38 
39 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
40 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
41 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
42 #include <com/sun/star/chart2/data/XDataSink.hpp>
43 #include <com/sun/star/chart/ErrorBarStyle.hpp>
44 
45 using ::com::sun::star::uno::Sequence;
46 using ::com::sun::star::uno::Reference;
47 using ::rtl::OUString;
48 using ::rtl::OUStringBuffer;
49 using namespace ::com::sun::star;
50 
51 namespace
52 {
53 
54 double lcl_getVariance( const Sequence< double > & rData, sal_Int32 & rOutValidCount,
55         bool bUnbiasedEstimator )
56 {
57     const sal_Int32 nCount = rData.getLength();
58     rOutValidCount = nCount;
59 
60     double fSum = 0.0;
61     double fQuadSum = 0.0;
62 
63     for( sal_Int32 i = 0; i < nCount; ++i )
64     {
65         const double fData = rData[i];
66         if( ::rtl::math::isNan( fData ))
67             --rOutValidCount;
68         else
69         {
70             fSum     += fData;
71             fQuadSum += fData * fData;
72         }
73     }
74 
75     double fResult;
76     if( rOutValidCount == 0 )
77         ::rtl::math::setNan( & fResult );
78     else
79     {
80         const double fN = static_cast< double >( rOutValidCount );
81         if( bUnbiasedEstimator )
82             fResult = (fQuadSum - fSum*fSum/fN) / (fN - 1);
83         else
84             fResult = (fQuadSum - fSum*fSum/fN) / fN;
85     }
86 
87     return fResult;
88 }
89 
90 Reference< chart2::data::XLabeledDataSequence > lcl_getErrorBarLabeledSequence(
91     const Reference< chart2::data::XDataSource > & xDataSource,
92     bool bPositiveValue, bool bYError,
93     OUString & rOutRoleNameUsed )
94 {
95     OUStringBuffer aRole( C2U("error-bars-"));
96     if( bYError )
97         aRole.append( sal_Unicode( 'y' ));
98     else
99         aRole.append( sal_Unicode( 'x' ));
100 
101     OUString aPlainRole = aRole.makeStringAndClear();
102     aRole.append( aPlainRole );
103     aRole.append( sal_Unicode( '-' ));
104 
105     if( bPositiveValue )
106         aRole = aRole.appendAscii( "positive" );
107     else
108         aRole = aRole.appendAscii( "negative" );
109 
110     OUString aLongRole = aRole.makeStringAndClear();
111     Reference< chart2::data::XLabeledDataSequence > xLSeq(
112         ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, aLongRole ));
113     // try role without "-negative" or "-positive" postfix
114     if( xLSeq.is())
115         rOutRoleNameUsed = aLongRole;
116     else
117     {
118         xLSeq.set( ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, aPlainRole ));
119         if( xLSeq.is())
120             rOutRoleNameUsed = aPlainRole;
121         else
122             rOutRoleNameUsed = aLongRole;
123     }
124 
125     return xLSeq;
126 }
127 
128 void lcl_setRole(
129     const Reference< chart2::data::XDataSequence > & xNewSequence,
130     const OUString & rRole )
131 {
132     Reference< beans::XPropertySet > xSeqProp( xNewSequence, uno::UNO_QUERY );
133     if( xSeqProp.is())
134         xSeqProp->setPropertyValue( C2U("Role"), uno::makeAny( rRole ));
135 }
136 
137 void lcl_addSequenceToDataSource(
138     const Reference< chart2::data::XDataSource > & xDataSource,
139     const Reference< chart2::data::XDataSequence > & xNewSequence,
140     const OUString & rRole )
141 {
142     Reference< chart2::data::XDataSink > xSink( xDataSource, uno::UNO_QUERY );
143     Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory(), uno::UNO_QUERY_THROW );
144     if( ! ( xFact.is() && xSink.is() ))
145         return;
146 
147     Reference< chart2::data::XLabeledDataSequence > xLSeq(
148         xFact->createInstance( C2U("com.sun.star.chart2.data.LabeledDataSequence")), uno::UNO_QUERY );
149     if( xLSeq.is())
150     {
151         lcl_setRole( xNewSequence, rRole );
152         xLSeq->setValues( xNewSequence );
153         Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences(
154             xDataSource->getDataSequences());
155         aSequences.realloc( aSequences.getLength() + 1 );
156         aSequences[ aSequences.getLength() - 1 ] = xLSeq;
157         xSink->setData( aSequences );
158     }
159 }
160 
161 void lcl_setXMLRangePropertyAtDataSequence(
162     const Reference< chart2::data::XDataSequence > & xDataSequence,
163     const OUString & rXMLRange )
164 {
165     try
166     {
167         const OUString aXMLRangePropName( C2U( "CachedXMLRange" ));
168         Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW );
169         Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo());
170         if( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ))
171             xProp->setPropertyValue( aXMLRangePropName, uno::makeAny( rXMLRange ));
172     }
173     catch( const uno::Exception & ex )
174     {
175         ASSERT_EXCEPTION( ex );
176     }
177 }
178 
179 } // anonymous namespace
180 
181 namespace chart
182 {
183 
184 double StatisticsHelper::getVariance(
185     const Sequence< double > & rData,
186     bool bUnbiasedEstimator /* = false */ )
187 {
188     sal_Int32 nValCount;
189     return lcl_getVariance( rData, nValCount, bUnbiasedEstimator );
190 }
191 
192 double StatisticsHelper::getStandardDeviation( const Sequence< double > & rData )
193 {
194     double fResult = getVariance( rData );
195     if( ! ::rtl::math::isNan( fResult ))
196         fResult = sqrt( fResult );
197 
198     return fResult;
199 }
200 
201 double StatisticsHelper::getStandardError( const Sequence< double > & rData )
202 {
203     sal_Int32 nValCount;
204     double fVar = lcl_getVariance( rData, nValCount, false );
205     double fResult;
206 
207     if( nValCount == 0 ||
208         ::rtl::math::isNan( fVar ))
209     {
210         ::rtl::math::setNan( & fResult );
211     }
212     else
213     {
214         // standard-deviation / sqrt(n)
215         fResult = sqrt( fVar ) / sqrt( double(nValCount) );
216     }
217 
218     return fResult;
219 }
220 
221 Reference< chart2::data::XLabeledDataSequence > StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
222     const Reference< chart2::data::XDataSource > & xDataSource,
223     bool bPositiveValue,
224     bool bYError /* = true */ )
225 {
226     Reference< chart2::data::XLabeledDataSequence > xResult;
227     if( !xDataSource.is())
228         return xResult;
229 
230     OUString aRole;
231     Reference< chart2::data::XLabeledDataSequence > xLSeq(
232         lcl_getErrorBarLabeledSequence( xDataSource, bPositiveValue, bYError, aRole ));
233     if( xLSeq.is())
234         xResult.set( xLSeq );
235 
236     return xResult;
237 }
238 
239 Reference< chart2::data::XDataSequence > StatisticsHelper::getErrorDataSequenceFromDataSource(
240     const Reference< chart2::data::XDataSource > & xDataSource,
241     bool bPositiveValue,
242     bool bYError /* = true */ )
243 {
244     Reference< chart2::data::XLabeledDataSequence > xLSeq(
245         StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
246             xDataSource, bPositiveValue,
247             bYError ));
248     if( !xLSeq.is())
249         return Reference< chart2::data::XDataSequence >();
250 
251     return xLSeq->getValues();
252 }
253 
254 double StatisticsHelper::getErrorFromDataSource(
255     const Reference< chart2::data::XDataSource > & xDataSource,
256     sal_Int32 nIndex,
257     bool bPositiveValue,
258     bool bYError /* = true */ )
259 {
260     double fResult = 0.0;
261     ::rtl::math::setNan( & fResult );
262 
263     Reference< chart2::data::XDataSequence > xValues(
264         StatisticsHelper::getErrorDataSequenceFromDataSource( xDataSource, bPositiveValue, bYError ));
265 
266     Reference< chart2::data::XNumericalDataSequence > xNumValues( xValues, uno::UNO_QUERY );
267     if( xNumValues.is())
268     {
269         Sequence< double > aData( xNumValues->getNumericalData());
270         if( nIndex < aData.getLength())
271             fResult = aData[nIndex];
272     }
273     else if( xValues.is())
274     {
275         Sequence< uno::Any > aData( xValues->getData());
276         if( nIndex < aData.getLength())
277             aData[nIndex] >>= fResult;
278     }
279 
280     return fResult;
281 }
282 
283 void StatisticsHelper::setErrorDataSequence(
284     const Reference< chart2::data::XDataSource > & xDataSource,
285     const Reference< chart2::data::XDataProvider > & xDataProvider,
286     const OUString & rNewRange,
287     bool bPositiveValue,
288     bool bYError /* = true */,
289     OUString * pXMLRange /* = 0 */ )
290 {
291     Reference< chart2::data::XDataSink > xDataSink( xDataSource, uno::UNO_QUERY );
292     if( ! ( xDataSink.is() && xDataProvider.is()))
293         return;
294 
295     OUString aRole;
296     Reference< chart2::data::XLabeledDataSequence > xLSeq(
297         lcl_getErrorBarLabeledSequence( xDataSource, bPositiveValue, bYError, aRole ));
298     Reference< chart2::data::XDataSequence > xNewSequence(
299         xDataProvider->createDataSequenceByRangeRepresentation( rNewRange ));
300     if( xNewSequence.is())
301     {
302         if( pXMLRange )
303             lcl_setXMLRangePropertyAtDataSequence( xNewSequence, *pXMLRange );
304         if( xLSeq.is())
305         {
306             lcl_setRole( xNewSequence, aRole );
307             xLSeq->setValues( xNewSequence );
308         }
309         else
310             lcl_addSequenceToDataSource( xDataSource, xNewSequence, aRole );
311     }
312 }
313 
314 Reference< beans::XPropertySet > StatisticsHelper::addErrorBars(
315     const Reference< chart2::XDataSeries > & xDataSeries,
316     const Reference< uno::XComponentContext > & xContext,
317     sal_Int32 nStyle,
318     bool bYError /* = true */ )
319 {
320     Reference< beans::XPropertySet > xErrorBar;
321     Reference< beans::XPropertySet > xSeriesProp( xDataSeries, uno::UNO_QUERY );
322     if( !xSeriesProp.is())
323         return xErrorBar;
324 
325     const OUString aPropName( bYError ? C2U("ErrorBarY") : C2U("ErrorBarX"));
326     if( !( xSeriesProp->getPropertyValue( aPropName ) >>= xErrorBar ) ||
327         !xErrorBar.is())
328     {
329         xErrorBar.set( createErrorBar( xContext ));
330     }
331 
332     OSL_ASSERT( xErrorBar.is());
333     if( xErrorBar.is())
334     {
335         xErrorBar->setPropertyValue( C2U("ErrorBarStyle"), uno::makeAny( nStyle ));
336     }
337 
338     xSeriesProp->setPropertyValue( aPropName, uno::makeAny( xErrorBar ));
339 
340     return xErrorBar;
341 }
342 
343 Reference< beans::XPropertySet > StatisticsHelper::getErrorBars(
344     const Reference< chart2::XDataSeries > & xDataSeries,
345     bool bYError /* = true */ )
346 {
347     Reference< beans::XPropertySet > xSeriesProp( xDataSeries, uno::UNO_QUERY );
348     Reference< beans::XPropertySet > xErrorBar;
349     const OUString aPropName( bYError ? C2U("ErrorBarY") : C2U("ErrorBarX"));
350 
351     if ( xSeriesProp.is())
352         xSeriesProp->getPropertyValue( aPropName ) >>= xErrorBar;
353 
354     return xErrorBar;
355 }
356 
357 bool StatisticsHelper::hasErrorBars(
358     const Reference< chart2::XDataSeries > & xDataSeries,
359     bool bYError /* = true */ )
360 {
361     Reference< beans::XPropertySet > xErrorBar( getErrorBars( xDataSeries, bYError ));
362     sal_Int32 nStyle = ::com::sun::star::chart::ErrorBarStyle::NONE;
363 
364     return ( xErrorBar.is() &&
365              ( xErrorBar->getPropertyValue( C2U("ErrorBarStyle")) >>= nStyle ) &&
366              nStyle != ::com::sun::star::chart::ErrorBarStyle::NONE );
367 }
368 
369 void StatisticsHelper::removeErrorBars(
370     const Reference< chart2::XDataSeries > & xDataSeries,
371     bool bYError /* = true  */ )
372 {
373     Reference< beans::XPropertySet > xErrorBar( getErrorBars( xDataSeries, bYError ));
374     if ( xErrorBar.is())
375         xErrorBar->setPropertyValue( C2U("ErrorBarStyle"), uno::makeAny(
376                                          ::com::sun::star::chart::ErrorBarStyle::NONE ));
377 }
378 
379 bool StatisticsHelper::usesErrorBarRanges(
380     const Reference< chart2::XDataSeries > & xDataSeries,
381     bool bYError /* = true */ )
382 {
383     Reference< beans::XPropertySet > xErrorBar( getErrorBars( xDataSeries, bYError ));
384     sal_Int32 nStyle = ::com::sun::star::chart::ErrorBarStyle::NONE;
385 
386     return ( xErrorBar.is() &&
387              ( xErrorBar->getPropertyValue( C2U("ErrorBarStyle")) >>= nStyle ) &&
388              nStyle == ::com::sun::star::chart::ErrorBarStyle::FROM_DATA );
389 }
390 
391 } //  namespace chart
392