Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/model/template/DataInterpreter.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <DataInterpreter.hxx>
21
#include <DataSeries.hxx>
22
#include <DataSource.hxx>
23
#include <DataSeriesHelper.hxx>
24
#include <CommonConverters.hxx>
25
#include <com/sun/star/beans/XPropertySet.hpp>
26
#include <cppuhelper/supportsservice.hxx>
27
#include <comphelper/diagnose_ex.hxx>
28
29
#include <algorithm>
30
#include <cstddef>
31
32
using namespace ::com::sun::star;
33
using namespace ::com::sun::star::chart2;
34
35
using ::com::sun::star::uno::Reference;
36
using ::com::sun::star::uno::Sequence;
37
38
#ifdef DEBUG_CHART2_TEMPLATE
39
namespace
40
{
41
void lcl_ShowDataSource( const Reference< data::XDataSource > & xSource );
42
}
43
#endif
44
45
namespace chart
46
{
47
48
DataInterpreter::DataInterpreter()
49
0
{}
50
51
DataInterpreter::~DataInterpreter()
52
0
{}
53
54
// ____ XDataInterpreter ____
55
InterpretedData DataInterpreter::interpretDataSource(
56
    const Reference< data::XDataSource >& xSource,
57
    const Sequence< beans::PropertyValue >& aArguments,
58
    const std::vector< rtl::Reference< DataSeries > >& aSeriesToReUse )
59
0
{
60
0
    if( ! xSource.is())
61
0
        return InterpretedData();
62
63
#ifdef DEBUG_CHART2_TEMPLATE
64
    lcl_ShowDataSource( xSource );
65
#endif
66
67
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aData = getDataSequences(xSource);
68
69
0
    uno::Reference< chart2::data::XLabeledDataSequence > xCategories;
70
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSequencesVec;
71
72
    // check if we should use categories
73
74
0
    bool bHasCategories( HasCategories( aArguments, aData ));
75
76
    // parse data
77
0
    bool bCategoriesUsed = false;
78
0
    for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledData : aData )
79
0
    {
80
0
        try
81
0
        {
82
0
            if( bHasCategories && ! bCategoriesUsed )
83
0
            {
84
0
                xCategories = labeledData;
85
0
                if( xCategories.is())
86
0
                    SetRole( xCategories->getValues(), u"categories"_ustr);
87
0
                bCategoriesUsed = true;
88
0
            }
89
0
            else
90
0
            {
91
0
                aSequencesVec.push_back( labeledData );
92
0
                if( labeledData.is())
93
0
                    SetRole( labeledData->getValues(), u"values-y"_ustr);
94
0
            }
95
0
        }
96
0
        catch( const uno::Exception & )
97
0
        {
98
0
            DBG_UNHANDLED_EXCEPTION("chart2");
99
0
        }
100
0
    }
101
102
    // create DataSeries
103
0
    std::size_t nSeriesIndex = 0;
104
0
    std::vector< rtl::Reference< DataSeries > > aSeriesVec;
105
0
    aSeriesVec.reserve( aSequencesVec.size());
106
107
0
    for (auto const& elem : aSequencesVec)
108
0
    {
109
0
        std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aNewData { elem };
110
0
        rtl::Reference< DataSeries > xSeries;
111
0
        if( nSeriesIndex < aSeriesToReUse.size())
112
0
            xSeries = aSeriesToReUse[nSeriesIndex];
113
0
        else
114
0
            xSeries = new DataSeries;
115
0
        assert( xSeries.is() );
116
0
        xSeries->setData( aNewData );
117
118
0
        aSeriesVec.push_back( xSeries );
119
0
        ++nSeriesIndex;
120
0
    }
121
122
0
    return { { std::move(aSeriesVec) }, xCategories };
123
0
}
124
125
InterpretedData DataInterpreter::reinterpretDataSeries(
126
    const InterpretedData& aInterpretedData )
