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
