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 <rtl/math.hxx>
30 #include <xmloff/xmlnmspe.hxx>
31 #include <xmloff/xmltoken.hxx>
32 #include <xmloff/nmspmap.hxx>
33 #include <comphelper/sequence.hxx>
34 #include <com/sun/star/frame/XModel.hpp>
35 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
36 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
37 #include <com/sun/star/chart2/XChartDocument.hpp>
38 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
39 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
40 #include <com/sun/star/chart/ChartSeriesAddress.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/beans/XPropertySetInfo.hpp>
43 #include <com/sun/star/beans/PropertyAttribute.hpp>
45 #include <com/sun/star/chart2/XDiagram.hpp>
46 #include <com/sun/star/chart2/XAxis.hpp>
47 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
48 #include <com/sun/star/chart2/AxisType.hpp>
54 using namespace com::sun::star
;
55 using namespace ::xmloff::token
;
56 using ::com::sun::star::uno::Sequence
;
57 using ::com::sun::star::uno::Reference
;
62 const char aLabelPrefix
[] = "label ";
63 const char aCategoriesRange
[] = "categories";
65 typedef ::std::multimap
< OUString
, OUString
>
66 lcl_tOriginalRangeToInternalRangeMap
;
68 struct lcl_ApplyCellToData
: public ::std::unary_function
< SchXMLCell
, void >
70 explicit lcl_ApplyCellToData( Sequence
< double > & rOutData
) :
73 m_nSize( rOutData
.getLength()),
76 ::rtl::math::setNan( &m_fNaN
);
79 void operator() ( const SchXMLCell
& rCell
)
81 if( m_nIndex
< m_nSize
)
83 if( rCell
.eType
== SCH_CELL_TYPE_FLOAT
)
84 m_rData
[m_nIndex
] = rCell
.fValue
;
86 m_rData
[m_nIndex
] = m_fNaN
;
91 sal_Int32
getCurrentIndex() const
97 Sequence
< double > & m_rData
;
103 void lcl_fillRangeMapping(
104 const SchXMLTable
& rTable
,
105 lcl_tOriginalRangeToInternalRangeMap
& rOutRangeMap
,
106 chart::ChartDataRowSource eDataRowSource
)
108 sal_Int32 nRowOffset
= ( rTable
.bHasHeaderRow
? 1 : 0 );
109 sal_Int32 nColOffset
= ( rTable
.bHasHeaderColumn
? 1 : 0 );
111 const OUString
lcl_aCategoriesRange(aCategoriesRange
);
112 const OUString
lcl_aLabelPrefix(aLabelPrefix
);
114 // Fill range mapping
115 const size_t nTableRowCount( rTable
.aData
.size());
116 for( size_t nRow
= 0; nRow
< nTableRowCount
; ++nRow
)
118 const ::std::vector
< SchXMLCell
> & rRow( rTable
.aData
[nRow
] );
119 const size_t nTableColCount( rRow
.size());
120 for( size_t nCol
= 0; nCol
< nTableColCount
; ++nCol
)
122 const OUString
aRangeId( rRow
[nCol
].aRangeId
);
123 if( !aRangeId
.isEmpty())
125 if( eDataRowSource
== chart::ChartDataRowSource_COLUMNS
)
127 if( nCol
== 0 && rTable
.bHasHeaderColumn
)
129 SAL_WARN_IF( static_cast< sal_Int32
>( nRow
) != nRowOffset
, "xmloff.chart", "nRow != nRowOffset" );
130 rOutRangeMap
.insert( lcl_tOriginalRangeToInternalRangeMap::value_type(
131 aRangeId
, lcl_aCategoriesRange
));
135 OUString aColNumStr
= OUString::number( nCol
- nColOffset
);
136 if( nRow
== 0 && rTable
.bHasHeaderRow
)
137 rOutRangeMap
.insert( lcl_tOriginalRangeToInternalRangeMap::value_type(
138 aRangeId
, lcl_aLabelPrefix
+ aColNumStr
));
140 rOutRangeMap
.insert( lcl_tOriginalRangeToInternalRangeMap::value_type(
141 aRangeId
, aColNumStr
));
144 else // eDataRowSource == chart::ChartDataRowSource_ROWS
146 if( nRow
== 0 && rTable
.bHasHeaderRow
)
148 SAL_WARN_IF( static_cast< sal_Int32
>( nCol
) != nColOffset
, "xmloff.chart", "nCol != nColOffset" );
149 rOutRangeMap
.insert( lcl_tOriginalRangeToInternalRangeMap::value_type(
150 aRangeId
, lcl_aCategoriesRange
));
154 OUString aRowNumStr
= OUString::number( nRow
- nRowOffset
);
155 if( nCol
== 0 && rTable
.bHasHeaderColumn
)
156 rOutRangeMap
.insert( lcl_tOriginalRangeToInternalRangeMap::value_type(
157 aRangeId
, lcl_aLabelPrefix
+ aRowNumStr
));
159 rOutRangeMap
.insert( lcl_tOriginalRangeToInternalRangeMap::value_type(
160 aRangeId
, aRowNumStr
));
168 Reference
< chart2::data::XDataSequence
>
169 lcl_reassignDataSequence(
170 const Reference
< chart2::data::XDataSequence
> & xSequence
,
171 const Reference
< chart2::data::XDataProvider
> & xDataProvider
,
172 lcl_tOriginalRangeToInternalRangeMap
& rRangeMap
,
173 const OUString
& rRange
)
175 Reference
< chart2::data::XDataSequence
> xResult( xSequence
);
176 lcl_tOriginalRangeToInternalRangeMap::iterator
aIt( rRangeMap
.find( rRange
));
177 if( aIt
!= rRangeMap
.end())
179 // set sequence with correct data
180 xResult
.set( xDataProvider
->createDataSequenceByRangeRepresentation( aIt
->second
));
181 // remove translation, because it was used
182 rRangeMap
.erase( aIt
);
188 bool lcl_mapContainsRange(
189 lcl_tOriginalRangeToInternalRangeMap
& rRangeMap
,
190 const OUString
& rRange
)
192 lcl_tOriginalRangeToInternalRangeMap::iterator
aIt( rRangeMap
.find( rRange
));
193 return ( aIt
!= rRangeMap
.end());
196 bool lcl_tableOfRangeMatches(
197 const OUString
& rRange
,
198 const OUString
& rTableName
)
200 // both strings are non-empty and the table name is part of the range
201 return ( !rRange
.isEmpty() &&
202 !rTableName
.isEmpty() &&
203 (rRange
.indexOf( rTableName
) != -1 ));
206 template< typename T
>
207 ::std::vector
< T
> lcl_SequenceToVector( const uno::Sequence
< T
> & rSequence
)
209 ::std::vector
< T
> aResult( rSequence
.getLength());
210 ::std::copy( rSequence
.begin(), rSequence
.end(), aResult
.begin());
214 } // anonymous namespace
216 // class SchXMLTableContext
217 SchXMLTableContext::SchXMLTableContext( SchXMLImportHelper
& rImpHelper
,
218 SvXMLImport
& rImport
,
219 const OUString
& rLName
,
220 SchXMLTable
& aTable
) :
221 SvXMLImportContext( rImport
, XML_NAMESPACE_TABLE
, rLName
),
222 mrImportHelper( rImpHelper
),
224 mbHasRowPermutation( false ),
225 mbHasColumnPermutation( false )
227 mrTable
.nColumnIndex
= -1;
228 mrTable
.nMaxColumnIndex
= -1;
229 mrTable
.nRowIndex
= -1;
230 mrTable
.aData
.clear();
233 SchXMLTableContext::~SchXMLTableContext()
237 SvXMLImportContext
*SchXMLTableContext::CreateChildContext(
239 const OUString
& rLocalName
,
240 const uno::Reference
< xml::sax::XAttributeList
>& )
242 SvXMLImportContext
* pContext
= nullptr;
243 const SvXMLTokenMap
& rTokenMap
= mrImportHelper
.GetTableElemTokenMap();
245 switch( rTokenMap
.Get( nPrefix
, rLocalName
))
247 case XML_TOK_TABLE_HEADER_COLS
:
248 mrTable
.bHasHeaderColumn
= true;
250 case XML_TOK_TABLE_COLUMNS
:
251 pContext
= new SchXMLTableColumnsContext( GetImport(), rLocalName
, mrTable
);
254 case XML_TOK_TABLE_COLUMN
:
255 pContext
= new SchXMLTableColumnContext( GetImport(), rLocalName
, mrTable
);
258 case XML_TOK_TABLE_HEADER_ROWS
:
259 mrTable
.bHasHeaderRow
= true;
261 case XML_TOK_TABLE_ROWS
:
262 pContext
= new SchXMLTableRowsContext( mrImportHelper
, GetImport(), rLocalName
, mrTable
);
265 case XML_TOK_TABLE_ROW
:
266 pContext
= new SchXMLTableRowContext( mrImportHelper
, GetImport(), rLocalName
, mrTable
);
270 pContext
= new SvXMLImportContext( GetImport(), nPrefix
, rLocalName
);
276 void SchXMLTableContext::StartElement( const uno::Reference
< xml::sax::XAttributeList
>& xAttrList
)
279 sal_Int16 nAttrCount
= xAttrList
.is()? xAttrList
->getLength(): 0;
281 for( sal_Int16 i
= 0; i
< nAttrCount
; i
++ )
283 OUString sAttrName
= xAttrList
->getNameByIndex( i
);
285 sal_uInt16 nPrefix
= GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName
, &aLocalName
);
286 if ( nPrefix
== XML_NAMESPACE_TABLE
)
288 if ( IsXMLToken( aLocalName
, XML_NAME
) )
290 mrTable
.aTableNameOfFile
= xAttrList
->getValueByIndex( i
);
292 else if ( IsXMLToken( aLocalName
, XML_PROTECTED
) )
294 if ( IsXMLToken( xAttrList
->getValueByIndex( i
), XML_TRUE
) )
296 mrTable
.bProtected
= true;
303 void SchXMLTableContext::EndElement()
305 if( mbHasColumnPermutation
)
307 SAL_WARN_IF( mbHasRowPermutation
, "xmloff.chart", "mbHasColumnPermutation is true" );
308 ::std::vector
< sal_Int32
> aPermutation( lcl_SequenceToVector( maColumnPermutation
));
309 SAL_WARN_IF( aPermutation
.empty(), "xmloff.chart", "aPermutation is NULL");
310 if( aPermutation
.empty())
313 // permute the values of all rows according to aPermutation
314 for( ::std::vector
< ::std::vector
< SchXMLCell
> >::iterator
aRowIt( mrTable
.aData
.begin());
315 aRowIt
!= mrTable
.aData
.end(); ++aRowIt
)
317 bool bModified
= false;
318 ::std::vector
< SchXMLCell
> aModifiedRow
;
319 const size_t nPermSize
= aPermutation
.size();
320 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())");
321 const size_t nRowSize
= aRowIt
->size();
322 const size_t nDestSize
= ::std::min( nPermSize
, nRowSize
);
323 for( size_t nDestinationIndex
= 0; nDestinationIndex
< nDestSize
; ++nDestinationIndex
)
325 const size_t nSourceIndex
= static_cast< size_t >( aPermutation
[ nDestinationIndex
] );
326 if( nSourceIndex
!= nDestinationIndex
&&
327 nSourceIndex
< nRowSize
)
329 // copy original on first real permutation
332 SAL_WARN_IF( !aModifiedRow
.empty(), "xmloff.chart", "aModifiedRow is NOT NULL");
333 aModifiedRow
.reserve( aRowIt
->size());
334 ::std::copy( aRowIt
->begin(), aRowIt
->end(), ::std::back_inserter( aModifiedRow
));
335 SAL_WARN_IF( aModifiedRow
.empty(), "xmloff.chart", "aModifiedRow is NULL");
337 SAL_WARN_IF( nDestinationIndex
>= aModifiedRow
.size(), "xmloff.chart", "nDestinationIndex >= aModifiedRow.size()");
338 aModifiedRow
[ nDestinationIndex
] = (*aRowIt
)[ nSourceIndex
];
344 ::std::copy( aModifiedRow
.begin(), aModifiedRow
.end(), aRowIt
->begin());
347 else if( mbHasRowPermutation
)
349 ::std::vector
< sal_Int32
> aPermutation( lcl_SequenceToVector( maRowPermutation
));
350 SAL_WARN_IF( aPermutation
.empty(), "xmloff.chart", "aPermutation is NULL");
351 if( aPermutation
.empty())
354 bool bModified
= false;
355 const size_t nPermSize
= aPermutation
.size();
356 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())");
357 const size_t nTableRowCount
= mrTable
.aData
.size();
358 const size_t nDestSize
= ::std::min( nPermSize
, nTableRowCount
);
359 ::std::vector
< ::std::vector
< SchXMLCell
> > aDestination
;
360 for( size_t nDestinationIndex
= 0; nDestinationIndex
< nDestSize
; ++nDestinationIndex
)
362 const size_t nSourceIndex
= static_cast< size_t >( aPermutation
[ nDestinationIndex
] );
363 if( nSourceIndex
!= nDestinationIndex
&&
364 nSourceIndex
< nTableRowCount
)
366 // copy original on first real permutation
369 SAL_WARN_IF( !aDestination
.empty(), "xmloff.chart", "aDestination is NOT NULL");
370 aDestination
.reserve( mrTable
.aData
.size());
371 ::std::copy( mrTable
.aData
.begin(), mrTable
.aData
.end(), ::std::back_inserter( aDestination
));
372 SAL_WARN_IF( aDestination
.empty(), "xmloff.chart", "aDestination is NULL");
374 SAL_WARN_IF( nDestinationIndex
>= aDestination
.size(), "xmloff.chart", "nDestinationIndex >= aDestination.size()");
375 aDestination
[ nDestinationIndex
] = mrTable
.aData
[ nSourceIndex
];
382 ::std::copy( aDestination
.begin(), aDestination
.end(), mrTable
.aData
.begin());
387 void SchXMLTableContext::setRowPermutation( const uno::Sequence
< sal_Int32
> & rPermutation
)
389 maRowPermutation
= rPermutation
;
390 mbHasRowPermutation
= ( rPermutation
.getLength() > 0 );
392 if( mbHasRowPermutation
&& mbHasColumnPermutation
)
394 mbHasColumnPermutation
= false;
395 maColumnPermutation
.realloc( 0 );
399 void SchXMLTableContext::setColumnPermutation( const uno::Sequence
< sal_Int32
> & rPermutation
)
401 maColumnPermutation
= rPermutation
;
402 mbHasColumnPermutation
= ( rPermutation
.getLength() > 0 );
404 if( mbHasColumnPermutation
&& mbHasRowPermutation
)
406 mbHasRowPermutation
= false;
407 maRowPermutation
.realloc( 0 );
411 // classes for columns
412 // class SchXMLTableColumnsContext
413 SchXMLTableColumnsContext::SchXMLTableColumnsContext(
414 SvXMLImport
& rImport
,
415 const OUString
& rLocalName
,
416 SchXMLTable
& aTable
) :
417 SvXMLImportContext( rImport
, XML_NAMESPACE_TABLE
, rLocalName
),
422 SchXMLTableColumnsContext::~SchXMLTableColumnsContext()
426 SvXMLImportContext
* SchXMLTableColumnsContext::CreateChildContext(
428 const OUString
& rLocalName
,
429 const uno::Reference
< xml::sax::XAttributeList
>& )
431 SvXMLImportContext
* pContext
= nullptr;
433 if( nPrefix
== XML_NAMESPACE_TABLE
&&
434 IsXMLToken( rLocalName
, XML_TABLE_COLUMN
) )
436 pContext
= new SchXMLTableColumnContext( GetImport(), rLocalName
, mrTable
);
439 pContext
= new SvXMLImportContext( GetImport(), nPrefix
, rLocalName
);
444 // class SchXMLTableColumnContext
445 SchXMLTableColumnContext::SchXMLTableColumnContext(
446 SvXMLImport
& rImport
,
447 const OUString
& rLocalName
,
448 SchXMLTable
& aTable
) :
449 SvXMLImportContext( rImport
, XML_NAMESPACE_TABLE
, rLocalName
),
454 void SchXMLTableColumnContext::StartElement( const uno::Reference
< xml::sax::XAttributeList
>& xAttrList
)
456 // get number-columns-repeated attribute
457 sal_Int16 nAttrCount
= xAttrList
.is()? xAttrList
->getLength(): 0;
458 sal_Int32 nRepeated
= 1;
459 bool bHidden
= false;
461 for( sal_Int16 i
= 0; i
< nAttrCount
; i
++ )
463 OUString sAttrName
= xAttrList
->getNameByIndex( i
);
465 sal_uInt16 nPrefix
= GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName
, &aLocalName
);
467 if( nPrefix
== XML_NAMESPACE_TABLE
&&
468 IsXMLToken( aLocalName
, XML_NUMBER_COLUMNS_REPEATED
) )
470 OUString aValue
= xAttrList
->getValueByIndex( i
);
471 if( !aValue
.isEmpty())
472 nRepeated
= aValue
.toInt32();
474 else if( nPrefix
== XML_NAMESPACE_TABLE
&&
475 IsXMLToken( aLocalName
, XML_VISIBILITY
) )
477 OUString aVisibility
= xAttrList
->getValueByIndex( i
);
478 bHidden
= aVisibility
.equals( GetXMLToken( XML_COLLAPSE
) );
482 sal_Int32 nOldCount
= mrTable
.nNumberOfColsEstimate
;
483 sal_Int32 nNewCount
= nOldCount
+ nRepeated
;
484 mrTable
.nNumberOfColsEstimate
= nNewCount
;
488 //i91578 display of hidden values (copy paste scenario; use hidden flag during migration to locale table upon paste )
489 sal_Int32 nColOffset
= ( mrTable
.bHasHeaderColumn
? 1 : 0 );
490 for( sal_Int32 nN
= nOldCount
; nN
<nNewCount
; nN
++ )
492 sal_Int32 nHiddenColumnIndex
= nN
-nColOffset
;
493 if( nHiddenColumnIndex
>=0 )
494 mrTable
.aHiddenColumns
.push_back(nHiddenColumnIndex
);
499 SchXMLTableColumnContext::~SchXMLTableColumnContext()
504 // class SchXMLTableRowsContext
505 SchXMLTableRowsContext::SchXMLTableRowsContext(
506 SchXMLImportHelper
& rImpHelper
,
507 SvXMLImport
& rImport
,
508 const OUString
& rLocalName
,
509 SchXMLTable
& aTable
) :
510 SvXMLImportContext( rImport
, XML_NAMESPACE_TABLE
, rLocalName
),
511 mrImportHelper( rImpHelper
),
516 SchXMLTableRowsContext::~SchXMLTableRowsContext()
520 SvXMLImportContext
* SchXMLTableRowsContext::CreateChildContext(
522 const OUString
& rLocalName
,
523 const uno::Reference
< xml::sax::XAttributeList
>& )
525 SvXMLImportContext
* pContext
= nullptr;
527 if( nPrefix
== XML_NAMESPACE_TABLE
&&
528 IsXMLToken( rLocalName
, XML_TABLE_ROW
) )
530 pContext
= new SchXMLTableRowContext( mrImportHelper
, GetImport(), rLocalName
, mrTable
);
534 pContext
= new SvXMLImportContext( GetImport(), nPrefix
, rLocalName
);
540 // class SchXMLTableRowContext
541 SchXMLTableRowContext::SchXMLTableRowContext(
542 SchXMLImportHelper
& rImpHelper
,
543 SvXMLImport
& rImport
,
544 const OUString
& rLocalName
,
545 SchXMLTable
& aTable
) :
546 SvXMLImportContext( rImport
, XML_NAMESPACE_TABLE
, rLocalName
),
547 mrImportHelper( rImpHelper
),
550 mrTable
.nColumnIndex
= -1;
553 std::vector
< SchXMLCell
> aNewRow
;
554 aNewRow
.reserve( mrTable
.nNumberOfColsEstimate
);
555 while( mrTable
.aData
.size() <= (unsigned long)mrTable
.nRowIndex
)
556 mrTable
.aData
.push_back( aNewRow
);
559 SchXMLTableRowContext::~SchXMLTableRowContext()
563 SvXMLImportContext
* SchXMLTableRowContext::CreateChildContext(
565 const OUString
& rLocalName
,
566 const uno::Reference
< xml::sax::XAttributeList
>& )
568 SvXMLImportContext
* pContext
= nullptr;
570 // <table:table-cell> element
571 if( nPrefix
== XML_NAMESPACE_TABLE
&&
572 IsXMLToken(rLocalName
, XML_TABLE_CELL
) )
574 pContext
= new SchXMLTableCellContext( mrImportHelper
, GetImport(), rLocalName
, mrTable
);
578 pContext
= new SvXMLImportContext( GetImport(), nPrefix
, rLocalName
);
584 class SchXMLRangeSomewhereContext
: public SvXMLImportContext
586 //#i113950# previously the range was exported to attribute text:id,
587 //but that attribute does not allow arbitrary strings anymore
588 //so we need to find an alternative to save that range info for copy/paste scenario ...
589 //-> use description at an empty group element for now
592 OUString
& mrRangeString
;
593 OUStringBuffer maRangeStringBuffer
;
596 SchXMLRangeSomewhereContext( SvXMLImport
& rImport
,
598 const OUString
& rLocalName
,
599 OUString
& rRangeString
);
600 virtual ~SchXMLRangeSomewhereContext();
602 virtual SvXMLImportContext
* CreateChildContext(
604 const OUString
& rLocalName
,
605 const css::uno::Reference
< css::xml::sax::XAttributeList
>& xAttrList
) override
;
606 virtual void EndElement() override
;
609 // classes for cells and their content
610 // class SchXMLTableCellContext
611 SchXMLTableCellContext::SchXMLTableCellContext(
612 SchXMLImportHelper
& rImpHelper
, SvXMLImport
& rImport
,
613 const OUString
& rLocalName
, SchXMLTable
& aTable
)
614 : SvXMLImportContext(rImport
, XML_NAMESPACE_TABLE
, rLocalName
)
615 , mrImportHelper(rImpHelper
)
621 SchXMLTableCellContext::~SchXMLTableCellContext()
625 void SchXMLTableCellContext::StartElement( const uno::Reference
< xml::sax::XAttributeList
>& xAttrList
)
627 sal_Int16 nAttrCount
= xAttrList
.is()? xAttrList
->getLength(): 0;
630 OUString aCellContent
;
631 SchXMLCellType eValueType
= SCH_CELL_TYPE_UNKNOWN
;
632 const SvXMLTokenMap
& rAttrTokenMap
= mrImportHelper
.GetCellAttrTokenMap();
634 for( sal_Int16 i
= 0; i
< nAttrCount
; i
++ )
636 OUString sAttrName
= xAttrList
->getNameByIndex( i
);
637 sal_uInt16 nPrefix
= GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName
, &aLocalName
);
639 switch( rAttrTokenMap
.Get( nPrefix
, aLocalName
))
641 case XML_TOK_CELL_VAL_TYPE
:
642 aValue
= xAttrList
->getValueByIndex( i
);
643 if( IsXMLToken( aValue
, XML_FLOAT
) )
644 eValueType
= SCH_CELL_TYPE_FLOAT
;
645 else if( IsXMLToken( aValue
, XML_STRING
) )
646 eValueType
= SCH_CELL_TYPE_STRING
;
649 case XML_TOK_CELL_VALUE
:
650 aCellContent
= xAttrList
->getValueByIndex( i
);
657 aCell
.eType
= eValueType
;
659 if( eValueType
== SCH_CELL_TYPE_FLOAT
)
662 // the result may be false if a NaN is read, but that's ok
663 ::sax::Converter::convertDouble( fData
, aCellContent
);
665 aCell
.fValue
= fData
;
666 // don't read text from following <text:p> or <text:list> element
670 mrTable
.aData
[ mrTable
.nRowIndex
].push_back( aCell
);
671 mrTable
.nColumnIndex
++;
672 if( mrTable
.nMaxColumnIndex
< mrTable
.nColumnIndex
)
673 mrTable
.nMaxColumnIndex
= mrTable
.nColumnIndex
;
676 SvXMLImportContext
* SchXMLTableCellContext::CreateChildContext(
678 const OUString
& rLocalName
,
679 const uno::Reference
< xml::sax::XAttributeList
>& )
681 SvXMLImportContext
* pContext
= nullptr;
683 // <text:list> element
684 if( nPrefix
== XML_NAMESPACE_TEXT
&& IsXMLToken( rLocalName
, XML_LIST
) && mbReadText
)
686 SchXMLCell
& rCell
= mrTable
.aData
[ mrTable
.nRowIndex
][ mrTable
.nColumnIndex
];
687 rCell
.aComplexString
= Sequence
< OUString
>();
688 rCell
.eType
= SCH_CELL_TYPE_COMPLEX_STRING
;
689 pContext
= new SchXMLTextListContext( GetImport(), rLocalName
, rCell
.aComplexString
);
690 mbReadText
= false;//don't apply text from <text:p>
692 // <text:p> element - read text (and range from text:id old version)
693 else if( (nPrefix
== XML_NAMESPACE_TEXT
||
694 nPrefix
== XML_NAMESPACE_LO_EXT
) && IsXMLToken( rLocalName
, XML_P
) )
696 pContext
= new SchXMLParagraphContext( GetImport(), rLocalName
, maCellContent
, &maRangeId
);
698 // <draw:g> element - read range
699 else if( nPrefix
== XML_NAMESPACE_DRAW
&& IsXMLToken( rLocalName
, XML_G
) )
701 //#i113950# previously the range was exported to attribute text:id, but that attribute does not allow arbitrary strings anymore
702 //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
703 pContext
= new SchXMLRangeSomewhereContext( GetImport(), nPrefix
, rLocalName
, maRangeId
);
707 pContext
= new SvXMLImportContext( GetImport(), nPrefix
, rLocalName
);
713 void SchXMLTableCellContext::EndElement()
715 if( mbReadText
&& !maCellContent
.isEmpty() ) //apply text from <text:p> element
716 mrTable
.aData
[ mrTable
.nRowIndex
][ mrTable
.nColumnIndex
].aString
= maCellContent
;
717 if( !maRangeId
.isEmpty())
718 mrTable
.aData
[ mrTable
.nRowIndex
][ mrTable
.nColumnIndex
].aRangeId
= maRangeId
;
721 static void lcl_ApplyCellToComplexLabel( const SchXMLCell
& rCell
, Sequence
< uno::Any
>& rComplexLabel
)
723 if( rCell
.eType
== SCH_CELL_TYPE_STRING
)
725 rComplexLabel
.realloc(1);
726 rComplexLabel
[0] = uno::makeAny( rCell
.aString
);
728 else if( rCell
.aComplexString
.getLength() && rCell
.eType
== SCH_CELL_TYPE_COMPLEX_STRING
)
730 sal_Int32 nCount
= rCell
.aComplexString
.getLength();
731 rComplexLabel
.realloc( nCount
);
732 for( sal_Int32 nN
=0; nN
<nCount
; nN
++)
733 rComplexLabel
[nN
] = uno::makeAny((rCell
.aComplexString
)[nN
]);
735 else if( rCell
.eType
== SCH_CELL_TYPE_FLOAT
)
737 rComplexLabel
.realloc(1);
738 rComplexLabel
[0] = uno::makeAny( rCell
.fValue
);
742 void SchXMLTableHelper::applyTableToInternalDataProvider(
743 const SchXMLTable
& rTable
,
744 const uno::Reference
< chart2::XChartDocument
>& xChartDoc
)
746 // apply all data read from the local table to the internal data provider
747 if( !xChartDoc
.is() || !xChartDoc
->hasInternalDataProvider() )
749 Reference
< chart2::data::XDataProvider
> xDataProv( xChartDoc
->getDataProvider() );
750 if( !xDataProv
.is() )
753 //prepare the read local table data
754 sal_Int32
nNumRows( static_cast< sal_Int32
>( rTable
.aData
.size()));
755 sal_Int32 nRowOffset
= 0;
756 if( rTable
.bHasHeaderRow
)
761 sal_Int32
nNumColumns( rTable
.nMaxColumnIndex
+ 1 );
762 sal_Int32 nColOffset
= 0;
763 if( rTable
.bHasHeaderColumn
)
769 Sequence
< Sequence
< double > > aDataInRows( nNumRows
);
770 Sequence
< Sequence
< uno::Any
> > aComplexRowDescriptions( nNumRows
);
771 Sequence
< Sequence
< uno::Any
> > aComplexColumnDescriptions( nNumColumns
);
772 for( sal_Int32 i
=0; i
<nNumRows
; ++i
)
773 aDataInRows
[i
].realloc( nNumColumns
);
775 if( !rTable
.aData
.empty() )
777 //apply column labels
778 if( rTable
.bHasHeaderRow
)
780 const ::std::vector
< SchXMLCell
>& rFirstRow
= rTable
.aData
.front();
781 const sal_Int32 nColumnLabelsSize
= aComplexColumnDescriptions
.getLength();
782 const sal_Int32 nMax
= ::std::min
< sal_Int32
>( nColumnLabelsSize
, static_cast< sal_Int32
>( rFirstRow
.size()) - nColOffset
);
783 SAL_WARN_IF( nMax
!= nColumnLabelsSize
, "xmloff.chart", "nMax != nColumnLabelsSize");
784 for( sal_Int32 i
=0; i
<nMax
; ++i
)
785 lcl_ApplyCellToComplexLabel( rFirstRow
[i
+nColOffset
], aComplexColumnDescriptions
[i
] );
788 std::vector
< ::std::vector
< SchXMLCell
> >::const_iterator
aRowIter( rTable
.aData
.begin() + nRowOffset
);
789 std::vector
< ::std::vector
< SchXMLCell
> >::const_iterator
aEnd( rTable
.aData
.end() );
790 for( sal_Int32 nRow
= 0; aRowIter
!= aEnd
&& nRow
< nNumRows
; ++aRowIter
, ++nRow
)
792 const ::std::vector
< SchXMLCell
>& rRow
= *aRowIter
;
796 if( rTable
.bHasHeaderColumn
)
797 lcl_ApplyCellToComplexLabel( rRow
.front(), aComplexRowDescriptions
[nRow
] );
800 Sequence
< double >& rTargetRow
= aDataInRows
[nRow
];
801 lcl_ApplyCellToData aApplyCellToData
= ::std::for_each( rRow
.begin() + nColOffset
, rRow
.end(), lcl_ApplyCellToData( rTargetRow
) );
803 ::rtl::math::setNan( &fNaN
);
804 for( sal_Int32 nCurrentIndex
= aApplyCellToData
.getCurrentIndex(); nCurrentIndex
<nNumColumns
; nCurrentIndex
++ )
805 rTargetRow
[nCurrentIndex
] = fNaN
;//#i110615#
810 //apply the collected data to the chart
811 Reference
< chart2::XAnyDescriptionAccess
> xDataAccess( xDataProv
, uno::UNO_QUERY
);
812 if( !xDataAccess
.is() )
815 xDataAccess
->setData( aDataInRows
);
816 if( rTable
.bHasHeaderColumn
)
817 xDataAccess
->setAnyRowDescriptions( aComplexRowDescriptions
);
818 if( rTable
.bHasHeaderRow
)
819 xDataAccess
->setAnyColumnDescriptions( aComplexColumnDescriptions
);
821 if ( rTable
.bProtected
)
825 Reference
< beans::XPropertySet
> xProps( xChartDoc
, uno::UNO_QUERY_THROW
);
826 xProps
->setPropertyValue( "DisableDataTableDialog", uno::makeAny( true ) );
827 xProps
->setPropertyValue( "DisableComplexChartTypes", uno::makeAny( true ) );
829 catch ( uno::Exception
& )
835 void SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary(
836 const SchXMLTable
& rTable
,
837 const tSchXMLLSequencesPerIndex
& rLSequencesPerIndex
,
838 const uno::Reference
< chart2::XChartDocument
>& xChartDoc
,
839 chart::ChartDataRowSource eDataRowSource
)
841 if( ! (xChartDoc
.is() && xChartDoc
->hasInternalDataProvider()))
844 // If the range-strings are valid (starting with "local-table") they should
845 // be interpreted like given, otherwise (when the ranges refer to Calc- or
846 // Writer-ranges, but the container is not available like when pasting a
847 // chart from Calc to Impress) the range is ignored, and every object gets
848 // one table column in the order of appearance, which is: 1. categories,
849 // 2. data series: 2.a) domains, 2.b) values (main-role, usually y-values)
851 Reference
< chart2::data::XDataProvider
> xDataProv( xChartDoc
->getDataProvider());
853 // create a mapping from original ranges to new ranges
854 lcl_tOriginalRangeToInternalRangeMap aRangeMap
;
856 lcl_fillRangeMapping( rTable
, aRangeMap
, eDataRowSource
);
858 const OUString
lcl_aCategoriesRange(aCategoriesRange
);
860 bool bCategoriesApplied
= false;
861 // translate ranges (using the map created before)
862 for( tSchXMLLSequencesPerIndex::const_iterator
aLSeqIt( rLSequencesPerIndex
.begin());
863 aLSeqIt
!= rLSequencesPerIndex
.end(); ++aLSeqIt
)
865 if( aLSeqIt
->second
.is())
867 // values/error bars/categories
868 if( aLSeqIt
->first
.second
== SCH_XML_PART_VALUES
||
869 aLSeqIt
->first
.second
== SCH_XML_PART_ERROR_BARS
)
871 Reference
< chart2::data::XDataSequence
> xSeq( aLSeqIt
->second
->getValues());
875 SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq
, aRange
, /* bClearProp = */ true ) &&
876 lcl_mapContainsRange( aRangeMap
, aRange
))
878 Reference
< chart2::data::XDataSequence
> xNewSeq(
879 lcl_reassignDataSequence( xSeq
, xDataProv
, aRangeMap
, aRange
));
880 if( xNewSeq
!= xSeq
)
882 SchXMLTools::copyProperties( Reference
< beans::XPropertySet
>( xSeq
, uno::UNO_QUERY
),
883 Reference
< beans::XPropertySet
>( xNewSeq
, uno::UNO_QUERY
));
884 aLSeqIt
->second
->setValues( xNewSeq
);
889 if( lcl_tableOfRangeMatches( aRange
, rTable
.aTableNameOfFile
))
891 if( aLSeqIt
->first
.first
== SCH_XML_CATEGORIES_INDEX
)
892 bCategoriesApplied
= true;
896 if( aLSeqIt
->first
.first
== SCH_XML_CATEGORIES_INDEX
)
898 Reference
< beans::XPropertySet
> xOldSequenceProp( aLSeqIt
->second
->getValues(), uno::UNO_QUERY
);
899 Reference
< chart2::data::XDataSequence
> xNewSequence(
900 xDataProv
->createDataSequenceByRangeRepresentation("categories"));
901 SchXMLTools::copyProperties(
902 xOldSequenceProp
, Reference
< beans::XPropertySet
>( xNewSequence
, uno::UNO_QUERY
));
903 aLSeqIt
->second
->setValues( xNewSequence
);
904 bCategoriesApplied
= true;
908 Reference
< beans::XPropertySet
> xOldSequenceProp( aLSeqIt
->second
->getValues(), uno::UNO_QUERY
);
909 OUString
aRep( OUString::number( aLSeqIt
->first
.first
));
910 Reference
< chart2::data::XDataSequence
> xNewSequence(
911 xDataProv
->createDataSequenceByRangeRepresentation( aRep
));
912 SchXMLTools::copyProperties(
913 xOldSequenceProp
, Reference
< beans::XPropertySet
>( xNewSequence
, uno::UNO_QUERY
));
914 aLSeqIt
->second
->setValues( xNewSequence
);
921 SAL_WARN_IF( aLSeqIt
->first
.second
!= SCH_XML_PART_LABEL
, "xmloff.chart", "aLSeqIt->first.second != SCH_XML_PART_LABEL" );
923 Reference
< chart2::data::XDataSequence
> xSeq( aLSeqIt
->second
->getLabel());
926 SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq
, aRange
, /* bClearProp = */ true ) &&
927 lcl_mapContainsRange( aRangeMap
, aRange
))
929 Reference
< chart2::data::XDataSequence
> xNewSeq(
930 lcl_reassignDataSequence( xSeq
, xDataProv
, aRangeMap
, aRange
));
931 if( xNewSeq
!= xSeq
)
933 SchXMLTools::copyProperties( Reference
< beans::XPropertySet
>( xSeq
, uno::UNO_QUERY
),
934 Reference
< beans::XPropertySet
>( xNewSeq
, uno::UNO_QUERY
));
935 aLSeqIt
->second
->setLabel( xNewSeq
);
938 else if( ! lcl_tableOfRangeMatches( aRange
, rTable
.aTableNameOfFile
))
940 OUString
aRep("label ");
941 aRep
+= OUString::number( aLSeqIt
->first
.first
);
943 Reference
< chart2::data::XDataSequence
> xNewSeq(
944 xDataProv
->createDataSequenceByRangeRepresentation( aRep
));
945 SchXMLTools::copyProperties( Reference
< beans::XPropertySet
>( xSeq
, uno::UNO_QUERY
),
946 Reference
< beans::XPropertySet
>( xNewSeq
, uno::UNO_QUERY
));
947 aLSeqIt
->second
->setLabel( xNewSeq
);
953 // there exist files with own data without a categories element but with row
954 // descriptions. The row descriptions were used as categories even without
955 // the categories element
956 if( ! bCategoriesApplied
)
958 SchXMLTools::CreateCategories(
959 xDataProv
, xChartDoc
, "categories",
960 0 /* nCooSysIndex */, 0 /* nDimension */ );
963 //i91578 display of hidden values (copy paste scenario; use hidden flag during migration to locale table upon paste )
964 //remove series that consist only of hidden columns
965 Reference
< chart2::XInternalDataProvider
> xInternalDataProvider( xDataProv
, uno::UNO_QUERY
);
966 if( xInternalDataProvider
.is() && !rTable
.aHiddenColumns
.empty() )
970 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt( xChartDoc
->getFirstDiagram(), uno::UNO_QUERY_THROW
);
971 Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq( xCooSysCnt
->getCoordinateSystems() );
972 for( sal_Int32 nC
=0; nC
<aCooSysSeq
.getLength(); ++nC
)
974 Reference
< chart2::XChartTypeContainer
> xCooSysContainer( aCooSysSeq
[nC
], uno::UNO_QUERY_THROW
);
975 Sequence
< Reference
< chart2::XChartType
> > aChartTypeSeq( xCooSysContainer
->getChartTypes());
976 for( sal_Int32 nT
=0; nT
<aChartTypeSeq
.getLength(); ++nT
)
978 Reference
< chart2::XDataSeriesContainer
> xSeriesContainer( aChartTypeSeq
[nT
], uno::UNO_QUERY
);
979 if(!xSeriesContainer
.is())
981 Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq( xSeriesContainer
->getDataSeries() );
982 std::vector
< Reference
< chart2::XDataSeries
> > aRemainingSeries
;
984 for( sal_Int32 nS
= 0; nS
< aSeriesSeq
.getLength(); nS
++ )
986 Reference
< chart2::data::XDataSource
> xDataSource( aSeriesSeq
[nS
], uno::UNO_QUERY
);
987 if( xDataSource
.is() )
989 bool bHasUnhiddenColumns
= false;
991 uno::Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSequences( xDataSource
->getDataSequences() );
992 for( sal_Int32 nN
=0; nN
< aSequences
.getLength(); ++nN
)
994 Reference
< chart2::data::XLabeledDataSequence
> xLabeledSequence( aSequences
[nN
] );
995 if(!xLabeledSequence
.is())
997 Reference
< chart2::data::XDataSequence
> xValues( xLabeledSequence
->getValues() );
1000 aRange
= xValues
->getSourceRangeRepresentation();
1001 if( ::std::find( rTable
.aHiddenColumns
.begin(), rTable
.aHiddenColumns
.end(), aRange
.toInt32() ) == rTable
.aHiddenColumns
.end() )
1002 bHasUnhiddenColumns
= true;
1004 if( !bHasUnhiddenColumns
)
1006 Reference
< chart2::data::XDataSequence
> xLabel( xLabeledSequence
->getLabel() );
1009 aRange
= xLabel
->getSourceRangeRepresentation();
1010 sal_Int32 nSearchIndex
= 0;
1011 OUString aSecondToken
= aRange
.getToken( 1, ' ', nSearchIndex
);
1012 if( ::std::find( rTable
.aHiddenColumns
.begin(), rTable
.aHiddenColumns
.end(), aSecondToken
.toInt32() ) == rTable
.aHiddenColumns
.end() )
1013 bHasUnhiddenColumns
= true;
1017 if( bHasUnhiddenColumns
)
1018 aRemainingSeries
.push_back( aSeriesSeq
[nS
] );
1022 if( static_cast<sal_Int32
>(aRemainingSeries
.size()) != aSeriesSeq
.getLength() )
1024 //remove the series that have only hidden data
1025 xSeriesContainer
->setDataSeries( comphelper::containerToSequence(aRemainingSeries
) );
1027 //remove unused sequences
1028 Reference
< chart2::data::XDataSource
> xDataSource( xChartDoc
, uno::UNO_QUERY
);
1029 if( xDataSource
.is() )
1031 //first detect which collumns are really used
1032 std::map
< sal_Int32
, bool > aUsageMap
;
1034 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aUsedSequences( xDataSource
->getDataSequences() );
1035 for( sal_Int32 nN
=0; nN
< aUsedSequences
.getLength(); ++nN
)
1037 Reference
< chart2::data::XLabeledDataSequence
> xLabeledSequence( aUsedSequences
[nN
] );
1038 if(!xLabeledSequence
.is())
1040 Reference
< chart2::data::XDataSequence
> xValues( xLabeledSequence
->getValues() );
1043 aRange
= xValues
->getSourceRangeRepresentation();
1044 sal_Int32 nIndex
= aRange
.toInt32();
1045 if( nIndex
!=0 || !aRange
.equals(lcl_aCategoriesRange
) )
1046 aUsageMap
[nIndex
] = true;
1048 Reference
< chart2::data::XDataSequence
> xLabel( xLabeledSequence
->getLabel() );
1051 aRange
= xLabel
->getSourceRangeRepresentation();
1052 sal_Int32 nSearchIndex
= 0;
1053 OUString aSecondToken
= aRange
.getToken( 1, ' ', nSearchIndex
);
1054 if( !aSecondToken
.isEmpty() )
1055 aUsageMap
[aSecondToken
.toInt32()] = true;
1059 ::std::vector
< sal_Int32
> aSequenceIndexesToDelete
;
1060 for( ::std::vector
< sal_Int32
>::const_iterator
aIt(
1061 rTable
.aHiddenColumns
.begin()); aIt
!= rTable
.aHiddenColumns
.end(); ++aIt
)
1063 sal_Int32 nSequenceIndex
= *aIt
;
1064 if( aUsageMap
.find(nSequenceIndex
) != aUsageMap
.end() )
1066 aSequenceIndexesToDelete
.push_back(nSequenceIndex
);
1069 // delete unnecessary sequences of the internal data
1070 // iterate using greatest index first, so that deletion does not
1071 // shift other sequences that will be deleted later
1072 ::std::sort( aSequenceIndexesToDelete
.begin(), aSequenceIndexesToDelete
.end());
1073 for( ::std::vector
< sal_Int32
>::reverse_iterator
aIt(
1074 aSequenceIndexesToDelete
.rbegin()); aIt
!= aSequenceIndexesToDelete
.rend(); ++aIt
)
1077 xInternalDataProvider
->deleteSequence( *aIt
);
1084 catch( const uno::Exception
& )
1090 SchXMLRangeSomewhereContext::SchXMLRangeSomewhereContext( SvXMLImport
& rImport
,
1092 const OUString
& rLocalName
,
1093 OUString
& rRangeString
) :
1094 SvXMLImportContext( rImport
, nPrefix
, rLocalName
),
1095 mrRangeString( rRangeString
)
1099 SchXMLRangeSomewhereContext::~SchXMLRangeSomewhereContext()
1103 SvXMLImportContext
* SchXMLRangeSomewhereContext::CreateChildContext(
1105 const OUString
& rLocalName
,
1106 const uno::Reference
< xml::sax::XAttributeList
>& )
1108 if( XML_NAMESPACE_SVG
== nPrefix
&& IsXMLToken( rLocalName
, XML_DESC
) )
1110 return new XMLStringBufferImportContext(
1111 GetImport(), nPrefix
, rLocalName
, maRangeStringBuffer
);
1113 return new SvXMLImportContext( GetImport(), nPrefix
, rLocalName
);
1116 void SchXMLRangeSomewhereContext::EndElement()
1118 mrRangeString
= maRangeStringBuffer
.makeStringAndClear();
1121 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */