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