LibreOffice Module xmloff (master) 1
SchXMLTableContext.cxx
Go to the documentation of this file.
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
21
25#include <SchXMLImport.hxx>
26#include "SchXMLTools.hxx"
27#include "transporttypes.hxx"
29#include <o3tl/safeint.hxx>
30#include <o3tl/string_view.hxx>
31#include <sal/log.hxx>
33#include <xmloff/xmltoken.hxx>
36#include <comphelper/string.hxx>
37#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
38#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
39#include <com/sun/star/chart2/XChartDocument.hpp>
40#include <com/sun/star/chart2/XChartTypeContainer.hpp>
41#include <com/sun/star/chart2/XInternalDataProvider.hpp>
42#include <com/sun/star/beans/XPropertySet.hpp>
43
44#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
45
46#include <limits>
47#include <vector>
48#include <algorithm>
49#include <iterator>
50#include <string_view>
51
52using namespace com::sun::star;
53using namespace ::xmloff::token;
54using ::com::sun::star::uno::Sequence;
55using ::com::sun::star::uno::Reference;
56
57namespace
58{
59
60constexpr OUStringLiteral aCategoriesRange = u"categories";
61
62typedef ::std::multimap< OUString, OUString >
63 lcl_tOriginalRangeToInternalRangeMap;
64
65struct lcl_ApplyCellToData
66{
67 explicit lcl_ApplyCellToData( Sequence< double > & rOutData ) :
68 m_rData( rOutData ),
69 m_nIndex( 0 ),
70 m_nSize( rOutData.getLength())
71 {
72 }
73
74 void operator() ( const SchXMLCell & rCell )
75 {
76 if( m_nIndex < m_nSize )
77 {
78 auto pData = m_rData.getArray();
79 if( rCell.eType == SCH_CELL_TYPE_FLOAT )
80 pData[m_nIndex] = rCell.fValue;
81 else
82 pData[m_nIndex] = std::numeric_limits<double>::quiet_NaN();
83 }
84 ++m_nIndex;
85 }
86
87 sal_Int32 getCurrentIndex() const
88 {
89 return m_nIndex;
90 }
91
92private:
93 Sequence< double > & m_rData;
94 sal_Int32 m_nIndex;
95 sal_Int32 m_nSize;
96};
97
98void lcl_fillRangeMapping(
99 const SchXMLTable & rTable,
100 lcl_tOriginalRangeToInternalRangeMap & rOutRangeMap,
101 chart::ChartDataRowSource eDataRowSource )
102{
103 sal_Int32 nRowOffset = ( rTable.bHasHeaderRow ? 1 : 0 );
104 sal_Int32 nColOffset = ( rTable.bHasHeaderColumn ? 1 : 0 );
105
106 const OUString lcl_aCategoriesRange(aCategoriesRange);
107 static const OUStringLiteral lcl_aLabelPrefix(u"label ");
108
109 // Fill range mapping
110 const size_t nTableRowCount( rTable.aData.size());
111 for( size_t nRow = 0; nRow < nTableRowCount; ++nRow )
112 {
113 const ::std::vector< SchXMLCell > & rRow( rTable.aData[nRow] );
114 const size_t nTableColCount( rRow.size());
115 for( size_t nCol = 0; nCol < nTableColCount; ++nCol )
116 {
117 const OUString aRangeId( rRow[nCol].aRangeId );
118 if( !aRangeId.isEmpty())
119 {
120 if( eDataRowSource == chart::ChartDataRowSource_COLUMNS )
121 {
122 if( nCol == 0 && rTable.bHasHeaderColumn )
123 {
124 SAL_WARN_IF( static_cast< sal_Int32 >( nRow ) != nRowOffset, "xmloff.chart", "nRow != nRowOffset" );
125 rOutRangeMap.emplace(aRangeId, lcl_aCategoriesRange);
126 }
127 else
128 {
129 OUString aColNumStr = OUString::number( nCol - nColOffset);
130 if( nRow == 0 && rTable.bHasHeaderRow )
131 rOutRangeMap.emplace( aRangeId, lcl_aLabelPrefix + aColNumStr );
132 else
133 rOutRangeMap.emplace( aRangeId, aColNumStr );
134 }
135 }
136 else // eDataRowSource == chart::ChartDataRowSource_ROWS
137 {
138 if( nRow == 0 && rTable.bHasHeaderRow )
139 {
140 SAL_WARN_IF( static_cast< sal_Int32 >( nCol ) != nColOffset, "xmloff.chart", "nCol != nColOffset" );
141 rOutRangeMap.emplace( aRangeId, lcl_aCategoriesRange );
142 }
143 else
144 {
145 OUString aRowNumStr = OUString::number( nRow - nRowOffset);
146 if( nCol == 0 && rTable.bHasHeaderColumn )
147 rOutRangeMap.emplace( aRangeId, lcl_aLabelPrefix + aRowNumStr );
148 else
149 rOutRangeMap.emplace( aRangeId, aRowNumStr );
150 }
151 }
152 }
153 }
154 }
155}
156
157Reference< chart2::data::XDataSequence >
158 lcl_reassignDataSequence(
159 const Reference< chart2::data::XDataSequence > & xSequence,
160 const Reference< chart2::data::XDataProvider > & xDataProvider,
161 lcl_tOriginalRangeToInternalRangeMap & rRangeMap,
162 const OUString & rRange )
163{
164 Reference< chart2::data::XDataSequence > xResult( xSequence );
165 lcl_tOriginalRangeToInternalRangeMap::iterator aIt( rRangeMap.find( rRange ));
166 if( aIt != rRangeMap.end())
167 {
168 // set sequence with correct data
169 xResult.set( xDataProvider->createDataSequenceByRangeRepresentation( aIt->second ));
170 // remove translation, because it was used
171 rRangeMap.erase( aIt );
172 }
173
174 return xResult;
175}
176
177bool lcl_mapContainsRange(
178 lcl_tOriginalRangeToInternalRangeMap & rRangeMap,
179 const OUString & rRange )
180{
181 lcl_tOriginalRangeToInternalRangeMap::iterator aIt( rRangeMap.find( rRange ));
182 return ( aIt != rRangeMap.end());
183}
184
185bool lcl_tableOfRangeMatches(
186 std::u16string_view rRange,
187 std::u16string_view rTableName )
188{
189 // both strings are non-empty and the table name is part of the range
190 return ( !rRange.empty() &&
191 !rTableName.empty() &&
192 (rRange.find( rTableName ) != std::u16string_view::npos ));
193}
194
195} // anonymous namespace
196
197// class SchXMLTableContext
199 SchXMLTable& aTable ) :
200 SvXMLImportContext( rImport ),
201 mrTable( aTable ),
202 mbHasRowPermutation( false ),
203 mbHasColumnPermutation( false )
204{
207 mrTable.nRowIndex = -1;
208 mrTable.aData.clear();
209}
210
212{
213}
214
215css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableContext::createFastChildContext(
216 sal_Int32 nElement,
217 const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
218{
219 SvXMLImportContext* pContext = nullptr;
220
221 switch(nElement)
222 {
225 [[fallthrough]];
227 pContext = new SchXMLTableColumnsContext( GetImport(), mrTable );
228 break;
229
231 pContext = new SchXMLTableColumnContext( GetImport(), mrTable );
232 break;
233
235 mrTable.bHasHeaderRow = true;
236 [[fallthrough]];
238 pContext = new SchXMLTableRowsContext( GetImport(), mrTable );
239 break;
240
242 pContext = new SchXMLTableRowContext( GetImport(), mrTable );
243 break;
244 default:
245 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
246 }
247
248 return pContext;
249}
250
251void SchXMLTableContext::startFastElement (sal_Int32 /*nElement*/,
252 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
253{
254 // get table-name
255
256 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
257 {
258 switch(aIter.getToken())
259 {
261 mrTable.aTableNameOfFile = aIter.toString();
262 break;
264 if ( IsXMLToken( aIter, XML_TRUE ) )
265 {
266 mrTable.bProtected = true;
267 }
268 break;
269 default:
270 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
271 }
272 }
273}
274
276{
278 {
279 SAL_WARN_IF( mbHasRowPermutation, "xmloff.chart", "mbHasColumnPermutation is true" );
280 const auto & aPermutation( maColumnPermutation );
281 SAL_WARN_IF( !aPermutation.hasElements(), "xmloff.chart", "aPermutation is NULL");
282 if( !aPermutation.hasElements())
283 return;
284
285 // permute the values of all rows according to aPermutation
286 for( auto& rRow : mrTable.aData )
287 {
288 bool bModified = false;
289 ::std::vector< SchXMLCell > aModifiedRow;
290 const size_t nPermSize = aPermutation.size();
291 SAL_WARN_IF( static_cast< sal_Int32 >( nPermSize ) - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())), "xmloff.chart", "nPermSize - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())");
292 const size_t nRowSize = rRow.size();
293 const size_t nDestSize = ::std::min( nPermSize, nRowSize );
294 for( size_t nDestinationIndex = 0; nDestinationIndex < nDestSize; ++nDestinationIndex )
295 {
296 const size_t nSourceIndex = static_cast< size_t >( aPermutation[ nDestinationIndex ] );
297 if( nSourceIndex != nDestinationIndex &&
298 nSourceIndex < nRowSize )
299 {
300 // copy original on first real permutation
301 if( !bModified )
302 {
303 SAL_WARN_IF( !aModifiedRow.empty(), "xmloff.chart", "aModifiedRow is NOT NULL");
304 aModifiedRow.insert( aModifiedRow.end(), rRow.begin(), rRow.end() );
305 SAL_WARN_IF( aModifiedRow.empty(), "xmloff.chart", "aModifiedRow is NULL");
306 }
307 SAL_WARN_IF( nDestinationIndex >= aModifiedRow.size(), "xmloff.chart", "nDestinationIndex >= aModifiedRow.size()");
308 aModifiedRow[ nDestinationIndex ] = rRow[ nSourceIndex ];
309 bModified = true;
310 }
311 }
312 // copy back
313 if( bModified )
314 ::std::copy( aModifiedRow.begin(), aModifiedRow.end(), rRow.begin());
315 }
316 }
317 else if( mbHasRowPermutation )
318 {
319 const auto & aPermutation( maRowPermutation );
320 SAL_WARN_IF( !aPermutation.hasElements(), "xmloff.chart", "aPermutation is NULL");
321 if( !aPermutation.hasElements())
322 return;
323
324 bool bModified = false;
325 const size_t nPermSize = aPermutation.size();
326 SAL_WARN_IF( static_cast< sal_Int32 >( nPermSize ) - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())), "xmloff.chart", "nPermSize - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())");
327 const size_t nTableRowCount = mrTable.aData.size();
328 const size_t nDestSize = ::std::min( nPermSize, nTableRowCount );
329 ::std::vector< ::std::vector< SchXMLCell > > aDestination;
330 for( size_t nDestinationIndex = 0; nDestinationIndex < nDestSize; ++nDestinationIndex )
331 {
332 const size_t nSourceIndex = static_cast< size_t >( aPermutation[ nDestinationIndex ] );
333 if( nSourceIndex != nDestinationIndex &&
334 nSourceIndex < nTableRowCount )
335 {
336 // copy original on first real permutation
337 if( !bModified )
338 {
339 SAL_WARN_IF( !aDestination.empty(), "xmloff.chart", "aDestination is NOT NULL");
340 aDestination.insert( aDestination.end(), mrTable.aData.begin(), mrTable.aData.end());
341 SAL_WARN_IF( aDestination.empty(), "xmloff.chart", "aDestination is NULL");
342 }
343 SAL_WARN_IF( nDestinationIndex >= aDestination.size(), "xmloff.chart", "nDestinationIndex >= aDestination.size()");
344 aDestination[ nDestinationIndex ] = mrTable.aData[ nSourceIndex ];
345 bModified = true;
346 }
347 }
348 if( bModified )
349 {
350 // copy back
351 ::std::copy( aDestination.begin(), aDestination.end(), mrTable.aData.begin());
352 }
353 }
354}
355
356void SchXMLTableContext::setRowPermutation( const uno::Sequence< sal_Int32 > & rPermutation )
357{
358 maRowPermutation = rPermutation;
359 mbHasRowPermutation = rPermutation.hasElements();
360
362 {
364 maColumnPermutation.realloc( 0 );
365 }
366}
367
368void SchXMLTableContext::setColumnPermutation( const uno::Sequence< sal_Int32 > & rPermutation )
369{
370 maColumnPermutation = rPermutation;
371 mbHasColumnPermutation = rPermutation.hasElements();
372
374 {
375 mbHasRowPermutation = false;
376 maRowPermutation.realloc( 0 );
377 }
378}
379
380// classes for columns
381// class SchXMLTableColumnsContext
383 SvXMLImport& rImport,
384 SchXMLTable& aTable ) :
385 SvXMLImportContext( rImport ),
386 mrTable( aTable )
387{
388}
389
391{
392}
393
394css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableColumnsContext::createFastChildContext(
395 sal_Int32 nElement,
396 const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
397{
398 SvXMLImportContext* pContext = nullptr;
399
400 if( nElement == XML_ELEMENT(TABLE, XML_TABLE_COLUMN) )
401 pContext = new SchXMLTableColumnContext( GetImport(), mrTable );
402 else
403 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
404
405 return pContext;
406}
407
408// class SchXMLTableColumnContext
410 SvXMLImport& rImport,
411 SchXMLTable& aTable ) :
412 SvXMLImportContext( rImport ),
413 mrTable( aTable )
414{
415}
416
418 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
419{
420 // get number-columns-repeated attribute
421 sal_Int32 nRepeated = 1;
422 bool bHidden = false;
423
424 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
425 {
426 switch(aIter.getToken())
427 {
429 {
430 if( !aIter.isEmpty())
431 nRepeated = aIter.toInt32();
432 break;
433 }
435 {
436 OUString aVisibility = aIter.toString();
437 bHidden = aVisibility == GetXMLToken( XML_COLLAPSE );
438 break;
439 }
440 default:
441 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
442 }
443 }
444
445 sal_Int32 nOldCount = mrTable.nNumberOfColsEstimate;
446 sal_Int32 nNewCount = nOldCount + nRepeated;
447 mrTable.nNumberOfColsEstimate = nNewCount;
448
449 if( bHidden )
450 {
451 //i91578 display of hidden values (copy paste scenario; use hidden flag during migration to locale table upon paste )
452 sal_Int32 nColOffset = ( mrTable.bHasHeaderColumn ? 1 : 0 );
453 for( sal_Int32 nN = nOldCount; nN<nNewCount; nN++ )
454 {
455 sal_Int32 nHiddenColumnIndex = nN-nColOffset;
456 if( nHiddenColumnIndex>=0 )
457 mrTable.aHiddenColumns.push_back(nHiddenColumnIndex);
458 }
459 }
460}
461
463{
464}
465
466// classes for rows
467// class SchXMLTableRowsContext
469 SvXMLImport& rImport,
470 SchXMLTable& aTable ) :
471 SvXMLImportContext( rImport ),
472 mrTable( aTable )
473{
474}
475
477{
478}
479
480css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableRowsContext::createFastChildContext(
481 sal_Int32 nElement,
482 const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
483{
484 SvXMLImportContext* pContext = nullptr;
485
486 if( nElement == XML_ELEMENT(TABLE, XML_TABLE_ROW) )
487 pContext = new SchXMLTableRowContext( GetImport(), mrTable );
488 else
489 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
490
491 return pContext;
492}
493
494// class SchXMLTableRowContext
496 SvXMLImport& rImport,
497 SchXMLTable& aTable ) :
498 SvXMLImportContext( rImport ),
499 mrTable( aTable )
500{
503
504 std::vector< SchXMLCell > aNewRow;
505 aNewRow.reserve( mrTable.nNumberOfColsEstimate );
507 mrTable.aData.push_back( aNewRow );
508}
509
511{
512}
513
514css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableRowContext::createFastChildContext(
515 sal_Int32 nElement,
516 const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
517{
518 SvXMLImportContext* pContext = nullptr;
519
520 // <table:table-cell> element
521 if( nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) )
522 {
523 pContext = new SchXMLTableCellContext( GetImport(), mrTable );
524 }
525 else
526 {
527 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
528 assert(false);
529 }
530
531 return pContext;
532}
533
534namespace {
535
536class SchXMLRangeSomewhereContext : public SvXMLImportContext
537{
538//#i113950# previously the range was exported to attribute text:id,
539//but that attribute does not allow arbitrary strings anymore
540//so we need to find an alternative to save that range info for copy/paste scenario ...
541//-> use description at an empty group element for now
542
543private:
544 OUString& mrRangeString;
545 OUStringBuffer maRangeStringBuffer;
546
547public:
548 SchXMLRangeSomewhereContext( SvXMLImport& rImport,
549 OUString& rRangeString );
550
551 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
552 sal_Int32 nElement,
553 const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
554 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
555};
556
557}
558
559// classes for cells and their content
560// class SchXMLTableCellContext
562 SvXMLImport& rImport,
563 SchXMLTable& aTable)
564 : SvXMLImportContext(rImport)
565 , mrTable(aTable)
566 , mbReadText(false)
567{
568}
569
571{
572}
573
574void SchXMLTableCellContext::startFastElement (sal_Int32 /*nElement*/,
575 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
576{
577 OUString aCellContent;
579
580 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
581 {
582 switch(aIter.getToken())
583 {
585 if( IsXMLToken( aIter, XML_FLOAT ) )
586 eValueType = SCH_CELL_TYPE_FLOAT;
587 else if( IsXMLToken( aIter, XML_STRING ) )
588 eValueType = SCH_CELL_TYPE_STRING;
589 break;
590
592 aCellContent = aIter.toString();
593 break;
594
595 default:
596 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
597 }
598 }
599
600 mbReadText = true;
601 SchXMLCell aCell;
602 aCell.eType = eValueType;
603
604 if( eValueType == SCH_CELL_TYPE_FLOAT )
605 {
606 double fData;
607 // the result may be false if a NaN is read, but that's ok
608 ::sax::Converter::convertDouble( fData, aCellContent );
609
610 aCell.fValue = fData;
611 // don't read text from following <text:p> or <text:list> element
612 mbReadText = false;
613 }
614
615 mrTable.aData[ mrTable.nRowIndex ].push_back( aCell );
619}
620
621css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableCellContext::createFastChildContext(
622 sal_Int32 nElement,
623 const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
624{
625 SvXMLImportContext* pContext = nullptr;
626
627 // <text:list> element
628 if( nElement == XML_ELEMENT(TEXT, XML_LIST ) && mbReadText )
629 {
631 rCell.aComplexString = Sequence< OUString >();
633 pContext = new SchXMLTextListContext( GetImport(), rCell.aComplexString );
634 mbReadText = false;//don't apply text from <text:p>
635 }
636 // <text:p> element - read text (and range from text:id old version)
637 else if( nElement == XML_ELEMENT(TEXT, XML_P) ||
638 nElement == XML_ELEMENT(LO_EXT, XML_P) )
639 {
641 }
642 // <draw:g> element - read range
643 else if( nElement == XML_ELEMENT(DRAW, XML_G) )
644 {
645 //#i113950# previously the range was exported to attribute text:id, but that attribute does not allow arbitrary strings anymore
646 //so we need to find an alternative to save that range info for copy/paste scenario ... -> use description at an empty group element for now
647 pContext = new SchXMLRangeSomewhereContext( GetImport(), maRangeId );
648 }
649 else
650 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
651
652 return pContext;
653}
654
656{
657 if( mbReadText && !maCellContent.isEmpty() ) //apply text from <text:p> element
659 if( !maRangeId.isEmpty())
661}
662
663static void lcl_ApplyCellToComplexLabel( const SchXMLCell& rCell, Sequence< uno::Any >& rComplexLabel )
664{
665 if( rCell.eType == SCH_CELL_TYPE_STRING )
666 {
667 rComplexLabel = { uno::Any(rCell.aString) };
668 }
669 else if( rCell.aComplexString.hasElements() && rCell.eType == SCH_CELL_TYPE_COMPLEX_STRING )
670 {
671 sal_Int32 nCount = rCell.aComplexString.getLength();
672 rComplexLabel.realloc( nCount );
673 auto pComplexLabel = rComplexLabel.getArray();
674 for( sal_Int32 nN=0; nN<nCount; nN++)
675 pComplexLabel[nN] <<= (rCell.aComplexString)[nN];
676 }
677 else if( rCell.eType == SCH_CELL_TYPE_FLOAT )
678 {
679 rComplexLabel = { uno::Any(rCell.fValue) };
680 }
681}
682
684 const SchXMLTable& rTable,
685 const uno::Reference< chart2::XChartDocument >& xChartDoc )
686{
687 // apply all data read from the local table to the internal data provider
688 if( !xChartDoc.is() || !xChartDoc->hasInternalDataProvider() )
689 return;
690 Reference< chart2::data::XDataProvider > xDataProv( xChartDoc->getDataProvider() );
691 if( !xDataProv.is() )
692 return;
693
694 //prepare the read local table data
695 sal_Int32 nNumRows( static_cast< sal_Int32 >( rTable.aData.size()));
696 sal_Int32 nRowOffset = 0;
697 if( rTable.bHasHeaderRow )
698 {
699 --nNumRows;
700 nRowOffset = 1;
701 }
702 sal_Int32 nNumColumns( rTable.nMaxColumnIndex + 1 );
703 sal_Int32 nColOffset = 0;
704 if( rTable.bHasHeaderColumn )
705 {
706 --nNumColumns;
707 nColOffset = 1;
708 }
709
710 Sequence< Sequence< double > > aDataInRows( nNumRows );
711 auto aDataInRowsRange = asNonConstRange(aDataInRows);
712 Sequence< Sequence< uno::Any > > aComplexRowDescriptions( nNumRows );
713 auto aComplexRowDescriptionsRange = asNonConstRange(aComplexRowDescriptions);
714 Sequence< Sequence< uno::Any > > aComplexColumnDescriptions( nNumColumns );
715 auto aComplexColumnDescriptionsRange = asNonConstRange(aComplexColumnDescriptions);
716 for( sal_Int32 i=0; i<nNumRows; ++i )
717 aDataInRowsRange[i].realloc( nNumColumns );
718
719 if( !rTable.aData.empty() )
720 {
721 //apply column labels
722 if( rTable.bHasHeaderRow )
723 {
724 const ::std::vector< SchXMLCell >& rFirstRow = rTable.aData.front();
725 const sal_Int32 nColumnLabelsSize = aComplexColumnDescriptions.getLength();
726 const sal_Int32 nMax = ::std::min< sal_Int32 >( nColumnLabelsSize, static_cast< sal_Int32 >( rFirstRow.size()) - nColOffset );
727 SAL_WARN_IF( nMax != nColumnLabelsSize, "xmloff.chart", "nMax != nColumnLabelsSize");
728 for( sal_Int32 i=0; i<nMax; ++i )
729 lcl_ApplyCellToComplexLabel( rFirstRow[i+nColOffset], aComplexColumnDescriptionsRange[i] );
730 }
731
732 std::vector< ::std::vector< SchXMLCell > >::const_iterator aRowIter( rTable.aData.begin() + nRowOffset );
733 std::vector< ::std::vector< SchXMLCell > >::const_iterator aEnd( rTable.aData.end() );
734 for( sal_Int32 nRow = 0; aRowIter != aEnd && nRow < nNumRows; ++aRowIter, ++nRow )
735 {
736 const ::std::vector< SchXMLCell >& rRow = *aRowIter;
737 if( !rRow.empty() )
738 {
739 // row label
740 if( rTable.bHasHeaderColumn )
741 lcl_ApplyCellToComplexLabel( rRow.front(), aComplexRowDescriptionsRange[nRow] );
742
743 // values
744 Sequence< double >& rTargetRow = aDataInRowsRange[nRow];
745 auto pTargetRow = rTargetRow.getArray();
746 lcl_ApplyCellToData aApplyCellToData = ::std::for_each( rRow.begin() + nColOffset, rRow.end(), lcl_ApplyCellToData( rTargetRow ) );
747 for( sal_Int32 nCurrentIndex = aApplyCellToData.getCurrentIndex(); nCurrentIndex<nNumColumns; nCurrentIndex++ )
748 pTargetRow[nCurrentIndex] = std::numeric_limits<double>::quiet_NaN();//#i110615#
749 }
750 }
751 }
752
753 //apply the collected data to the chart
754 Reference< chart2::XAnyDescriptionAccess > xDataAccess( xDataProv, uno::UNO_QUERY );
755 if( !xDataAccess.is() )
756 return;
757
758 xDataAccess->setData( aDataInRows );
759 if( rTable.bHasHeaderColumn )
760 xDataAccess->setAnyRowDescriptions( aComplexRowDescriptions );
761 if( rTable.bHasHeaderRow )
762 xDataAccess->setAnyColumnDescriptions( aComplexColumnDescriptions );
763
764 if ( rTable.bProtected )
765 {
766 try
767 {
768 Reference< beans::XPropertySet > xProps( xChartDoc, uno::UNO_QUERY_THROW );
769 xProps->setPropertyValue( "DisableDataTableDialog", uno::Any( true ) );
770 xProps->setPropertyValue( "DisableComplexChartTypes", uno::Any( true ) );
771 }
772 catch ( uno::Exception& )
773 {
774 }
775 }
776}
777
779 const SchXMLTable& rTable,
780 const tSchXMLLSequencesPerIndex & rLSequencesPerIndex,
781 const uno::Reference< chart2::XChartDocument >& xChartDoc,
782 chart::ChartDataRowSource eDataRowSource )
783{
784 if( ! (xChartDoc.is() && xChartDoc->hasInternalDataProvider()))
785 return;
786
787 // If the range-strings are valid (starting with "local-table") they should
788 // be interpreted like given, otherwise (when the ranges refer to Calc- or
789 // Writer-ranges, but the container is not available like when pasting a
790 // chart from Calc to Impress) the range is ignored, and every object gets
791 // one table column in the order of appearance, which is: 1. categories,
792 // 2. data series: 2.a) domains, 2.b) values (main-role, usually y-values)
793
794 Reference< chart2::data::XDataProvider > xDataProv( xChartDoc->getDataProvider());
795
796 // create a mapping from original ranges to new ranges
797 lcl_tOriginalRangeToInternalRangeMap aRangeMap;
798
799 lcl_fillRangeMapping( rTable, aRangeMap, eDataRowSource );
800
801 const OUString lcl_aCategoriesRange(aCategoriesRange);
802
803 bool bCategoriesApplied = false;
804 // translate ranges (using the map created before)
805 for( const auto& rLSeq : rLSequencesPerIndex )
806 {
807 if( rLSeq.second.is())
808 {
809 // values/error bars/categories
810 if( rLSeq.first.second == SCH_XML_PART_VALUES ||
811 rLSeq.first.second == SCH_XML_PART_ERROR_BARS )
812 {
813 Reference< chart2::data::XDataSequence > xSeq( rLSeq.second->getValues());
814
815 OUString aRange;
816 if( xSeq.is() &&
817 SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) &&
818 lcl_mapContainsRange( aRangeMap, aRange ))
819 {
820 Reference< chart2::data::XDataSequence > xNewSeq(
821 lcl_reassignDataSequence( xSeq, xDataProv, aRangeMap, aRange ));
822 if( xNewSeq != xSeq )
823 {
824 SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ),
825 Reference< beans::XPropertySet >( xNewSeq, uno::UNO_QUERY ));
826 rLSeq.second->setValues( xNewSeq );
827 }
828 }
829 else
830 {
831 if( lcl_tableOfRangeMatches( aRange, rTable.aTableNameOfFile ))
832 {
833 if( rLSeq.first.first == SCH_XML_CATEGORIES_INDEX )
834 bCategoriesApplied = true;
835 }
836 else
837 {
838 if( rLSeq.first.first == SCH_XML_CATEGORIES_INDEX )
839 {
840 Reference< beans::XPropertySet > xOldSequenceProp( rLSeq.second->getValues(), uno::UNO_QUERY );
841 Reference< chart2::data::XDataSequence > xNewSequence(
842 xDataProv->createDataSequenceByRangeRepresentation("categories"));
844 xOldSequenceProp, Reference< beans::XPropertySet >( xNewSequence, uno::UNO_QUERY ));
845 rLSeq.second->setValues( xNewSequence );
846 bCategoriesApplied = true;
847 }
848 else
849 {
850 Reference< beans::XPropertySet > xOldSequenceProp( rLSeq.second->getValues(), uno::UNO_QUERY );
851 OUString aRep( OUString::number( rLSeq.first.first ));
852 Reference< chart2::data::XDataSequence > xNewSequence(
853 xDataProv->createDataSequenceByRangeRepresentation( aRep ));
855 xOldSequenceProp, Reference< beans::XPropertySet >( xNewSequence, uno::UNO_QUERY ));
856 rLSeq.second->setValues( xNewSequence );
857 }
858 }
859 }
860 }
861 else // labels
862 {
863 SAL_WARN_IF( rLSeq.first.second != SCH_XML_PART_LABEL, "xmloff.chart", "rLSeq.first.second != SCH_XML_PART_LABEL" );
864 // labels
865 Reference< chart2::data::XDataSequence > xSeq( rLSeq.second->getLabel());
866 OUString aRange;
867 if( xSeq.is() &&
868 SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) &&
869 lcl_mapContainsRange( aRangeMap, aRange ))
870 {
871 Reference< chart2::data::XDataSequence > xNewSeq(
872 lcl_reassignDataSequence( xSeq, xDataProv, aRangeMap, aRange ));
873 if( xNewSeq != xSeq )
874 {
875 SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ),
876 Reference< beans::XPropertySet >( xNewSeq, uno::UNO_QUERY ));
877 rLSeq.second->setLabel( xNewSeq );
878 }
879 }
880 else if( ! lcl_tableOfRangeMatches( aRange, rTable.aTableNameOfFile ))
881 {
882 OUString aRep = "label " + OUString::number( rLSeq.first.first );
883
884 Reference< chart2::data::XDataSequence > xNewSeq(
885 xDataProv->createDataSequenceByRangeRepresentation( aRep ));
886 SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ),
887 Reference< beans::XPropertySet >( xNewSeq, uno::UNO_QUERY ));
888 rLSeq.second->setLabel( xNewSeq );
889 }
890 }
891 }
892 }
893
894 // there exist files with own data without a categories element but with row
895 // descriptions. The row descriptions were used as categories even without
896 // the categories element
897 if( ! bCategoriesApplied )
898 {
900 xDataProv, xChartDoc, "categories",
901 0 /* nCooSysIndex */, 0 /* nDimension */ );
902 }
903
904 //i91578 display of hidden values (copy paste scenario; use hidden flag during migration to locale table upon paste )
905 //remove series that consist only of hidden columns
906 Reference< chart2::XInternalDataProvider > xInternalDataProvider( xDataProv, uno::UNO_QUERY );
907 if( !xInternalDataProvider.is() || rTable.aHiddenColumns.empty() )
908 return;
909
910 try
911 {
912 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xChartDoc->getFirstDiagram(), uno::UNO_QUERY_THROW );
913 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
914 for( const auto& rCooSys : aCooSysSeq )
915 {
916 Reference< chart2::XChartTypeContainer > xCooSysContainer( rCooSys, uno::UNO_QUERY_THROW );
917 const Sequence< Reference< chart2::XChartType > > aChartTypeSeq( xCooSysContainer->getChartTypes());
918 for( const auto& rChartType : aChartTypeSeq )
919 {
920 Reference< chart2::XDataSeriesContainer > xSeriesContainer( rChartType, uno::UNO_QUERY );
921 if(!xSeriesContainer.is())
922 continue;
923 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xSeriesContainer->getDataSeries() );
924 std::vector< Reference< chart2::XDataSeries > > aRemainingSeries;
925
926 for( const auto& rSeries : aSeriesSeq )
927 {
928 Reference< chart2::data::XDataSource > xDataSource( rSeries, uno::UNO_QUERY );
929 if( xDataSource.is() )
930 {
931 bool bHasUnhiddenColumns = false;
932 OUString aRange;
933 const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences() );
934 for( const auto& xLabeledSequence : aSequences )
935 {
936 if(!xLabeledSequence.is())
937 continue;
938 Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues() );
939 if( xValues.is() )
940 {
941 aRange = xValues->getSourceRangeRepresentation();
942 if( ::std::find( rTable.aHiddenColumns.begin(), rTable.aHiddenColumns.end(), aRange.toInt32() ) == rTable.aHiddenColumns.end() )
943 bHasUnhiddenColumns = true;
944 }
945 if( !bHasUnhiddenColumns )
946 {
947 Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel() );
948 if( xLabel.is() )
949 {
950 aRange = xLabel->getSourceRangeRepresentation();
951 const sal_Int32 nId = o3tl::toInt32(o3tl::getToken(aRange, 1, ' '));
952 if( ::std::find( rTable.aHiddenColumns.begin(), rTable.aHiddenColumns.end(), nId ) == rTable.aHiddenColumns.end() )
953 bHasUnhiddenColumns = true;
954 }
955 }
956 }
957 if( bHasUnhiddenColumns )
958 aRemainingSeries.push_back( rSeries );
959 }
960 }
961
962 if( aRemainingSeries.size() != o3tl::make_unsigned(aSeriesSeq.getLength()) )
963 {
964 //remove the series that have only hidden data
965 xSeriesContainer->setDataSeries( comphelper::containerToSequence(aRemainingSeries) );
966
967 //remove unused sequences
968 Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY );
969 if( xDataSource.is() )
970 {
971 //first detect which columns are really used
972 std::map< sal_Int32, bool > aUsageMap;
973 OUString aRange;
974 const Sequence< Reference< chart2::data::XLabeledDataSequence > > aUsedSequences( xDataSource->getDataSequences() );
975 for( const auto& xLabeledSequence : aUsedSequences )
976 {
977 if(!xLabeledSequence.is())
978 continue;
979 Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues() );
980 if( xValues.is() )
981 {
982 aRange = xValues->getSourceRangeRepresentation();
983 sal_Int32 nIndex = aRange.toInt32();
984 if( nIndex!=0 || aRange != lcl_aCategoriesRange )
985 aUsageMap[nIndex] = true;
986 }
987 Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel() );
988 if( xLabel.is() )
989 {
990 aRange = xLabel->getSourceRangeRepresentation();
991 std::u16string_view aSecondToken = o3tl::getToken(aRange, 1, ' ');
992 if( !aSecondToken.empty() )
993 aUsageMap[o3tl::toInt32(aSecondToken)] = true;
994 }
995 }
996
997 ::std::vector< sal_Int32 > aSequenceIndexesToDelete;
998 std::copy_if(rTable.aHiddenColumns.begin(), rTable.aHiddenColumns.end(),
999 std::back_inserter(aSequenceIndexesToDelete),
1000 [&aUsageMap](sal_Int32 nSequenceIndex) { return aUsageMap.find(nSequenceIndex) == aUsageMap.end(); });
1001
1002 // delete unnecessary sequences of the internal data
1003 // iterate using greatest index first, so that deletion does not
1004 // shift other sequences that will be deleted later
1005 ::std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end());
1006 for( ::std::vector< sal_Int32 >::reverse_iterator aIt(
1007 aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt )
1008 {
1009 if( *aIt != -1 )
1010 xInternalDataProvider->deleteSequence( *aIt );
1011 }
1012 }
1013 }
1014 }
1015 }
1016 }
1017 catch( const uno::Exception & )
1018 {
1019 }
1020}
1021
1022SchXMLRangeSomewhereContext::SchXMLRangeSomewhereContext( SvXMLImport& rImport,
1023 OUString& rRangeString ) :
1024 SvXMLImportContext( rImport ),
1025 mrRangeString( rRangeString )
1026{
1027}
1028
1029css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLRangeSomewhereContext::createFastChildContext(
1030 sal_Int32 nElement,
1031 const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
1032{
1033 if( nElement == XML_ELEMENT(SVG, XML_DESC)
1034 || nElement == XML_ELEMENT(SVG_COMPAT, XML_DESC) )
1035 {
1036 return new XMLStringBufferImportContext( GetImport(), maRangeStringBuffer );
1037 }
1038 else
1039 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
1040 return nullptr;
1041}
1042
1043void SchXMLRangeSomewhereContext::endFastElement(sal_Int32 )
1044{
1045 mrRangeString = maRangeStringBuffer.makeStringAndClear();
1046}
1047
1048/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const Sequence< Sequence< double > > & m_rData
static void lcl_ApplyCellToComplexLabel(const SchXMLCell &rCell, Sequence< uno::Any > &rComplexLabel)
virtual ~SchXMLTableCellContext() override
virtual void SAL_CALL endFastElement(sal_Int32 nElement) override
endFastElement is called before a context will be destructed, but after an elements context has been ...
SchXMLTableCellContext(SvXMLImport &rImport, SchXMLTable &aTable)
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &AttrList) override
virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList) override
SchXMLTableColumnContext(SvXMLImport &rImport, SchXMLTable &aTable)
virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList) override
virtual ~SchXMLTableColumnContext() override
With this context all column elements are parsed to determine the index of the column containing the ...
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &AttrList) override
SchXMLTableColumnsContext(SvXMLImport &rImport, SchXMLTable &aTable)
virtual ~SchXMLTableColumnsContext() override
SchXMLTableContext(SvXMLImport &rImport, SchXMLTable &aTable)
virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList) override
virtual void SAL_CALL endFastElement(sal_Int32 nElement) override
endFastElement is called before a context will be destructed, but after an elements context has been ...
css::uno::Sequence< sal_Int32 > maColumnPermutation
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &AttrList) override
virtual ~SchXMLTableContext() override
void setRowPermutation(const css::uno::Sequence< sal_Int32 > &rPermutation)
void setColumnPermutation(const css::uno::Sequence< sal_Int32 > &rPermutation)
css::uno::Sequence< sal_Int32 > maRowPermutation
static void applyTableToInternalDataProvider(const SchXMLTable &rTable, const css::uno::Reference< css::chart2::XChartDocument > &xChartDoc)
static void switchRangesFromOuterToInternalIfNecessary(const SchXMLTable &rTable, const tSchXMLLSequencesPerIndex &rLSequencesPerIndex, const css::uno::Reference< css::chart2::XChartDocument > &xChartDoc, css::chart::ChartDataRowSource eDataRowSource)
This function reorders local data to fit the correct data structure.
SchXMLTableRowContext(SvXMLImport &rImport, SchXMLTable &aTable)
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &AttrList) override
virtual ~SchXMLTableRowContext() override
virtual ~SchXMLTableRowsContext() override
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &AttrList) override
SchXMLTableRowsContext(SvXMLImport &rImport, SchXMLTable &aTable)
This class deliberately does not support XWeak, to improve performance when loading large documents.
Definition: xmlictxt.hxx:48
virtual void SAL_CALL endFastElement(sal_Int32 Element) override
endFastElement is called before a context will be destructed, but after an elements context has been ...
Definition: xmlictxt.cxx:40
SvXMLImport & GetImport()
Definition: xmlictxt.hxx:60
virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > &Attribs) override
Definition: xmlictxt.cxx:59
Import all text into a string buffer.
static void convertDouble(OUStringBuffer &rBuffer, double fNumber, bool bWriteUnits, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
int nCount
float u
DRAW
sal_Int32 nIndex
sal_uInt32 m_nSize
#define SAL_WARN_IF(condition, area, stream)
std::unique_ptr< sal_Int32[]> pData
void CreateCategories(const uno::Reference< chart2::data::XDataProvider > &xDataProvider, const uno::Reference< chart2::XChartDocument > &xNewDoc, const OUString &rRangeAddress, sal_Int32 nCooSysIndex, sal_Int32 nDimensionIndex, tSchXMLLSequencesPerIndex *pLSequencesPerIndex)
bool getXMLRangePropertyFromDataSequence(const Reference< chart2::data::XDataSequence > &xDataSequence, OUString &rOutXMLRange, bool bClearProp)
void copyProperties(const Reference< beans::XPropertySet > &xSource, const Reference< beans::XPropertySet > &xDestination)
double getLength(const B2DPolygon &rCandidate)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
FastAttributeList & castToFastAttributeList(const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList)
Handling of tokens in XML:
@ XML_TABLE_HEADER_COLUMNS
Definition: xmltoken.hxx:1914
@ XML_NUMBER_COLUMNS_REPEATED
Definition: xmltoken.hxx:1407
bool IsXMLToken(std::u16string_view rString, enum XMLTokenEnum eToken)
compare eToken to the string
Definition: xmltoken.cxx:3583
const OUString & GetXMLToken(enum XMLTokenEnum eToken)
return the OUString representation for eToken
Definition: xmltoken.cxx:3527
sal_Int16 nId
css::uno::Sequence< OUString > aComplexString
SchXMLCellType eType
OUString aString
sal_Int32 nColumnIndex
reflects the index of the row currently parsed
sal_Int32 nMaxColumnIndex
reflects the index of the column currently parsed
sal_Int32 nNumberOfColsEstimate
the greatest number of columns detected
::std::vector< sal_Int32 > aHiddenColumns
the table name read at the table:table element
bool bHasHeaderRow
parsing column-elements may yield an estimate
sal_Int32 nRowIndex
an array of rows containing the table contents
std::vector< std::vector< SchXMLCell > > aData
OUString aTableNameOfFile
TEXT
@ SCH_XML_PART_VALUES
@ SCH_XML_PART_ERROR_BARS
@ SCH_XML_PART_LABEL
::std::multimap< tSchXMLIndexWithPart, css::uno::Reference< css::chart2::data::XLabeledDataSequence > > tSchXMLLSequencesPerIndex
SchXMLCellType
@ SCH_CELL_TYPE_FLOAT
@ SCH_CELL_TYPE_UNKNOWN
@ SCH_CELL_TYPE_COMPLEX_STRING
@ SCH_CELL_TYPE_STRING
#define SCH_XML_CATEGORIES_INDEX
TABLE
#define XMLOFF_WARN_UNKNOWN_ELEMENT(area, token)
Definition: xmlictxt.hxx:120
#define XMLOFF_WARN_UNKNOWN(area, rIter)
Definition: xmlictxt.hxx:114
#define XML_ELEMENT(prefix, name)
Definition: xmlimp.hxx:98