Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / comphelper / source / xml / ofopxmlhelper.cxx
blobbfb3dbdd0fa619ff5bb6b460b4251e067f450876
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <comphelper/ofopxmlhelper.hxx>
22 #include <comphelper/attributelist.hxx>
24 #include <cppuhelper/implbase.hxx>
26 #include <com/sun/star/beans/StringPair.hpp>
27 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
28 #include <com/sun/star/io/XActiveDataSource.hpp>
29 #include <com/sun/star/xml/sax/Parser.hpp>
30 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
31 #include <com/sun/star/xml/sax/SAXException.hpp>
32 #include <com/sun/star/xml/sax/Writer.hpp>
33 #include <com/sun/star/lang/IllegalArgumentException.hpp>
34 #include <vector>
36 #define RELATIONINFO_FORMAT 0
37 #define CONTENTTYPE_FORMAT 1
38 #define FORMAT_MAX_ID CONTENTTYPE_FORMAT
40 using namespace ::com::sun::star;
42 namespace comphelper {
44 // this helper class is designed to allow to parse ContentType- and Relationship-related information from OfficeOpenXML format
45 class OFOPXMLHelper_Impl
46 : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
48 sal_uInt16 m_nFormat; // which format to parse
50 // Relations info related strings
51 OUString m_aRelListElement;
52 OUString m_aRelElement;
53 OUString m_aIDAttr;
54 OUString m_aTypeAttr;
55 OUString m_aTargetModeAttr;
56 OUString m_aTargetAttr;
58 // ContentType related strings
59 OUString m_aTypesElement;
60 OUString m_aDefaultElement;
61 OUString m_aOverrideElement;
62 OUString m_aExtensionAttr;
63 OUString m_aPartNameAttr;
64 OUString m_aContentTypeAttr;
66 css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aResultSeq;
67 std::vector< OUString > m_aElementsSeq; // stack of elements being parsed
70 public:
71 css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > const & GetParsingResult() const;
73 explicit OFOPXMLHelper_Impl( sal_uInt16 nFormat ); // must not be created directly
75 // XDocumentHandler
76 virtual void SAL_CALL startDocument() override;
77 virtual void SAL_CALL endDocument() override;
78 virtual void SAL_CALL startElement( const OUString& aName, const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) override;
79 virtual void SAL_CALL endElement( const OUString& aName ) override;
80 virtual void SAL_CALL characters( const OUString& aChars ) override;
81 virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
82 virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override;
83 virtual void SAL_CALL setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
87 namespace OFOPXMLHelper {
89 /// @throws css::uno::Exception
90 static uno::Sequence<uno::Sequence< beans::StringPair>> ReadSequence_Impl(
91 const uno::Reference<io::XInputStream>& xInStream,
92 const OUString& aStringID, sal_uInt16 nFormat,
93 const uno::Reference<uno::XComponentContext>& xContext);
95 uno::Sequence< uno::Sequence< beans::StringPair > > ReadRelationsInfoSequence(
96 const uno::Reference< io::XInputStream >& xInStream,
97 const OUString & aStreamName,
98 const uno::Reference< uno::XComponentContext >& rContext )
100 OUString aStringID = "_rels/" + aStreamName;
101 return ReadSequence_Impl( xInStream, aStringID, RELATIONINFO_FORMAT, rContext );
105 uno::Sequence< uno::Sequence< beans::StringPair > > ReadContentTypeSequence(
106 const uno::Reference< io::XInputStream >& xInStream,
107 const uno::Reference< uno::XComponentContext >& rContext )
109 OUString aStringID = "[Content_Types].xml";
110 return ReadSequence_Impl( xInStream, aStringID, CONTENTTYPE_FORMAT, rContext );
113 OUString GetContentTypeByName(
114 const css::uno::Sequence<css::uno::Sequence<css::beans::StringPair>>& rContentTypes,
115 const OUString& rFilename)
117 if (rContentTypes.getLength() < 2)
119 return OUString();
122 const uno::Sequence<beans::StringPair>& rDefaults = rContentTypes[0];
123 const uno::Sequence<beans::StringPair>& rOverrides = rContentTypes[1];
125 // Find the extension and use it to get the type.
126 const sal_Int32 nDotOffset = rFilename.lastIndexOf('.');
127 const OUString aExt = (nDotOffset >= 0 ? rFilename.copy(nDotOffset + 1) : rFilename); // Skip the dot.
129 const std::vector<OUString> aNames = { aExt, "/" + rFilename };
130 for (const OUString& aName : aNames)
132 const auto it1 = std::find_if(rOverrides.begin(), rOverrides.end(), [&aName](const beans::StringPair& rPair)
133 { return rPair.First == aName; });
134 if (it1 != rOverrides.end())
135 return it1->Second;
137 const auto it2 = std::find_if(rDefaults.begin(), rDefaults.end(), [&aName](const beans::StringPair& rPair)
138 { return rPair.First == aName; });
139 if (it2 != rDefaults.end())
140 return it2->Second;
143 return OUString();
146 void WriteRelationsInfoSequence(
147 const uno::Reference< io::XOutputStream >& xOutStream,
148 const uno::Sequence< uno::Sequence< beans::StringPair > >& aSequence,
149 const uno::Reference< uno::XComponentContext >& rContext )
151 if ( !xOutStream.is() )
152 throw uno::RuntimeException();
154 uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(rContext);
156 xWriter->setOutputStream( xOutStream );
158 OUString aRelListElement( "Relationships" );
159 OUString aRelElement( "Relationship" );
160 OUString aCDATAString( "CDATA" );
161 OUString aWhiteSpace( " " );
163 // write the namespace
164 AttributeList* pRootAttrList = new AttributeList;
165 uno::Reference< css::xml::sax::XAttributeList > xRootAttrList( pRootAttrList );
166 pRootAttrList->AddAttribute(
167 "xmlns",
168 aCDATAString,
169 "http://schemas.openxmlformats.org/package/2006/relationships" );
171 xWriter->startDocument();
172 xWriter->startElement( aRelListElement, xRootAttrList );
174 for ( sal_Int32 nInd = 0; nInd < aSequence.getLength(); nInd++ )
176 AttributeList *pAttrList = new AttributeList;
177 uno::Reference< css::xml::sax::XAttributeList > xAttrList( pAttrList );
178 for( sal_Int32 nSecInd = 0; nSecInd < aSequence[nInd].getLength(); nSecInd++ )
180 if ( !(aSequence[nInd][nSecInd].First == "Id"
181 || aSequence[nInd][nSecInd].First == "Type"
182 || aSequence[nInd][nSecInd].First == "TargetMode"
183 || aSequence[nInd][nSecInd].First == "Target") )
185 // TODO/LATER: should the extensions be allowed?
186 throw lang::IllegalArgumentException();
188 pAttrList->AddAttribute( aSequence[nInd][nSecInd].First, aCDATAString, aSequence[nInd][nSecInd].Second );
191 xWriter->startElement( aRelElement, xAttrList );
192 xWriter->ignorableWhitespace( aWhiteSpace );
193 xWriter->endElement( aRelElement );
196 xWriter->ignorableWhitespace( aWhiteSpace );
197 xWriter->endElement( aRelListElement );
198 xWriter->endDocument();
202 void WriteContentSequence(
203 const uno::Reference< io::XOutputStream >& xOutStream,
204 const uno::Sequence< beans::StringPair >& aDefaultsSequence,
205 const uno::Sequence< beans::StringPair >& aOverridesSequence,
206 const uno::Reference< uno::XComponentContext >& rContext )
208 if ( !xOutStream.is() )
209 throw uno::RuntimeException();
211 uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(rContext);
213 xWriter->setOutputStream( xOutStream );
215 static const OUString aTypesElement("Types");
216 static const OUString aDefaultElement("Default");
217 static const OUString aOverrideElement("Override");
218 static const OUString aContentTypeAttr("ContentType");
219 static const OUString aCDATAString("CDATA");
220 static const OUString aWhiteSpace(" ");
222 // write the namespace
223 AttributeList* pRootAttrList = new AttributeList;
224 uno::Reference< css::xml::sax::XAttributeList > xRootAttrList( pRootAttrList );
225 pRootAttrList->AddAttribute(
226 "xmlns",
227 aCDATAString,
228 "http://schemas.openxmlformats.org/package/2006/content-types" );
230 xWriter->startDocument();
231 xWriter->startElement( aTypesElement, xRootAttrList );
233 for ( sal_Int32 nInd = 0; nInd < aDefaultsSequence.getLength(); nInd++ )
235 AttributeList *pAttrList = new AttributeList;
236 uno::Reference< css::xml::sax::XAttributeList > xAttrList( pAttrList );
237 pAttrList->AddAttribute( "Extension", aCDATAString, aDefaultsSequence[nInd].First );
238 pAttrList->AddAttribute( aContentTypeAttr, aCDATAString, aDefaultsSequence[nInd].Second );
240 xWriter->startElement( aDefaultElement, xAttrList );
241 xWriter->ignorableWhitespace( aWhiteSpace );
242 xWriter->endElement( aDefaultElement );
245 for ( sal_Int32 nInd = 0; nInd < aOverridesSequence.getLength(); nInd++ )
247 AttributeList *pAttrList = new AttributeList;
248 uno::Reference< css::xml::sax::XAttributeList > xAttrList( pAttrList );
249 pAttrList->AddAttribute( "PartName", aCDATAString, aOverridesSequence[nInd].First );
250 pAttrList->AddAttribute( aContentTypeAttr, aCDATAString, aOverridesSequence[nInd].Second );
252 xWriter->startElement( aOverrideElement, xAttrList );
253 xWriter->ignorableWhitespace( aWhiteSpace );
254 xWriter->endElement( aOverrideElement );
257 xWriter->ignorableWhitespace( aWhiteSpace );
258 xWriter->endElement( aTypesElement );
259 xWriter->endDocument();
263 uno::Sequence< uno::Sequence< beans::StringPair > > ReadSequence_Impl(
264 const uno::Reference< io::XInputStream >& xInStream,
265 const OUString& aStringID, sal_uInt16 nFormat,
266 const uno::Reference< uno::XComponentContext >& rContext )
268 if ( !rContext.is() || !xInStream.is() || nFormat > FORMAT_MAX_ID )
269 throw uno::RuntimeException();
271 uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create( rContext );
273 OFOPXMLHelper_Impl *const pHelper = new OFOPXMLHelper_Impl( nFormat );
274 uno::Reference< css::xml::sax::XDocumentHandler > xHelper( static_cast< css::xml::sax::XDocumentHandler* >( pHelper ) );
275 css::xml::sax::InputSource aParserInput;
276 aParserInput.aInputStream = xInStream;
277 aParserInput.sSystemId = aStringID;
278 xParser->setDocumentHandler( xHelper );
279 xParser->parseStream( aParserInput );
280 xParser->setDocumentHandler( uno::Reference < css::xml::sax::XDocumentHandler > () );
282 return pHelper->GetParsingResult();
285 } // namespace OFOPXMLHelper
287 OFOPXMLHelper_Impl::OFOPXMLHelper_Impl( sal_uInt16 nFormat )
288 : m_nFormat( nFormat )
289 , m_aRelListElement( "Relationships" )
290 , m_aRelElement( "Relationship" )
291 , m_aIDAttr( "Id" )
292 , m_aTypeAttr( "Type" )
293 , m_aTargetModeAttr( "TargetMode" )
294 , m_aTargetAttr( "Target" )
295 , m_aTypesElement( "Types" )
296 , m_aDefaultElement( "Default" )
297 , m_aOverrideElement( "Override" )
298 , m_aExtensionAttr( "Extension" )
299 , m_aPartNameAttr( "PartName" )
300 , m_aContentTypeAttr( "ContentType" )
304 uno::Sequence< uno::Sequence< beans::StringPair > > const & OFOPXMLHelper_Impl::GetParsingResult() const
306 if ( m_aElementsSeq.size() )
307 throw uno::RuntimeException(); // the parsing has still not finished!
309 return m_aResultSeq;
313 void SAL_CALL OFOPXMLHelper_Impl::startDocument()
318 void SAL_CALL OFOPXMLHelper_Impl::endDocument()
323 void SAL_CALL OFOPXMLHelper_Impl::startElement( const OUString& aName, const uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
325 if ( m_nFormat == RELATIONINFO_FORMAT )
327 if ( aName == m_aRelListElement )
329 sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
331 if ( nNewLength != 1 )
332 throw css::xml::sax::SAXException(); // TODO: this element must be the first level element
334 m_aElementsSeq.push_back( aName );
336 return; // nothing to do
338 else if ( aName == m_aRelElement )
340 sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
341 if ( nNewLength != 2 )
342 throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
344 m_aElementsSeq.push_back( aName );
346 sal_Int32 nNewEntryNum = m_aResultSeq.getLength() + 1;
347 m_aResultSeq.realloc( nNewEntryNum );
348 sal_Int32 nAttrNum = 0;
349 m_aResultSeq[nNewEntryNum-1].realloc( 4 ); // the maximal expected number of arguments is 4
351 OUString aIDValue = xAttribs->getValueByName( m_aIDAttr );
352 if ( aIDValue.isEmpty() )
353 throw css::xml::sax::SAXException(); // TODO: the ID value must present
355 OUString aTypeValue = xAttribs->getValueByName( m_aTypeAttr );
356 OUString aTargetValue = xAttribs->getValueByName( m_aTargetAttr );
357 OUString aTargetModeValue = xAttribs->getValueByName( m_aTargetModeAttr );
359 m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = m_aIDAttr;
360 m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aIDValue;
362 if ( !aTypeValue.isEmpty() )
364 m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = m_aTypeAttr;
365 m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aTypeValue;
368 if ( !aTargetValue.isEmpty() )
370 m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = m_aTargetAttr;
371 m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aTargetValue;
374 if ( !aTargetModeValue.isEmpty() )
376 m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = m_aTargetModeAttr;
377 m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aTargetModeValue;
380 m_aResultSeq[nNewEntryNum-1].realloc( nAttrNum );
382 else
383 throw css::xml::sax::SAXException(); // TODO: no other elements expected!
385 else if ( m_nFormat == CONTENTTYPE_FORMAT )
387 if ( aName == m_aTypesElement )
389 sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
391 if ( nNewLength != 1 )
392 throw css::xml::sax::SAXException(); // TODO: this element must be the first level element
394 m_aElementsSeq.push_back( aName );
396 if ( !m_aResultSeq.getLength() )
397 m_aResultSeq.realloc( 2 );
399 return; // nothing to do
401 else if ( aName == m_aDefaultElement )
403 sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
404 if ( nNewLength != 2 )
405 throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
407 m_aElementsSeq.push_back( aName );
409 if ( !m_aResultSeq.getLength() )
410 m_aResultSeq.realloc( 2 );
412 if ( m_aResultSeq.getLength() != 2 )
413 throw uno::RuntimeException();
415 const OUString aExtensionValue = xAttribs->getValueByName( m_aExtensionAttr );
416 if ( aExtensionValue.isEmpty() )
417 throw css::xml::sax::SAXException(); // TODO: the Extension value must present
419 const OUString aContentTypeValue = xAttribs->getValueByName( m_aContentTypeAttr );
420 if ( aContentTypeValue.isEmpty() )
421 throw css::xml::sax::SAXException(); // TODO: the ContentType value must present
423 const sal_Int32 nNewResultLen = m_aResultSeq[0].getLength() + 1;
424 m_aResultSeq[0].realloc( nNewResultLen );
426 m_aResultSeq[0][nNewResultLen-1].First = aExtensionValue;
427 m_aResultSeq[0][nNewResultLen-1].Second = aContentTypeValue;
429 else if ( aName == m_aOverrideElement )
431 sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
432 if ( nNewLength != 2 )
433 throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
435 m_aElementsSeq.push_back( aName );
437 if ( !m_aResultSeq.getLength() )
438 m_aResultSeq.realloc( 2 );
440 if ( m_aResultSeq.getLength() != 2 )
441 throw uno::RuntimeException();
443 OUString aPartNameValue = xAttribs->getValueByName( m_aPartNameAttr );
444 if ( aPartNameValue.isEmpty() )
445 throw css::xml::sax::SAXException(); // TODO: the PartName value must present
447 OUString aContentTypeValue = xAttribs->getValueByName( m_aContentTypeAttr );
448 if ( aContentTypeValue.isEmpty() )
449 throw css::xml::sax::SAXException(); // TODO: the ContentType value must present
451 sal_Int32 nNewResultLen = m_aResultSeq[1].getLength() + 1;
452 m_aResultSeq[1].realloc( nNewResultLen );
454 m_aResultSeq[1][nNewResultLen-1].First = aPartNameValue;
455 m_aResultSeq[1][nNewResultLen-1].Second = aContentTypeValue;
457 else
458 throw css::xml::sax::SAXException(); // TODO: no other elements expected!
460 else
461 throw css::xml::sax::SAXException(); // TODO: no other elements expected!
465 void SAL_CALL OFOPXMLHelper_Impl::endElement( const OUString& aName )
467 if ( m_nFormat == RELATIONINFO_FORMAT || m_nFormat == CONTENTTYPE_FORMAT )
469 sal_Int32 nLength = m_aElementsSeq.size();
470 if ( nLength <= 0 )
471 throw css::xml::sax::SAXException(); // TODO: no other end elements expected!
473 if ( m_aElementsSeq[nLength-1] != aName )
474 throw css::xml::sax::SAXException(); // TODO: unexpected element ended
476 m_aElementsSeq.resize( nLength - 1 );
481 void SAL_CALL OFOPXMLHelper_Impl::characters( const OUString& /*aChars*/ )
486 void SAL_CALL OFOPXMLHelper_Impl::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
491 void SAL_CALL OFOPXMLHelper_Impl::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
496 void SAL_CALL OFOPXMLHelper_Impl::setDocumentLocator( const uno::Reference< css::xml::sax::XLocator >& /*xLocator*/ )
500 } // namespace comphelper
502 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */