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 
31 #include "DataInterpreter.hxx"
32 #include "DataSeries.hxx"
33 #include "DataSourceHelper.hxx"
34 #include "DataSeriesHelper.hxx"
35 #include "macros.hxx"
36 #include "CommonConverters.hxx"
37 #include "ContainerHelper.hxx"
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/chart2/data/XDataSink.hpp>
40 
41 #include <vector>
42 #include <algorithm>
43 #include <iterator>
44 
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::chart2;
47 using namespace ::std;
48 using namespace ::chart::ContainerHelper;
49 
50 using ::com::sun::star::uno::Reference;
51 using ::com::sun::star::uno::Sequence;
52 using ::rtl::OUString;
53 
54 #if OSL_DEBUG_LEVEL > 1
55 namespace
56 {
57 void lcl_ShowDataSource( const Reference< data::XDataSource > & xSource );
58 }
59 #endif
60 
61 namespace chart
62 {
63 
64 DataInterpreter::DataInterpreter(
65     const Reference< uno::XComponentContext > & xContext ) :
66         m_xContext( xContext )
67 {}
68 
69 DataInterpreter::~DataInterpreter()
70 {}
71 
72 Reference< uno::XComponentContext > DataInterpreter::GetComponentContext() const
73 {
74     return m_xContext;
75 }
76 
77 // ____ XDataInterpreter ____
78 InterpretedData SAL_CALL DataInterpreter::interpretDataSource(
79     const Reference< data::XDataSource >& xSource,
80     const Sequence< beans::PropertyValue >& aArguments,
81     const Sequence< Reference< XDataSeries > >& aSeriesToReUse )
82     throw (uno::RuntimeException)
83 {
84     if( ! xSource.is())
85         return InterpretedData();
86 
87 #if OSL_DEBUG_LEVEL > 2
88     lcl_ShowDataSource( xSource );
89 #endif
90 
91     Sequence< Reference< data::XLabeledDataSequence > > aData( xSource->getDataSequences() );
92 
93     Reference< data::XLabeledDataSequence > xCategories;
94     vector< Reference< data::XLabeledDataSequence > > aSequencesVec;
95 
96     // check if we should use categories
97 
98     bool bHasCategories( HasCategories( aArguments, aData ));
99 
100     // parse data
101     bool bCategoriesUsed = false;
102     for( sal_Int32 i=0; i < aData.getLength(); ++i )
103     {
104         try
105         {
106             if( bHasCategories && ! bCategoriesUsed )
107             {
108                 xCategories.set( aData[i] );
109                 if( xCategories.is())
110                     SetRole( xCategories->getValues(), C2U("categories"));
111                 bCategoriesUsed = true;
112             }
113             else
114             {
115                 aSequencesVec.push_back( aData[i] );
116                 if( aData[i].is())
117                     SetRole( aData[i]->getValues(), C2U("values-y"));
118             }
119         }
120         catch( uno::Exception & ex )
121         {
122             ASSERT_EXCEPTION( ex );
123         }
124     }
125 
126     // create DataSeries
127     vector< Reference< data::XLabeledDataSequence > >::const_iterator
128           aSequencesVecIt = aSequencesVec.begin();
129 
130     sal_Int32 nSeriesIndex = 0;
131     vector< Reference< XDataSeries > > aSeriesVec;
132     aSeriesVec.reserve( aSequencesVec.size());
133 
134     for( ;aSequencesVecIt != aSequencesVec.end(); ++aSequencesVecIt, ++nSeriesIndex )
135     {
136         Sequence< Reference< data::XLabeledDataSequence > > aNewData( & (*aSequencesVecIt), 1 );
137         Reference< XDataSeries > xSeries;
138         if( nSeriesIndex < aSeriesToReUse.getLength())
139             xSeries.set( aSeriesToReUse[nSeriesIndex] );
140         else
141             xSeries.set( new DataSeries( GetComponentContext() ));
142         OSL_ASSERT( xSeries.is() );
143         Reference< data::XDataSink > xSink( xSeries, uno::UNO_QUERY );
144         OSL_ASSERT( xSink.is() );
145         xSink->setData( aNewData );
146 
147         aSeriesVec.push_back( xSeries );
148     }
149 
150     Sequence< Sequence< Reference< XDataSeries > > > aSeries(1);
151     aSeries[0] = ContainerToSequence( aSeriesVec );
152     return InterpretedData( aSeries, xCategories );
153 }
154 
155 InterpretedData SAL_CALL DataInterpreter::reinterpretDataSeries(
156     const InterpretedData& aInterpretedData )
157     throw (uno::RuntimeException)
158 {
159     InterpretedData aResult( aInterpretedData );
160 
161     sal_Int32 i=0;
162     Sequence< Reference< XDataSeries > > aSeries( FlattenSequence( aInterpretedData.Series ));
163     const sal_Int32 nCount = aSeries.getLength();
164     for( ; i<nCount; ++i )
165     {
166         try
167         {
168             Reference< data::XDataSource > xSeriesSource( aSeries[i], uno::UNO_QUERY_THROW );
169             Sequence< Reference< data::XLabeledDataSequence > > aNewSequences;
170 
171             // values-y
172             Reference< data::XLabeledDataSequence > xValuesY(
173                 DataSeriesHelper::getDataSequenceByRole( xSeriesSource, C2U("values-y"), false ));
174             // re-use values-... as values-y
175             if( ! xValuesY.is())
176             {
177                 xValuesY.set(
178                     DataSeriesHelper::getDataSequenceByRole( xSeriesSource, C2U("values"), true ));
179                 if( xValuesY.is())
180                     SetRole( xValuesY->getValues(), C2U("values-y"));
181             }
182             if( xValuesY.is())
183             {
184                 aNewSequences.realloc(1);
185                 aNewSequences[0] = xValuesY;
186             }
187 
188             Sequence< Reference< data::XLabeledDataSequence > > aSeqs( xSeriesSource->getDataSequences());
189             if( aSeqs.getLength() != aNewSequences.getLength() )
190             {
191 #if OSL_DEBUG_LEVEL > 1
192                 sal_Int32 j=0;
193                 for( ; j<aSeqs.getLength(); ++j )
194                 {
195                     OSL_ENSURE( aSeqs[j] == xValuesY, "All sequences should be used" );
196                 }
197 #endif
198                 Reference< data::XDataSink > xSink( xSeriesSource, uno::UNO_QUERY_THROW );
199                 xSink->setData( aNewSequences );
200             }
201         }
202         catch( uno::Exception & ex )
203         {
204             ASSERT_EXCEPTION( ex );
205         }
206     }
207 
208     return aResult;
209 }
210 
211 // criterion: all series must have exactly one data::XLabeledDataSequence
212 sal_Bool SAL_CALL DataInterpreter::isDataCompatible(
213     const chart2::InterpretedData& aInterpretedData )
214     throw (uno::RuntimeException)
215 {
216     Sequence< Reference< XDataSeries > > aSeries( FlattenSequence( aInterpretedData.Series ));
217     for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
218     {
219         try
220         {
221             Reference< data::XDataSource > xSrc( aSeries[i], uno::UNO_QUERY_THROW );
222             Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
223             if( aSeq.getLength() != 1 )
224                 return sal_False;
225         }
226         catch( uno::Exception & ex )
227         {
228             ASSERT_EXCEPTION( ex );
229         }
230     }
231 
232     return sal_True;
233 }
234 
235 namespace
236 {
237 
238 struct lcl_LabeledSequenceEquals : public unary_function< Reference< data::XLabeledDataSequence >, bool >
239 {
240     lcl_LabeledSequenceEquals( const Reference< data::XLabeledDataSequence > & xLSeqToCmp ) :
241             m_bHasLabels ( false ),
242             m_bHasValues ( false )
243     {
244         if( xLSeqToCmp.is())
245         {
246             Reference< data::XDataSequence > xSeq( xLSeqToCmp->getValues());
247             if( xSeq.is())
248             {
249                 m_bHasValues = true;
250                 m_aValuesRangeRep = xSeq->getSourceRangeRepresentation();
251             }
252 
253             xSeq.set( xLSeqToCmp->getLabel());
254             if( xSeq.is())
255             {
256                 m_bHasLabels = true;
257                 m_aLabelRangeRep = xSeq->getSourceRangeRepresentation();
258             }
259         }
260     }
261 
262     bool operator() ( const Reference< data::XLabeledDataSequence > & xSeq )
263     {
264         if( ! xSeq.is())
265             return false;
266 
267         Reference< data::XDataSequence > xSeqValues( xSeq->getValues() );
268         Reference< data::XDataSequence > xSeqLabels( xSeq->getLabel() );
269         bool bHasValues = xSeqValues.is();
270         bool bHasLabels = xSeqLabels.is();
271 
272         return ( ( (m_bHasValues == bHasValues) &&
273                    (!bHasValues || m_aValuesRangeRep.equals( xSeqValues->getSourceRangeRepresentation())) ) &&
274                  ( (m_bHasLabels == bHasLabels) &&
275                    (!bHasLabels || m_aLabelRangeRep.equals( xSeqLabels->getSourceRangeRepresentation())) )
276             );
277     }
278 
279 private:
280     bool m_bHasLabels;
281     bool m_bHasValues;
282     OUString m_aValuesRangeRep;
283     OUString m_aLabelRangeRep;
284 };
285 
286 } // anonymous namespace
287 
288 Reference< data::XDataSource > SAL_CALL DataInterpreter::mergeInterpretedData(
289     const InterpretedData& aInterpretedData )
290     throw (uno::RuntimeException)
291 {
292     vector< Reference< data::XLabeledDataSequence > > aResultVec;
293     aResultVec.reserve( aInterpretedData.Series.getLength() +
294                         1 // categories
295         );
296 
297     if( aInterpretedData.Categories.is())
298         aResultVec.push_back( aInterpretedData.Categories );
299 
300     Sequence< Reference< XDataSeries > > aSeries( FlattenSequence( aInterpretedData.Series ));
301     for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeries.getLength(); ++nSeriesIdx )
302     {
303         try
304         {
305             Reference< data::XDataSource > xSrc( aSeries[nSeriesIdx], uno::UNO_QUERY_THROW );
306             Sequence< Reference< data::XLabeledDataSequence > > aSeq( xSrc->getDataSequences());
307 
308             // add all sequences of data series
309             for( sal_Int32 nSeqIdx=0; nSeqIdx<aSeq.getLength(); ++nSeqIdx )
310             {
311                 Reference< data::XLabeledDataSequence > xAdd( aSeq[nSeqIdx] );
312 
313                 // only add if sequence is not yet in the result
314                 if( find_if( aResultVec.begin(), aResultVec.end(),
315                              lcl_LabeledSequenceEquals( xAdd )) == aResultVec.end())
316                 {
317                     aResultVec.push_back( xAdd );
318                 }
319             }
320         }
321         catch( uno::Exception & ex )
322         {
323             ASSERT_EXCEPTION( ex );
324         }
325     }
326 
327     return Reference< data::XDataSource >( DataSourceHelper::createDataSource( ContainerToSequence( aResultVec ) ) );
328 }
329 
330 // convenience methods
331 
332 OUString DataInterpreter::GetRole( const Reference< data::XDataSequence > & xSeq )
333 {
334     OUString aResult;
335     if( ! xSeq.is())
336         return aResult;
337 
338     try
339     {
340         Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY_THROW );
341         xProp->getPropertyValue( C2U("Role")) >>= aResult;
342     }
343     catch( uno::Exception & ex )
344     {
345         ASSERT_EXCEPTION( ex );
346     }
347     return aResult;
348 }
349 
350 void DataInterpreter::SetRole( const Reference< data::XDataSequence > & xSeq, const OUString & rRole )
351 {
352     if( ! xSeq.is())
353         return;
354     try
355     {
356         Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY_THROW );
357         xProp->setPropertyValue( C2U("Role"), uno::makeAny( rRole ));
358     }
359     catch( uno::Exception & ex )
360     {
361         ASSERT_EXCEPTION( ex );
362     }
363 }
364 
365 uno::Any DataInterpreter::GetProperty(
366     const Sequence< beans::PropertyValue > & aArguments,
367     const OUString & rName )
368 {
369     for( sal_Int32 i=aArguments.getLength(); i--; )
370     {
371         if( aArguments[i].Name.equals( rName ))
372             return aArguments[i].Value;
373     }
374     return uno::Any();
375 }
376 
377 bool DataInterpreter::HasCategories(
378     const Sequence< beans::PropertyValue > & rArguments,
379     const Sequence< Reference< data::XLabeledDataSequence > > & rData )
380 {
381     bool bHasCategories = false;
382 
383     if( rArguments.getLength() > 0 )
384         GetProperty( rArguments, C2U(("HasCategories"))) >>= bHasCategories;
385 
386     for( sal_Int32 nLSeqIdx=0; ! bHasCategories && nLSeqIdx<rData.getLength(); ++nLSeqIdx )
387         bHasCategories = ( rData[nLSeqIdx].is() &&
388                            GetRole( rData[nLSeqIdx]->getValues()).equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("categories")));
389 
390     return bHasCategories;
391 }
392 
393 bool DataInterpreter::UseCategoriesAsX( const Sequence< beans::PropertyValue > & rArguments )
394 {
395     bool bUseCategoriesAsX = true;
396     if( rArguments.getLength() > 0 )
397         GetProperty( rArguments, C2U(("UseCategoriesAsX"))) >>= bUseCategoriesAsX;
398     return bUseCategoriesAsX;
399 }
400 
401 // ------------------------------------------------------------
402 
403 Sequence< OUString > DataInterpreter::getSupportedServiceNames_Static()
404 {
405     Sequence< OUString > aServices( 1 );
406     aServices[0] = C2U( "com.sun.star.chart2.DataInterpreter" );
407     return aServices;
408 }
409 
410 // implement XServiceInfo methods basing upon getSupportedServiceNames_Static
411 APPHELPER_XSERVICEINFO_IMPL( DataInterpreter, C2U("com.sun.star.comp.chart2.DataInterpreter"));
412 
413 } // namespace chart
414 
415 #if OSL_DEBUG_LEVEL > 1
416 namespace
417 {
418 
419 void lcl_ShowDataSource( const Reference< data::XDataSource > & xSource )
420 {
421     if( ! xSource.is())
422         return;
423 
424     OSL_TRACE( "DataSource in DataInterpreter:" );
425     Sequence< Reference< data::XLabeledDataSequence > > aSequences( xSource->getDataSequences());
426     Reference< beans::XPropertySet > xProp;
427     OUString aId;
428     const sal_Int32 nMax = aSequences.getLength();
429     for( sal_Int32 k = 0; k < nMax; ++k )
430     {
431         if( aSequences[k].is())
432         {
433             OUString aSourceRepr(C2U("<none>"));
434             if( aSequences[k]->getValues().is())
435                 aSourceRepr = aSequences[k]->getValues()->getSourceRangeRepresentation();
436             xProp.set( aSequences[k]->getValues(), uno::UNO_QUERY );
437             if( xProp.is() &&
438                 ( xProp->getPropertyValue( C2U( "Role" )) >>= aId ))
439             {
440                 OSL_TRACE( "  <data sequence %d> Role: %s, Source: %s", k, U2C( aId ), U2C( aSourceRepr ));
441             }
442             else
443             {
444                 OSL_TRACE( "  <data sequence %d> unknown Role, Source: %s", k, U2C( aSourceRepr ) );
445             }
446 
447             aSourceRepr = C2U("<none>");
448             if( aSequences[k]->getLabel().is())
449                 aSourceRepr = OUString( aSequences[k]->getLabel()->getSourceRangeRepresentation());
450             xProp.set( aSequences[k]->getLabel(), uno::UNO_QUERY );
451             if( xProp.is() &&
452                 ( xProp->getPropertyValue( C2U( "Role" )) >>= aId ))
453             {
454                 OSL_TRACE( "  <data sequence label %d> Role: %s, Source: %s", k, U2C( aId ), U2C( aSourceRepr ));
455             }
456             else
457             {
458                 OSL_TRACE( "  <data sequence label %d> unknown Role, Source: %s", k, U2C( aSourceRepr ) );
459             }
460         }
461     }
462 }
463 
464 }
465 #endif
466