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 "xmlExportDocumentHandler.hxx"
21 #include <com/sun/star/sdb/CommandType.hpp>
22 #include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp>
23 #include <com/sun/star/chart/XComplexDescriptionAccess.hpp>
24 #include <com/sun/star/reflection/ProxyFactory.hpp>
25 #include <comphelper/sequenceashashmap.hxx>
26 #include <comphelper/documentconstants.hxx>
27 #include <cppuhelper/supportsservice.hxx>
28 #include <xmloff/attrlist.hxx>
29 #include <xmloff/xmltoken.hxx>
30 #include <xmloff/xmlement.hxx>
31 #include <xmloff/xmluconv.hxx>
32 #include <unotools/saveopt.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <connectivity/dbtools.hxx>
35 
36 namespace rptxml
37 {
38 using namespace ::com::sun::star;
39 using namespace ::xmloff::token;
40 
41 static void lcl_exportPrettyPrinting(const uno::Reference< xml::sax::XDocumentHandler >& _xDelegatee)
42 {
43     SvtSaveOptions aSaveOpt;
44     if ( aSaveOpt.IsPrettyPrinting() )
45     {
46         _xDelegatee->ignorableWhitespace(" ");
47     }
48 }
49 
50 OUString lcl_createAttribute(const xmloff::token::XMLTokenEnum& _eNamespace,const xmloff::token::XMLTokenEnum& _eAttribute)
51 {
52     return
53     // ...if it's in our map, make the prefix
54         xmloff::token::GetXMLToken(_eNamespace) +
55         ":" +
56         xmloff::token::GetXMLToken(_eAttribute);
57 }
58 
59 static void lcl_correctCellAddress(const OUString & _sName, const uno::Reference< xml::sax::XAttributeList > & xAttribs)
60 {
61     SvXMLAttributeList* pList = comphelper::getUnoTunnelImplementation<SvXMLAttributeList>(xAttribs);
62     OUString sCellAddress = pList->getValueByName(_sName);
63     const sal_Int32 nPos = sCellAddress.lastIndexOf('$');
64     if ( nPos != -1 )
65     {
66         sCellAddress = sCellAddress.copy(0,nPos) + "$65535";
67         pList->RemoveAttribute(_sName);
68         pList->AddAttribute(_sName,sCellAddress);
69     }
70 }
71 
72 ExportDocumentHandler::ExportDocumentHandler(uno::Reference< uno::XComponentContext > const & context) :
73     m_xContext(context)
74     ,m_nColumnCount(0)
75     ,m_bTableRowsStarted(false)
76     ,m_bFirstRowExported(false)
77     ,m_bCountColumnHeader(false)
78 {
79 }
80 
81 ExportDocumentHandler::~ExportDocumentHandler()
82 {
83     if ( m_xProxy.is() )
84     {
85         m_xProxy->setDelegator( nullptr );
86         m_xProxy.clear();
87     }
88 }
89 IMPLEMENT_GET_IMPLEMENTATION_ID(ExportDocumentHandler)
90 
91 OUString SAL_CALL ExportDocumentHandler::getImplementationName(  )
92 {
93     return getImplementationName_Static();
94 }
95 
96 sal_Bool SAL_CALL ExportDocumentHandler::supportsService( const OUString& ServiceName )
97 {
98     return cppu::supportsService(this, ServiceName);
99 }
100 
101 uno::Sequence< OUString > SAL_CALL ExportDocumentHandler::getSupportedServiceNames(  )
102 {
103     uno::Sequence< OUString > aSupported;
104     if ( m_xServiceInfo.is() )
105         aSupported = m_xServiceInfo->getSupportedServiceNames();
106     return ::comphelper::concatSequences(getSupportedServiceNames_static(),aSupported);
107 }
108 
109 OUString ExportDocumentHandler::getImplementationName_Static(  )
110 {
111     return "com.sun.star.comp.report.ExportDocumentHandler";
112 }
113 
114 
115 uno::Sequence< OUString > ExportDocumentHandler::getSupportedServiceNames_static(  )
116 {
117     uno::Sequence< OUString > aSupported { "com.sun.star.report.ExportDocumentHandler" };
118     return aSupported;
119 }
120 
121 
122 uno::Reference< uno::XInterface > ExportDocumentHandler::create( const uno::Reference< uno::XComponentContext >& _rxContext )
123 {
124     return *(new ExportDocumentHandler( _rxContext ));
125 }
126 // xml::sax::XDocumentHandler:
127 void SAL_CALL ExportDocumentHandler::startDocument()
128 {
129     m_xDelegatee->startDocument();
130 }
131 
132 void SAL_CALL ExportDocumentHandler::endDocument()
133 {
134     m_xDelegatee->endDocument();
135 }
136 
137 void SAL_CALL ExportDocumentHandler::startElement(const OUString & _sName, const uno::Reference< xml::sax::XAttributeList > & xAttribs)
138 {
139     bool bExport = true;
140     if ( _sName == "office:chart" )
141     {
142         SvXMLAttributeList* pList = new SvXMLAttributeList();
143         uno::Reference< xml::sax::XAttributeList > xNewAttribs = pList;
144         OUStringBuffer sValue;
145         static const SvXMLEnumMapEntry<sal_uInt16> aXML_CommnadTypeEnumMap[] =
146         {
147             { XML_TABLE, sdb::CommandType::TABLE },
148             { XML_QUERY, sdb::CommandType::QUERY },
149             { XML_TOKEN_INVALID, 0 }
150         };
151         if ( SvXMLUnitConverter::convertEnum( sValue, static_cast<sal_uInt16>(m_xDatabaseDataProvider->getCommandType()),aXML_CommnadTypeEnumMap ) )
152         {
153             pList->AddAttribute(lcl_createAttribute(XML_NP_RPT,XML_COMMAND_TYPE),sValue.makeStringAndClear());
154         }
155         const OUString sComamnd = m_xDatabaseDataProvider->getCommand();
156         if ( !sComamnd.isEmpty() )
157             pList->AddAttribute(lcl_createAttribute(XML_NP_RPT,XML_COMMAND),sComamnd);
158 
159         const OUString sFilter( m_xDatabaseDataProvider->getFilter() );
160         if ( !sFilter.isEmpty() )
161             pList->AddAttribute(lcl_createAttribute(XML_NP_RPT,XML_FILTER),sFilter);
162 
163         const bool bEscapeProcessing( m_xDatabaseDataProvider->getEscapeProcessing() );
164         if ( !bEscapeProcessing )
165             pList->AddAttribute(lcl_createAttribute(XML_NP_RPT,XML_ESCAPE_PROCESSING),::xmloff::token::GetXMLToken( XML_FALSE ));
166 
167         pList->AddAttribute(lcl_createAttribute(XML_NP_OFFICE,XML_MIMETYPE),MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII);
168 
169         m_xDelegatee->startElement(lcl_createAttribute(XML_NP_OFFICE,XML_REPORT),xNewAttribs);
170 
171         const OUString sTableCalc = lcl_createAttribute(XML_NP_TABLE,XML_CALCULATION_SETTINGS);
172         m_xDelegatee->startElement(sTableCalc,nullptr);
173         pList = new SvXMLAttributeList();
174         uno::Reference< xml::sax::XAttributeList > xNullAttr = pList;
175         pList->AddAttribute(lcl_createAttribute(XML_NP_TABLE,XML_DATE_VALUE),"1899-12-30");
176 
177         const OUString sNullDate = lcl_createAttribute(XML_NP_TABLE,XML_NULL_DATE);
178         m_xDelegatee->startElement(sNullDate,xNullAttr);
179         m_xDelegatee->endElement(sNullDate);
180         m_xDelegatee->endElement(sTableCalc);
181         bExport = false;
182     }
183     else if ( _sName == "table:table" )
184     {
185         m_xDelegatee->startElement(lcl_createAttribute(XML_NP_RPT,XML_DETAIL),nullptr);
186         lcl_exportPrettyPrinting(m_xDelegatee);
187     }
188     else if ( _sName == "table:table-header-rows" )
189     {
190         m_bCountColumnHeader = true;
191     }
192     else if ( m_bCountColumnHeader && _sName == "table:table-cell" )
193     {
194         ++m_nColumnCount;
195     }
196     else if ( _sName == "table:table-rows" )
197     {
198         m_xDelegatee->startElement(_sName,xAttribs);
199         exportTableRows();
200         bExport = false;
201         m_bTableRowsStarted = true;
202         m_bFirstRowExported = true;
203     }
204     else if ( m_bTableRowsStarted && m_bFirstRowExported && (_sName == "table:table-row" || _sName == "table:table-cell") )
205         bExport = false;
206     else if ( _sName == "chart:plot-area" )
207     {
208         SvXMLAttributeList* pList = comphelper::getUnoTunnelImplementation<SvXMLAttributeList>(xAttribs);
209         pList->RemoveAttribute("table:cell-range-address");
210     }
211     else if ( _sName == "chart:categories" )
212     {
213         static OUString s_sCellAddress(lcl_createAttribute(XML_NP_TABLE,XML_CELL_RANGE_ADDRESS));
214         lcl_correctCellAddress(s_sCellAddress,xAttribs);
215     }
216     else if ( _sName == "chart:series" )
217     {
218         static OUString s_sCellAddress(lcl_createAttribute(XML_NP_CHART,XML_VALUES_CELL_RANGE_ADDRESS));
219         lcl_correctCellAddress(s_sCellAddress,xAttribs);
220     }
221     else if ( m_bTableRowsStarted && !m_bFirstRowExported && _sName == "table:table-cell" )
222     {
223         SvXMLAttributeList* pList = comphelper::getUnoTunnelImplementation<SvXMLAttributeList>(xAttribs);
224         static OUString s_sValue(lcl_createAttribute(XML_NP_OFFICE,XML_VALUE));
225         pList->RemoveAttribute(s_sValue);
226     }
227     else if ( m_bTableRowsStarted && _sName == "text:p" )
228     {
229         bExport = false;
230     }
231     if ( bExport )
232         m_xDelegatee->startElement(_sName,xAttribs);
233 }
234 
235 void SAL_CALL ExportDocumentHandler::endElement(const OUString & _sName)
236 {
237     bool bExport = true;
238     OUString sNewName = _sName;
239     if ( _sName == "office:chart" )
240     {
241         sNewName = lcl_createAttribute(XML_NP_OFFICE,XML_REPORT);
242     }
243     else if ( _sName == "table:table" )
244     {
245         m_xDelegatee->endElement(_sName);
246         lcl_exportPrettyPrinting(m_xDelegatee);
247         sNewName = lcl_createAttribute(XML_NP_RPT,XML_DETAIL);
248     }
249     else if ( _sName == "table:table-header-rows" )
250     {
251         m_bCountColumnHeader = false;
252     }
253     else if ( _sName == "table:table-rows" )
254         m_bTableRowsStarted = false;
255     else if ( m_bTableRowsStarted && m_bFirstRowExported && (_sName == "table:table-row" || _sName == "table:table-cell") )
256         bExport = false;
257     else if ( m_bTableRowsStarted && _sName == "table:table-row" )
258         m_bFirstRowExported = true;
259     else if ( m_bTableRowsStarted && _sName == "text:p" )
260     {
261         bExport = !m_bFirstRowExported;
262     }
263 
264     if ( bExport )
265         m_xDelegatee->endElement(sNewName);
266 }
267 
268 void SAL_CALL ExportDocumentHandler::characters(const OUString & aChars)
269 {
270     if ( !(m_bTableRowsStarted || m_bFirstRowExported) )
271     {
272         m_xDelegatee->characters(aChars);
273     }
274 }
275 
276 void SAL_CALL ExportDocumentHandler::ignorableWhitespace(const OUString & aWhitespaces)
277 {
278     m_xDelegatee->ignorableWhitespace(aWhitespaces);
279 }
280 
281 void SAL_CALL ExportDocumentHandler::processingInstruction(const OUString & aTarget, const OUString & aData)
282 {
283     m_xDelegatee->processingInstruction(aTarget,aData);
284 }
285 
286 void SAL_CALL ExportDocumentHandler::setDocumentLocator(const uno::Reference< xml::sax::XLocator > & xLocator)
287 {
288     m_xDelegatee->setDocumentLocator(xLocator);
289 }
290 void SAL_CALL ExportDocumentHandler::initialize( const uno::Sequence< uno::Any >& _aArguments )
291 {
292     ::osl::MutexGuard aGuard(m_aMutex);
293     comphelper::SequenceAsHashMap aArgs(_aArguments);
294     m_xDelegatee = aArgs.getUnpackedValueOrDefault("DocumentHandler",m_xDelegatee);
295     m_xModel = aArgs.getUnpackedValueOrDefault("Model",m_xModel);
296 
297     OSL_ENSURE(m_xDelegatee.is(),"No document handler available!");
298     if ( !m_xDelegatee.is() || !m_xModel.is() )
299         throw uno::Exception("no delegatee and no model", nullptr);
300 
301     m_xDatabaseDataProvider.set(m_xModel->getDataProvider(),uno::UNO_QUERY_THROW);
302     if ( !m_xDatabaseDataProvider->getActiveConnection().is() )
303         throw uno::Exception("no active connection", nullptr);
304 
305     uno::Reference< reflection::XProxyFactory > xProxyFactory = reflection::ProxyFactory::create( m_xContext );
306     m_xProxy = xProxyFactory->createProxy(m_xDelegatee.get());
307     ::comphelper::query_aggregation(m_xProxy,m_xDelegatee);
308     m_xTypeProvider.set(m_xDelegatee,uno::UNO_QUERY);
309     m_xServiceInfo.set(m_xDelegatee,uno::UNO_QUERY);
310 
311     // set ourself as delegator
312     m_xProxy->setDelegator( *this );
313     const OUString sCommand = m_xDatabaseDataProvider->getCommand();
314     if ( !sCommand.isEmpty() )
315         m_aColumns = ::dbtools::getFieldNamesByCommandDescriptor(m_xDatabaseDataProvider->getActiveConnection()
316                     ,m_xDatabaseDataProvider->getCommandType()
317                     ,sCommand);
318 
319     uno::Reference< chart::XComplexDescriptionAccess > xDataProvider(m_xDatabaseDataProvider,uno::UNO_QUERY);
320     if ( xDataProvider.is() )
321     {
322         m_aColumns.realloc(1);
323         const uno::Sequence< OUString > aColumnNames = xDataProvider->getColumnDescriptions();
324         for(const auto& rColumnName : aColumnNames)
325         {
326             if ( !rColumnName.isEmpty() )
327             {
328                 sal_Int32 nCount = m_aColumns.getLength();
329                 m_aColumns.realloc(nCount+1);
330                 m_aColumns[nCount] = rColumnName;
331             }
332         }
333     }
334 }
335 
336 uno::Any SAL_CALL ExportDocumentHandler::queryInterface( const uno::Type& _rType )
337 {
338     uno::Any aReturn = ExportDocumentHandler_BASE::queryInterface(_rType);
339     return aReturn.hasValue() ? aReturn : (m_xProxy.is() ? m_xProxy->queryAggregation(_rType) : aReturn);
340 }
341 
342 uno::Sequence< uno::Type > SAL_CALL ExportDocumentHandler::getTypes(  )
343 {
344     if ( m_xTypeProvider.is() )
345         return ::comphelper::concatSequences(
346             ExportDocumentHandler_BASE::getTypes(),
347             m_xTypeProvider->getTypes()
348         );
349     return ExportDocumentHandler_BASE::getTypes();
350 }
351 
352 void ExportDocumentHandler::exportTableRows()
353 {
354     const OUString sRow( lcl_createAttribute(XML_NP_TABLE, XML_TABLE_ROW) );
355     m_xDelegatee->startElement(sRow,nullptr);
356 
357     const OUString sValueType( lcl_createAttribute(XML_NP_OFFICE, XML_VALUE_TYPE) );
358 
359     const OUString sCell( lcl_createAttribute(XML_NP_TABLE, XML_TABLE_CELL) );
360     const OUString sP( lcl_createAttribute(XML_NP_TEXT, XML_P) );
361     const OUString sFtext(lcl_createAttribute(XML_NP_RPT,XML_FORMATTED_TEXT) );
362     const OUString sRElement(lcl_createAttribute(XML_NP_RPT,XML_REPORT_ELEMENT) );
363     const OUString sRComponent( lcl_createAttribute(XML_NP_RPT,XML_REPORT_COMPONENT) ) ;
364     const OUString sFormulaAttrib( lcl_createAttribute(XML_NP_RPT,XML_FORMULA) );
365     static const char s_sFloat[] = "float";
366 
367     SvXMLAttributeList* pCellAtt = new SvXMLAttributeList();
368     uno::Reference< xml::sax::XAttributeList > xCellAtt = pCellAtt;
369     pCellAtt->AddAttribute(sValueType, "string");
370 
371     bool bRemoveString = true;
372     const sal_Int32 nCount = m_aColumns.getLength();
373     if ( m_nColumnCount > nCount )
374     {
375         const sal_Int32 nEmptyCellCount = m_nColumnCount - nCount;
376         for(sal_Int32 i = 0; i < nEmptyCellCount ; ++i)
377         {
378             m_xDelegatee->startElement(sCell,xCellAtt);
379             if ( bRemoveString )
380             {
381                 bRemoveString = false;
382                 pCellAtt->RemoveAttribute(sValueType);
383                 pCellAtt->AddAttribute(sValueType,s_sFloat);
384             }
385             m_xDelegatee->startElement(sP,nullptr);
386             m_xDelegatee->endElement(sP);
387             m_xDelegatee->endElement(sCell);
388         }
389     }
390     for(const auto& rColumn : std::as_const(m_aColumns))
391     {
392         OUString sFormula = "field:[" + rColumn + "]";
393         SvXMLAttributeList* pList = new SvXMLAttributeList();
394         uno::Reference< xml::sax::XAttributeList > xAttribs = pList;
395         pList->AddAttribute(sFormulaAttrib,sFormula);
396 
397         m_xDelegatee->startElement(sCell,xCellAtt);
398         if ( bRemoveString )
399         {
400             bRemoveString = false;
401             pCellAtt->RemoveAttribute(sValueType);
402             pCellAtt->AddAttribute(sValueType,s_sFloat);
403         }
404         m_xDelegatee->startElement(sP,nullptr);
405         m_xDelegatee->startElement(sFtext,xAttribs);
406         m_xDelegatee->startElement(sRElement,nullptr);
407         m_xDelegatee->startElement(sRComponent,nullptr);
408 
409         m_xDelegatee->endElement(sRComponent);
410         m_xDelegatee->endElement(sRElement);
411         m_xDelegatee->endElement(sFtext);
412         m_xDelegatee->endElement(sP);
413         m_xDelegatee->endElement(sCell);
414     }
415 
416     m_xDelegatee->endElement(sRow);
417 }
418 
419 } // namespace rptxml
420 
421 
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
423