1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sax/tools/converter.hxx>
22 #include "SchXMLTableContext.hxx"
23 #include "SchXMLParagraphContext.hxx"
24 #include "SchXMLTextListContext.hxx"
25 #include <SchXMLImport.hxx>
26 #include "SchXMLTools.hxx"
27 #include "transporttypes.hxx"
28 #include <XMLStringBufferImportContext.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <o3tl/string_view.hxx>
31 #include <sal/log.hxx>
32 #include <xmloff/xmlnamespace.hxx>
33 #include <xmloff/xmltoken.hxx>
34 #include <xmloff/namespacemap.hxx>
35 #include <comphelper/sequence.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>
44 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
50 #include <string_view>
52 using namespace com::sun::star
;
53 using namespace ::xmloff::token
;
54 using ::com::sun::star::uno::Sequence
;
55 using ::com::sun::star::uno::Reference
;
60 constexpr OUString aCategoriesRange
= u
"categories"_ustr
;
62 typedef ::std::multimap
< OUString
, OUString
>
63 lcl_tOriginalRangeToInternalRangeMap
;
65 struct lcl_ApplyCellToData
67 explicit lcl_ApplyCellToData( Sequence
< double > & rOutData
) :
70 m_nSize( rOutData
.getLength())
74 void operator() ( const SchXMLCell
& rCell
)
76 if( m_nIndex
< m_nSize
)
78 auto pData
= m_rData
.getArray();
79 if( rCell
.eType
== SCH_CELL_TYPE_FLOAT
)
80 pData
[m_nIndex
] = rCell
.fValue
;
82 pData
[m_nIndex
] = std::numeric_limits
<double>::quiet_NaN();
87 sal_Int32
getCurrentIndex() const
93 Sequence
< double > & m_rData
;
98 void lcl_fillRangeMapping(
99 const SchXMLTable
& rTable
,
100 lcl_tOriginalRangeToInternalRangeMap
& rOutRangeMap
,
101 chart::ChartDataRowSource eDataRowSource
)
103 sal_Int32 nRowOffset
= ( rTable
.bHasHeaderRow
? 1 : 0 );
104 sal_Int32 nColOffset
= ( rTable
.bHasHeaderColumn
? 1 : 0 );
106 const OUString
lcl_aCategoriesRange(aCategoriesRange
);
107 static constexpr OUString
lcl_aLabelPrefix(u
"label "_ustr
);
109 // Fill range mapping
110 const size_t nTableRowCount( rTable
.aData
.size());
111 for( size_t nRow
= 0; nRow
< nTableRowCount
; ++nRow
)
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
)
117 const OUString
aRangeId( rRow
[nCol
].aRangeId
);
118 if( !aRangeId
.isEmpty())
120 if( eDataRowSource
== chart::ChartDataRowSource_COLUMNS
)
122 if( nCol
== 0 && rTable
.bHasHeaderColumn
)
124 SAL_WARN_IF( static_cast< sal_Int32
>( nRow
) != nRowOffset
, "xmloff.chart", "nRow != nRowOffset" );
125 rOutRangeMap
.emplace(aRangeId
, lcl_aCategoriesRange
);
129 OUString aColNumStr
= OUString::number( nCol
- nColOffset
);
130 if( nRow
== 0 && rTable
.bHasHeaderRow
)
131 rOutRangeMap
.emplace( aRangeId
, lcl_aLabelPrefix
+ aColNumStr
);
133 rOutRangeMap
.emplace( aRangeId
, aColNumStr
);
136 else // eDataRowSource == chart::ChartDataRowSource_ROWS
138 if( nRow
== 0 && rTable
.bHasHeaderRow
)
140 SAL_WARN_IF( static_cast< sal_Int32
>( nCol
) != nColOffset
, "xmloff.chart", "nCol != nColOffset" );
141 rOutRangeMap
.emplace( aRangeId
, lcl_aCategoriesRange
);
145 OUString aRowNumStr
= OUString::number( nRow
- nRowOffset
);
146 if( nCol
== 0 && rTable
.bHasHeaderColumn
)
147 rOutRangeMap
.emplace( aRangeId
, lcl_aLabelPrefix
+ aRowNumStr
);
149 rOutRangeMap
.emplace( aRangeId
, aRowNumStr
);
157 Reference
< 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
)
164 Reference
< chart2::data::XDataSequence
> xResult( xSequence
);
165 lcl_tOriginalRangeToInternalRangeMap::iterator
aIt( rRangeMap
.find( rRange
));
166 if( aIt
!= rRangeMap
.end())
168 // set sequence with correct data
169 xResult
.set( xDataProvider
->createDataSequenceByRangeRepresentation( aIt
->second
));
170 // remove translation, because it was used
171 rRangeMap
.erase( aIt
);
177 bool lcl_mapContainsRange(
178 lcl_tOriginalRangeToInternalRangeMap
& rRangeMap
,
179 const OUString
& rRange
)
181 lcl_tOriginalRangeToInternalRangeMap::iterator
aIt( rRangeMap
.find( rRange
));
182 return ( aIt
!= rRangeMap
.end());
185 bool lcl_tableOfRangeMatches(
186 std::u16string_view rRange
,
187 std::u16string_view rTableName
)
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
));
195 } // anonymous namespace
197 // class SchXMLTableContext
198 SchXMLTableContext::SchXMLTableContext( SvXMLImport
& rImport
,
199 SchXMLTable
& aTable
) :
200 SvXMLImportContext( rImport
),
202 mbHasRowPermutation( false ),
203 mbHasColumnPermutation( false )
205 mrTable
.nColumnIndex
= -1;
206 mrTable
.nMaxColumnIndex
= -1;
207 mrTable
.nRowIndex
= -1;
208 mrTable
.aData
.clear();
211 SchXMLTableContext::~SchXMLTableContext()
215 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SchXMLTableContext::createFastChildContext(
217 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
219 SvXMLImportContext
* pContext
= nullptr;
223 case XML_ELEMENT(TABLE
, XML_TABLE_HEADER_COLUMNS
):
224 mrTable
.bHasHeaderColumn
= true;
226 case XML_ELEMENT(TABLE
, XML_TABLE_COLUMNS
):
227 pContext
= new SchXMLTableColumnsContext( GetImport(), mrTable
);
230 case XML_ELEMENT(TABLE
, XML_TABLE_COLUMN
):
231 pContext
= new SchXMLTableColumnContext( GetImport(), mrTable
);
234 case XML_ELEMENT(TABLE
, XML_TABLE_HEADER_ROWS
):
235 mrTable
.bHasHeaderRow
= true;
237 case XML_ELEMENT(TABLE
, XML_TABLE_ROWS
):
238 pContext
= new SchXMLTableRowsContext( GetImport(), mrTable
);
241 case XML_ELEMENT(TABLE
, XML_TABLE_ROW
):
242 pContext
= new SchXMLTableRowContext( GetImport(), mrTable
);
245 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
251 void SchXMLTableContext::startFastElement (sal_Int32
/*nElement*/,
252 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
256 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
258 switch(aIter
.getToken())
260 case XML_ELEMENT(TABLE
, XML_NAME
):
261 mrTable
.aTableNameOfFile
= aIter
.toString();
263 case XML_ELEMENT(TABLE
, XML_PROTECTED
):
264 if ( IsXMLToken( aIter
, XML_TRUE
) )
266 mrTable
.bProtected
= true;
270 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
275 void SchXMLTableContext::endFastElement(sal_Int32
)
277 if( mbHasColumnPermutation
)
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())
285 // permute the values of all rows according to aPermutation
286 for( auto& rRow
: mrTable
.aData
)
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
)
296 const size_t nSourceIndex
= static_cast< size_t >( aPermutation
[ nDestinationIndex
] );
297 if( nSourceIndex
!= nDestinationIndex
&&
298 nSourceIndex
< nRowSize
)
300 // copy original on first real permutation
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");
307 SAL_WARN_IF( nDestinationIndex
>= aModifiedRow
.size(), "xmloff.chart", "nDestinationIndex >= aModifiedRow.size()");
308 aModifiedRow
[ nDestinationIndex
] = rRow
[ nSourceIndex
];
314 ::std::copy( aModifiedRow
.begin(), aModifiedRow
.end(), rRow
.begin());
317 else if( mbHasRowPermutation
)
319 const auto & aPermutation( maRowPermutation
);
320 SAL_WARN_IF( !aPermutation
.hasElements(), "xmloff.chart", "aPermutation is NULL");
321 if( !aPermutation
.hasElements())
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
)
332 const size_t nSourceIndex
= static_cast< size_t >( aPermutation
[ nDestinationIndex
] );
333 if( nSourceIndex
!= nDestinationIndex
&&
334 nSourceIndex
< nTableRowCount
)
336 // copy original on first real permutation
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");
343 SAL_WARN_IF( nDestinationIndex
>= aDestination
.size(), "xmloff.chart", "nDestinationIndex >= aDestination.size()");
344 aDestination
[ nDestinationIndex
] = mrTable
.aData
[ nSourceIndex
];
351 ::std::copy( aDestination
.begin(), aDestination
.end(), mrTable
.aData
.begin());
356 void SchXMLTableContext::setRowPermutation( const uno::Sequence
< sal_Int32
> & rPermutation
)
358 maRowPermutation
= rPermutation
;
359 mbHasRowPermutation
= rPermutation
.hasElements();
361 if( mbHasRowPermutation
&& mbHasColumnPermutation
)
363 mbHasColumnPermutation
= false;
364 maColumnPermutation
.realloc( 0 );
368 void SchXMLTableContext::setColumnPermutation( const uno::Sequence
< sal_Int32
> & rPermutation
)
370 maColumnPermutation
= rPermutation
;
371 mbHasColumnPermutation
= rPermutation
.hasElements();
373 if( mbHasColumnPermutation
&& mbHasRowPermutation
)
375 mbHasRowPermutation
= false;
376 maRowPermutation
.realloc( 0 );
380 // classes for columns
381 // class SchXMLTableColumnsContext
382 SchXMLTableColumnsContext::SchXMLTableColumnsContext(
383 SvXMLImport
& rImport
,
384 SchXMLTable
& aTable
) :
385 SvXMLImportContext( rImport
),
390 SchXMLTableColumnsContext::~SchXMLTableColumnsContext()
394 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SchXMLTableColumnsContext::createFastChildContext(
396 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
398 SvXMLImportContext
* pContext
= nullptr;
400 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE_COLUMN
) )
401 pContext
= new SchXMLTableColumnContext( GetImport(), mrTable
);
403 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
408 // class SchXMLTableColumnContext
409 SchXMLTableColumnContext::SchXMLTableColumnContext(
410 SvXMLImport
& rImport
,
411 SchXMLTable
& aTable
) :
412 SvXMLImportContext( rImport
),
417 void SchXMLTableColumnContext::startFastElement (sal_Int32
/*nElement*/,
418 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
420 // get number-columns-repeated attribute
421 sal_Int32 nRepeated
= 1;
422 bool bHidden
= false;
424 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
426 switch(aIter
.getToken())
428 case XML_ELEMENT(TABLE
, XML_NUMBER_COLUMNS_REPEATED
):
430 if( !aIter
.isEmpty())
431 nRepeated
= aIter
.toInt32();
434 case XML_ELEMENT(TABLE
, XML_VISIBILITY
):
436 OUString aVisibility
= aIter
.toString();
437 bHidden
= aVisibility
== GetXMLToken( XML_COLLAPSE
);
441 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
445 sal_Int32 nOldCount
= mrTable
.nNumberOfColsEstimate
;
446 sal_Int32 nNewCount
= nOldCount
+ nRepeated
;
447 mrTable
.nNumberOfColsEstimate
= nNewCount
;
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
++ )
455 sal_Int32 nHiddenColumnIndex
= nN
-nColOffset
;
456 if( nHiddenColumnIndex
>=0 )
457 mrTable
.aHiddenColumns
.push_back(nHiddenColumnIndex
);
462 SchXMLTableColumnContext::~SchXMLTableColumnContext()
467 // class SchXMLTableRowsContext
468 SchXMLTableRowsContext::SchXMLTableRowsContext(
469 SvXMLImport
& rImport
,
470 SchXMLTable
& aTable
) :
471 SvXMLImportContext( rImport
),
476 SchXMLTableRowsContext::~SchXMLTableRowsContext()
480 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SchXMLTableRowsContext::createFastChildContext(
482 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
484 SvXMLImportContext
* pContext
= nullptr;
486 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE_ROW
) )
487 pContext
= new SchXMLTableRowContext( GetImport(), mrTable
);
489 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
494 // class SchXMLTableRowContext
495 SchXMLTableRowContext::SchXMLTableRowContext(
496 SvXMLImport
& rImport
,
497 SchXMLTable
& aTable
) :
498 SvXMLImportContext( rImport
),
501 mrTable
.nColumnIndex
= -1;
504 std::vector
< SchXMLCell
> aNewRow
;
505 aNewRow
.reserve( mrTable
.nNumberOfColsEstimate
);
506 while( mrTable
.aData
.size() <= o3tl::make_unsigned(mrTable
.nRowIndex
) )
507 mrTable
.aData
.push_back( aNewRow
);
510 SchXMLTableRowContext::~SchXMLTableRowContext()
514 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SchXMLTableRowContext::createFastChildContext(
516 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
518 SvXMLImportContext
* pContext
= nullptr;
520 // <table:table-cell> element
521 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE_CELL
) )
523 pContext
= new SchXMLTableCellContext( GetImport(), mrTable
);
527 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
536 class SchXMLRangeSomewhereContext
: public SvXMLImportContext
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
544 OUString
& mrRangeString
;
545 OUStringBuffer maRangeStringBuffer
;
548 SchXMLRangeSomewhereContext( SvXMLImport
& rImport
,
549 OUString
& rRangeString
);
551 virtual css::uno::Reference
< css::xml::sax::XFastContextHandler
> SAL_CALL
createFastChildContext(
553 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& AttrList
) override
;
554 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
559 // classes for cells and their content
560 // class SchXMLTableCellContext
561 SchXMLTableCellContext::SchXMLTableCellContext(
562 SvXMLImport
& rImport
,
564 : SvXMLImportContext(rImport
)
570 SchXMLTableCellContext::~SchXMLTableCellContext()
574 void SchXMLTableCellContext::startFastElement (sal_Int32
/*nElement*/,
575 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
577 OUString aCellContent
;
578 SchXMLCellType eValueType
= SCH_CELL_TYPE_UNKNOWN
;
580 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
582 switch(aIter
.getToken())
584 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
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
;
591 case XML_ELEMENT(OFFICE
, XML_VALUE
):
592 aCellContent
= aIter
.toString();
596 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
602 aCell
.eType
= eValueType
;
604 if( eValueType
== SCH_CELL_TYPE_FLOAT
)
607 // the result may be false if a NaN is read, but that's ok
608 ::sax::Converter::convertDouble( fData
, aCellContent
);
610 aCell
.fValue
= fData
;
611 // don't read text from following <text:p> or <text:list> element
615 mrTable
.aData
[ mrTable
.nRowIndex
].push_back( aCell
);
616 mrTable
.nColumnIndex
++;
617 if( mrTable
.nMaxColumnIndex
< mrTable
.nColumnIndex
)
618 mrTable
.nMaxColumnIndex
= mrTable
.nColumnIndex
;
621 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SchXMLTableCellContext::createFastChildContext(
623 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
625 SvXMLImportContext
* pContext
= nullptr;
627 // <text:list> element
628 if( nElement
== XML_ELEMENT(TEXT
, XML_LIST
) && mbReadText
)
630 SchXMLCell
& rCell
= mrTable
.aData
[ mrTable
.nRowIndex
][ mrTable
.nColumnIndex
];
631 rCell
.aComplexString
= Sequence
< OUString
>();
632 rCell
.eType
= SCH_CELL_TYPE_COMPLEX_STRING
;
633 pContext
= new SchXMLTextListContext( GetImport(), rCell
.aComplexString
);
634 mbReadText
= false;//don't apply text from <text:p>
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
) )
640 pContext
= new SchXMLParagraphContext( GetImport(), maCellContent
, &maRangeId
);
642 // <draw:g> element - read range
643 else if( nElement
== XML_ELEMENT(DRAW
, XML_G
) )
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
);
650 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
655 void SchXMLTableCellContext::endFastElement(sal_Int32
)
657 if( mbReadText
&& !maCellContent
.isEmpty() ) //apply text from <text:p> element
658 mrTable
.aData
[ mrTable
.nRowIndex
][ mrTable
.nColumnIndex
].aString
= maCellContent
;
659 if( !maRangeId
.isEmpty())
660 mrTable
.aData
[ mrTable
.nRowIndex
][ mrTable
.nColumnIndex
].aRangeId
= maRangeId
;
663 static void lcl_ApplyCellToComplexLabel( const SchXMLCell
& rCell
, Sequence
< uno::Any
>& rComplexLabel
)
665 if( rCell
.eType
== SCH_CELL_TYPE_STRING
)
667 rComplexLabel
= { uno::Any(rCell
.aString
) };
669 else if( rCell
.aComplexString
.hasElements() && rCell
.eType
== SCH_CELL_TYPE_COMPLEX_STRING
)
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
];
677 else if( rCell
.eType
== SCH_CELL_TYPE_FLOAT
)
679 rComplexLabel
= { uno::Any(rCell
.fValue
) };
683 void SchXMLTableHelper::applyTableToInternalDataProvider(
684 const SchXMLTable
& rTable
,
685 const uno::Reference
< chart2::XChartDocument
>& xChartDoc
)
687 // apply all data read from the local table to the internal data provider
688 if( !xChartDoc
.is() || !xChartDoc
->hasInternalDataProvider() )
690 Reference
< chart2::data::XDataProvider
> xDataProv( xChartDoc
->getDataProvider() );
691 if( !xDataProv
.is() )
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
)
702 sal_Int32
nNumColumns( rTable
.nMaxColumnIndex
+ 1 );
703 sal_Int32 nColOffset
= 0;
704 if( rTable
.bHasHeaderColumn
)
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
);
719 if( !rTable
.aData
.empty() )
721 //apply column labels
722 if( rTable
.bHasHeaderRow
)
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
] );
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
)
736 const ::std::vector
< SchXMLCell
>& rRow
= *aRowIter
;
740 if( rTable
.bHasHeaderColumn
)
741 lcl_ApplyCellToComplexLabel( rRow
.front(), aComplexRowDescriptionsRange
[nRow
] );
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#
753 //apply the collected data to the chart
754 Reference
< chart2::XAnyDescriptionAccess
> xDataAccess( xDataProv
, uno::UNO_QUERY
);
755 if( !xDataAccess
.is() )
758 xDataAccess
->setData( aDataInRows
);
759 if( rTable
.bHasHeaderColumn
)
760 xDataAccess
->setAnyRowDescriptions( aComplexRowDescriptions
);
761 if( rTable
.bHasHeaderRow
)
762 xDataAccess
->setAnyColumnDescriptions( aComplexColumnDescriptions
);
764 if ( rTable
.bProtected
)
768 Reference
< beans::XPropertySet
> xProps( xChartDoc
, uno::UNO_QUERY_THROW
);
769 xProps
->setPropertyValue( "DisableDataTableDialog", uno::Any( true ) );
770 xProps
->setPropertyValue( "DisableComplexChartTypes", uno::Any( true ) );
772 catch ( uno::Exception
& )
778 void SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary(
779 const SchXMLTable
& rTable
,
780 const tSchXMLLSequencesPerIndex
& rLSequencesPerIndex
,
781 const uno::Reference
< chart2::XChartDocument
>& xChartDoc
,
782 chart::ChartDataRowSource eDataRowSource
)
784 if( ! (xChartDoc
.is() && xChartDoc
->hasInternalDataProvider()))
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)
794 Reference
< chart2::data::XDataProvider
> xDataProv( xChartDoc
->getDataProvider());
796 // create a mapping from original ranges to new ranges
797 lcl_tOriginalRangeToInternalRangeMap aRangeMap
;
799 lcl_fillRangeMapping( rTable
, aRangeMap
, eDataRowSource
);
801 const OUString
lcl_aCategoriesRange(aCategoriesRange
);
803 bool bCategoriesApplied
= false;
804 // translate ranges (using the map created before)
805 for( const auto& rLSeq
: rLSequencesPerIndex
)
807 if( rLSeq
.second
.is())
809 // values/error bars/categories
810 if( rLSeq
.first
.second
== SCH_XML_PART_VALUES
||
811 rLSeq
.first
.second
== SCH_XML_PART_ERROR_BARS
)
813 Reference
< chart2::data::XDataSequence
> xSeq( rLSeq
.second
->getValues());
817 SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq
, aRange
, /* bClearProp = */ true ) &&
818 lcl_mapContainsRange( aRangeMap
, aRange
))
820 Reference
< chart2::data::XDataSequence
> xNewSeq(
821 lcl_reassignDataSequence( xSeq
, xDataProv
, aRangeMap
, aRange
));
822 if( xNewSeq
!= xSeq
)
824 SchXMLTools::copyProperties( Reference
< beans::XPropertySet
>( xSeq
, uno::UNO_QUERY
),
825 Reference
< beans::XPropertySet
>( xNewSeq
, uno::UNO_QUERY
));
826 rLSeq
.second
->setValues( xNewSeq
);
831 if( lcl_tableOfRangeMatches( aRange
, rTable
.aTableNameOfFile
))
833 if( rLSeq
.first
.first
== SCH_XML_CATEGORIES_INDEX
)
834 bCategoriesApplied
= true;
838 if( rLSeq
.first
.first
== SCH_XML_CATEGORIES_INDEX
)
840 Reference
< beans::XPropertySet
> xOldSequenceProp( rLSeq
.second
->getValues(), uno::UNO_QUERY
);
841 Reference
< chart2::data::XDataSequence
> xNewSequence(
842 xDataProv
->createDataSequenceByRangeRepresentation("categories"));
843 SchXMLTools::copyProperties(
844 xOldSequenceProp
, Reference
< beans::XPropertySet
>( xNewSequence
, uno::UNO_QUERY
));
845 rLSeq
.second
->setValues( xNewSequence
);
846 bCategoriesApplied
= true;
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
));
854 SchXMLTools::copyProperties(
855 xOldSequenceProp
, Reference
< beans::XPropertySet
>( xNewSequence
, uno::UNO_QUERY
));
856 rLSeq
.second
->setValues( xNewSequence
);
863 SAL_WARN_IF( rLSeq
.first
.second
!= SCH_XML_PART_LABEL
, "xmloff.chart", "rLSeq.first.second != SCH_XML_PART_LABEL" );
865 Reference
< chart2::data::XDataSequence
> xSeq( rLSeq
.second
->getLabel());
868 SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq
, aRange
, /* bClearProp = */ true ) &&
869 lcl_mapContainsRange( aRangeMap
, aRange
))
871 Reference
< chart2::data::XDataSequence
> xNewSeq(
872 lcl_reassignDataSequence( xSeq
, xDataProv
, aRangeMap
, aRange
));
873 if( xNewSeq
!= xSeq
)
875 SchXMLTools::copyProperties( Reference
< beans::XPropertySet
>( xSeq
, uno::UNO_QUERY
),
876 Reference
< beans::XPropertySet
>( xNewSeq
, uno::UNO_QUERY
));
877 rLSeq
.second
->setLabel( xNewSeq
);
880 else if( ! lcl_tableOfRangeMatches( aRange
, rTable
.aTableNameOfFile
))
882 OUString aRep
= "label " + OUString::number( rLSeq
.first
.first
);
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
);
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
)
899 SchXMLTools::CreateCategories(
900 xDataProv
, xChartDoc
, "categories",
901 0 /* nCooSysIndex */, 0 /* nDimension */ );
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() )
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
)
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
)
920 Reference
< chart2::XDataSeriesContainer
> xSeriesContainer( rChartType
, uno::UNO_QUERY
);
921 if(!xSeriesContainer
.is())
923 const Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq( xSeriesContainer
->getDataSeries() );
924 std::vector
< Reference
< chart2::XDataSeries
> > aRemainingSeries
;
926 for( const auto& rSeries
: aSeriesSeq
)
928 Reference
< chart2::data::XDataSource
> xDataSource( rSeries
, uno::UNO_QUERY
);
929 if( xDataSource
.is() )
931 bool bHasUnhiddenColumns
= false;
933 const uno::Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSequences( xDataSource
->getDataSequences() );
934 for( const auto& xLabeledSequence
: aSequences
)
936 if(!xLabeledSequence
.is())
938 Reference
< chart2::data::XDataSequence
> xValues( xLabeledSequence
->getValues() );
941 aRange
= xValues
->getSourceRangeRepresentation();
942 if( ::std::find( rTable
.aHiddenColumns
.begin(), rTable
.aHiddenColumns
.end(), aRange
.toInt32() ) == rTable
.aHiddenColumns
.end() )
943 bHasUnhiddenColumns
= true;
945 if( !bHasUnhiddenColumns
)
947 Reference
< chart2::data::XDataSequence
> xLabel( xLabeledSequence
->getLabel() );
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;
957 if( bHasUnhiddenColumns
)
958 aRemainingSeries
.push_back( rSeries
);
962 if( aRemainingSeries
.size() != o3tl::make_unsigned(aSeriesSeq
.getLength()) )
964 //remove the series that have only hidden data
965 xSeriesContainer
->setDataSeries( comphelper::containerToSequence(aRemainingSeries
) );
967 //remove unused sequences
968 Reference
< chart2::data::XDataSource
> xDataSource( xChartDoc
, uno::UNO_QUERY
);
969 if( xDataSource
.is() )
971 //first detect which columns are really used
972 std::map
< sal_Int32
, bool > aUsageMap
;
974 const Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aUsedSequences( xDataSource
->getDataSequences() );
975 for( const auto& xLabeledSequence
: aUsedSequences
)
977 if(!xLabeledSequence
.is())
979 Reference
< chart2::data::XDataSequence
> xValues( xLabeledSequence
->getValues() );
982 aRange
= xValues
->getSourceRangeRepresentation();
983 sal_Int32 nIndex
= aRange
.toInt32();
984 if( nIndex
!=0 || aRange
!= lcl_aCategoriesRange
)
985 aUsageMap
[nIndex
] = true;
987 Reference
< chart2::data::XDataSequence
> xLabel( xLabeledSequence
->getLabel() );
990 aRange
= xLabel
->getSourceRangeRepresentation();
991 std::u16string_view aSecondToken
= o3tl::getToken(aRange
, 1, ' ');
992 if( !aSecondToken
.empty() )
993 aUsageMap
[o3tl::toInt32(aSecondToken
)] = true;
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(); });
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
)
1010 xInternalDataProvider
->deleteSequence( *aIt
);
1017 catch( const uno::Exception
& )
1022 SchXMLRangeSomewhereContext::SchXMLRangeSomewhereContext( SvXMLImport
& rImport
,
1023 OUString
& rRangeString
) :
1024 SvXMLImportContext( rImport
),
1025 mrRangeString( rRangeString
)
1029 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SchXMLRangeSomewhereContext::createFastChildContext(
1031 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
1033 if( nElement
== XML_ELEMENT(SVG
, XML_DESC
)
1034 || nElement
== XML_ELEMENT(SVG_COMPAT
, XML_DESC
) )
1036 return new XMLStringBufferImportContext( GetImport(), maRangeStringBuffer
);
1039 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
1043 void SchXMLRangeSomewhereContext::endFastElement(sal_Int32
)
1045 mrRangeString
= maRangeStringBuffer
.makeStringAndClear();
1048 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */