1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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>
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
;
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
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
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)
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())
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())
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(
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(
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" )
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!
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
);
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
;
458 throw css::xml::sax::SAXException(); // TODO: no other elements expected!
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();
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: */