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