127
0
{
128
0
    InterpretedData aResult( aInterpretedData );
129
130
0
    sal_Int32 i=0;
131
0
    std::vector< rtl::Reference< DataSeries > > aSeries( FlattenSequence( aInterpretedData.Series ));
132
0
    const sal_Int32 nCount = aSeries.size();
133
0
    for( ; i<nCount; ++i )
134
0
    {
135
0
        try
136
0
        {
137
0
            std::vector< uno::Reference< data::XLabeledDataSequence > > aNewSequences;
138
139
            // values-y
140
0
            uno::Reference< data::XLabeledDataSequence > xValuesY =
141
0
                DataSeriesHelper::getDataSequenceByRole( aSeries[i], u"values-y"_ustr );
142
            // re-use values-... as values-y
143
0
            if( ! xValuesY.is())
144
0
            {
145
0
                xValuesY =
146
0
                    DataSeriesHelper::getDataSequenceByRole( aSeries[i], u"values"_ustr, true );
147
0
                if( xValuesY.is())
148
0
                    SetRole( xValuesY->getValues(), u"values-y"_ustr);
149
0
            }
150
0
            if( xValuesY.is())
151
0
            {
152
0
                aNewSequences = { xValuesY };
153
0
            }
154
155
0
            const std::vector< uno::Reference< data::XLabeledDataSequence > > & aSeqs = aSeries[i]->getDataSequences2();
156
0
            if( aSeqs.size() != aNewSequences.size() )
157
0
            {
158
#ifdef DEBUG_CHART2_TEMPLATE
159
                sal_Int32 j=0;
160
                for( ; j<aSeqs.(); ++j )
161
                {
162
                    assert( aSeqs[j] == xValuesY && "All sequences should be used" );
163
                }
164
#endif
165
0
                aSeries[i]->setData( aNewSequences );
166
0
            }
167
0
        }
168
0
        catch( const uno::Exception & )
169
0
        {
170
0
            DBG_UNHANDLED_EXCEPTION("chart2");
171
0
        }
172
0
    }
173
174
0
    return aResult;
175
0
}
176
177
// criterion: all series must have exactly one data::XLabeledDataSequence
178
bool DataInterpreter::isDataCompatible(
179
    const InterpretedData& aInterpretedData )
180
0
{
181
0
    const std::vector< rtl::Reference< DataSeries > > aSeries( FlattenSequence( aInterpretedData.Series ));
182
0
    for( rtl::Reference< DataSeries > const & i : aSeries )
183
0
    {
184
0
        try
185
0
        {
186
0
            if( i->getDataSequences2().size() != 1 )
187
0
                return false;
188
0
        }
189
0
        catch( const uno::Exception & )
190
0
        {
191
0
            DBG_UNHANDLED_EXCEPTION("chart2");
192
0
        }
193
0
    }
194
195
0
    return true;
196
0
}
197
198
namespace
199
{
200
201
struct lcl_LabeledSequenceEquals
202
{
203
    explicit lcl_LabeledSequenceEquals( const Reference< data::XLabeledDataSequence > & xLSeqToCmp ) :
204
0
            m_bHasLabels ( false ),
205
0
            m_bHasValues ( false )
206
0
    {
207
0
        if( !xLSeqToCmp.is())
208
0
            return;
209
210
0
        Reference< data::XDataSequence > xSeq( xLSeqToCmp->getValues());
211
0
        if( xSeq.is())
212
0
        {
213
0
            m_bHasValues = true;
214
0
            m_aValuesRangeRep = xSeq->getSourceRangeRepresentation();
215
0
        }
216
217
0
        xSeq.set( xLSeqToCmp->getLabel());
218
0
        if( xSeq.is())
219
0
        {
220
0
            m_bHasLabels = true;
221
0
            m_aLabelRangeRep = xSeq->getSourceRangeRepresentation();
222
0
        }
223
0
    }
224
225
    bool operator() ( const Reference< data::XLabeledDataSequence > & xSeq )
226
0
    {
227
0
        if( ! xSeq.is())
228
0
            return false;
229
230
0
        Reference< data::XDataSequence > xSeqValues( xSeq->getValues() );
231
0
        Reference< data::XDataSequence > xSeqLabels( xSeq->getLabel() );
232
0
        bool bHasValues = xSeqValues.is();
233
0
        bool bHasLabels = xSeqLabels.is();
234
235
0
        return ( ( (m_bHasValues == bHasValues) &&
236
0
                   (!bHasValues || m_aValuesRangeRep == xSeqValues->getSourceRangeRepresentation()) ) &&
237
0
                 ( (m_bHasLabels == bHasLabels) &&
238
0
                   (!bHasLabels || m_aLabelRangeRep == xSeqLabels->getSourceRangeRepresentation()) )
239
0
            );
240
0
    }
241
242
private:
243
    bool m_bHasLabels;
244
    bool m_bHasValues;
245
    OUString m_aValuesRangeRep;
246
    OUString m_aLabelRangeRep;
247
};
248
249
} // anonymous namespace
250
251
rtl::Reference< DataSource > DataInterpreter::mergeInterpretedData(
252
    const InterpretedData& aInterpretedData )
253
0
{
254
0
    std::vector< Reference< data::XLabeledDataSequence > > aResultVec;
255
0
    aResultVec.reserve( aInterpretedData.Series.size() +
256
0
                        1 // categories
257
0
        );
258
259
0
    if( aInterpretedData.Categories.is())
260
0
        aResultVec.push_back( aInterpretedData.Categories );
261
262
0
    const std::vector< rtl::Reference< DataSeries > > aSeries = FlattenSequence( aInterpretedData.Series );
263
0
    for( rtl::Reference< DataSeries > const & dataSeries : aSeries )
264
0
    {
265
0
        try
266
0
        {
267
            // add all sequences of data series
268
0
            for( uno::Reference< data::XLabeledDataSequence > const & xAdd : dataSeries->getDataSequences2() )
269
0
            {
270
                // only add if sequence is not yet in the result
271
0
                if( none_of( aResultVec.begin(), aResultVec.end(),
272
0
                             lcl_LabeledSequenceEquals( xAdd )) )
273
0
                {
274
0
                    aResultVec.push_back( xAdd );
275
0
                }
276
0
            }
277
0
        }
278
0
        catch( const uno::Exception & )
279
0
        {
280
0
            DBG_UNHANDLED_EXCEPTION("chart2");
281
0
        }
282
0
    }
283
284
0
    return new DataSource(aResultVec);
285
0
}
286
287
uno::Any DataInterpreter::getChartTypeSpecificData(
288
    const OUString & )
289
0
{
290
0
    return uno::Any();
291
0
}
292
293
// convenience methods
294
295
OUString DataInterpreter::GetRole( const Reference< data::XDataSequence > & xSeq )
296
0
{
297
0
    OUString aResult;
298
0
    if( ! xSeq.is())
299
0
        return aResult;
300
301
0
    try
302
0
    {
303
0
        Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY_THROW );
304
0
        xProp->getPropertyValue( u"Role"_ustr) >>= aResult;
305
0
    }
306
0
    catch( const uno::Exception & )
307
0
    {
308
0
        DBG_UNHANDLED_EXCEPTION("chart2");
309
0
    }
310
0
    return aResult;
311
0
}
312
313
void DataInterpreter::SetRole( const Reference< data::XDataSequence > & xSeq, const OUString & rRole )
314
0
{
315
0
    if( ! xSeq.is())
316
0
        return;
317
0
    try
318
0
    {
319
0
        Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY_THROW );
320
0
        xProp->setPropertyValue( u"Role"_ustr, uno::Any( rRole ));
321
0
    }
322
0
    catch( const uno::Exception & )
323
0
    {
324
0
        DBG_UNHANDLED_EXCEPTION("chart2");
325
0
    }
326
0
}
327
328
uno::Any DataInterpreter::GetProperty(
329
    const Sequence< beans::PropertyValue > & aArguments,
330
    std::u16string_view rName )
331
0
{
332
0
    for( sal_Int32 i=aArguments.getLength(); i--; )
333
0
    {
334
0
        if( aArguments[i].Name == rName )
335
0
            return aArguments[i].Value;
336
0
    }
337
0
    return uno::Any();
338
0
}
339
340
bool DataInterpreter::HasCategories(
341
    const Sequence< beans::PropertyValue > & rArguments,
342
    const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & rData )
343
0
{
344
0
    bool bHasCategories = false;
345
346
0
    if( rArguments.hasElements() )
347
0
        GetProperty( rArguments, u"HasCategories" ) >>= bHasCategories;
348
349
0
    for( std::size_t nLSeqIdx=0; ! bHasCategories && nLSeqIdx<rData.size(); ++nLSeqIdx )
350
0
        bHasCategories = ( rData[nLSeqIdx].is() && GetRole( rData[nLSeqIdx]->getValues() ) == "categories");
351
352
0
    return bHasCategories;
353
0
}
354
355
bool DataInterpreter::UseCategoriesAsX( const Sequence< beans::PropertyValue > & rArguments )
356
0
{
357
0
    bool bUseCategoriesAsX = true;
358
0
    if( rArguments.hasElements() )
359
0
        GetProperty( rArguments, u"UseCategoriesAsX" ) >>= bUseCategoriesAsX;
360
0
    return bUseCategoriesAsX;
361
0
}
362
363
OUString SAL_CALL DataInterpreter::getImplementationName()
364
0
{
365
0
    return u"com.sun.star.comp.chart2.DataInterpreter"_ustr;
366
0
}
367
368
sal_Bool SAL_CALL DataInterpreter::supportsService( const OUString& rServiceName )
369
0
{
370
0
    return cppu::supportsService(this, rServiceName);
371
0
}
372
373
css::uno::Sequence< OUString > SAL_CALL DataInterpreter::getSupportedServiceNames()
374
0
{
375
0
    return { u"com.sun.star.chart2.DataInterpreter"_ustr };
376
0
}
377
378
std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > DataInterpreter::getDataSequences(
379
        const css::uno::Reference< css::chart2::data::XDataSource >& xSource)
380
0
{
381
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aData;
382
0
    for (const Reference< data::XLabeledDataSequence > & rLDS : xSource->getDataSequences() )
383
0
    {
384
0
        aData.push_back(rLDS);
385
0
    }
386
0
    return aData;
387
0
}
388
389
} // namespace chart
390
391
#ifdef DEBUG_CHART2_TEMPLATE
392
namespace
393
{
394
395
void lcl_ShowDataSource( const Reference< data::XDataSource > & xSource )
396
{
397
    if( ! xSource.is())
398
        return;
399
400
    SAL_INFO("chart2", "DataSource in DataInterpreter:" );
401
    Sequence< Reference< data::XLabeledDataSequence > > aSequences( xSource->getDataSequences());
402
    Reference< beans::XPropertySet > xProp;
403
    OUString aId;
404
    const sal_Int32 nMax = aSequences.getLength();
405
    for( sal_Int32 k = 0; k < nMax; ++k )
406
    {
407
        if( aSequences[k].is())
408
        {
409
            OUString aSourceRepr(u"<none>"_ustr);
410
            if( aSequences[k]->getValues().is())
411
                aSourceRepr = aSequences[k]->getValues()->getSourceRangeRepresentation();
412
            xProp.set( aSequences[k]->getValues(), uno::UNO_QUERY );
413
            if( xProp.is() &&
414
                ( xProp->getPropertyValue( "Role") >>= aId ))
415
            {
416
                SAL_INFO("chart2", "  <data sequence " << k << "> Role: " << aId << ", Source: "<< aSourceRepr);
417
            }
418
            else
419
            {
420
                SAL_INFO("chart2", "  <data sequence " << k << "> unknown Role, Source: " << aSourceRepr );
421
            }
422
423
            aSourceRepr = u"<none>"_ustr;
424
            if( aSequences[k]->getLabel().is())
425
                aSourceRepr = aSequences[k]->getLabel()->getSourceRangeRepresentation();
426
            xProp.set( aSequences[k]->getLabel(), uno::UNO_QUERY );
427
            if( xProp.is() &&
428
                ( xProp->getPropertyValue( "Role") >>= aId ))
429
            {
430
                SAL_INFO("chart2", "  <data sequence label " << k << "> Role: " << aId
431
                        << ", Source: " << aSourceRepr );
432
            }
433
            else
434
            {
435
                SAL_INFO("chart2", "  <data sequence label " << k << "> unknown Role, Source: " << aSourceRepr );
436
            }
437
        }
438
    }
439
}
440
441
}
442
#endif
443
444
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */