LibreOffice Module comphelper (master)  1
ofopxmlhelper.cxx
Go to the documentation of this file.
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 
23 
24 #include <cppuhelper/implbase.hxx>
25 #include <rtl/ref.hxx>
26 
27 #include <com/sun/star/beans/StringPair.hpp>
28 #include <com/sun/star/xml/sax/Parser.hpp>
29 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
30 #include <com/sun/star/xml/sax/SAXException.hpp>
31 #include <com/sun/star/xml/sax/Writer.hpp>
32 #include <com/sun/star/lang/IllegalArgumentException.hpp>
33 #include <vector>
34 
35 #define RELATIONINFO_FORMAT 0
36 #define CONTENTTYPE_FORMAT 1
37 #define FORMAT_MAX_ID CONTENTTYPE_FORMAT
38 
39 using namespace ::com::sun::star;
40 
41 namespace comphelper {
42 
43 namespace {
44 
45 // this helper class is designed to allow to parse ContentType- and Relationship-related information from OfficeOpenXML format
46 class OFOPXMLHelper_Impl
47  : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
48 {
49  sal_uInt16 const m_nFormat; // which format to parse
50 
51  css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aResultSeq;
52  std::vector< OUString > m_aElementsSeq; // stack of elements being parsed
53 
54 
55 public:
56  css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > const & GetParsingResult() const;
57 
58  explicit OFOPXMLHelper_Impl( sal_uInt16 nFormat ); // must not be created directly
59 
60  // XDocumentHandler
61  virtual void SAL_CALL startDocument() override;
62  virtual void SAL_CALL endDocument() override;
63  virtual void SAL_CALL startElement( const OUString& aName, const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) override;
64  virtual void SAL_CALL endElement( const OUString& aName ) override;
65  virtual void SAL_CALL characters( const OUString& aChars ) override;
66  virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
67  virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override;
68  virtual void SAL_CALL setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
69 };
70 
71 }
72 
73 namespace OFOPXMLHelper {
74 
76 static uno::Sequence<uno::Sequence< beans::StringPair>> ReadSequence_Impl(
77  const uno::Reference<io::XInputStream>& xInStream,
78  const OUString& aStringID, sal_uInt16 nFormat,
79  const uno::Reference<uno::XComponentContext>& xContext);
80 
81 uno::Sequence< uno::Sequence< beans::StringPair > > ReadRelationsInfoSequence(
82  const uno::Reference< io::XInputStream >& xInStream,
83  std::u16string_view aStreamName,
84  const uno::Reference< uno::XComponentContext >& rContext )
85 {
86  OUString aStringID = OUString::Concat("_rels/") + aStreamName;
87  return ReadSequence_Impl( xInStream, aStringID, RELATIONINFO_FORMAT, rContext );
88 }
89 
90 
91 uno::Sequence< uno::Sequence< beans::StringPair > > ReadContentTypeSequence(
92  const uno::Reference< io::XInputStream >& xInStream,
93  const uno::Reference< uno::XComponentContext >& rContext )
94 {
95  return ReadSequence_Impl( xInStream, "[Content_Types].xml", CONTENTTYPE_FORMAT, rContext );
96 }
97 
99  const css::uno::Sequence<css::uno::Sequence<css::beans::StringPair>>& rContentTypes,
100  const OUString& rFilename)
101 {
102  if (rContentTypes.getLength() < 2)
103  {
104  return OUString();
105  }
106 
107  const uno::Sequence<beans::StringPair>& rDefaults = rContentTypes[0];
108  const uno::Sequence<beans::StringPair>& rOverrides = rContentTypes[1];
109 
110  // Find the extension and use it to get the type.
111  const sal_Int32 nDotOffset = rFilename.lastIndexOf('.');
112  const OUString aExt = (nDotOffset >= 0 ? rFilename.copy(nDotOffset + 1) : rFilename); // Skip the dot.
113 
114  const std::vector<OUString> aNames = { aExt, "/" + rFilename };
115  for (const OUString& aName : aNames)
116  {
117  const auto it1 = std::find_if(rOverrides.begin(), rOverrides.end(), [&aName](const beans::StringPair& rPair)
118  { return rPair.First == aName; });
119  if (it1 != rOverrides.end())
120  return it1->Second;
121 
122  const auto it2 = std::find_if(rDefaults.begin(), rDefaults.end(), [&aName](const beans::StringPair& rPair)
123  { return rPair.First == aName; });
124  if (it2 != rDefaults.end())
125  return it2->Second;
126  }
127 
128  return OUString();
129 }
130 
132  const uno::Reference< io::XOutputStream >& xOutStream,
133  const uno::Sequence< uno::Sequence< beans::StringPair > >& aSequence,
134  const uno::Reference< uno::XComponentContext >& rContext )
135 {
136  if ( !xOutStream.is() )
137  throw uno::RuntimeException();
138 
139  uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(rContext);
140 
141  xWriter->setOutputStream( xOutStream );
142 
143  OUString aRelListElement( "Relationships" );
144  OUString aRelElement( "Relationship" );
145  OUString aCDATAString( "CDATA" );
146  OUString aWhiteSpace( " " );
147 
148  // write the namespace
149  rtl::Reference<AttributeList> pRootAttrList = new AttributeList;
150  pRootAttrList->AddAttribute(
151  "xmlns",
152  aCDATAString,
153  "http://schemas.openxmlformats.org/package/2006/relationships" );
154 
155  xWriter->startDocument();
156  xWriter->startElement( aRelListElement, pRootAttrList );
157 
158  for ( const auto & i : aSequence )
159  {
161  for( const beans::StringPair & pair : i )
162  {
163  if ( !(pair.First == "Id"
164  || pair.First == "Type"
165  || pair.First == "TargetMode"
166  || pair.First == "Target") )
167  {
168  // TODO/LATER: should the extensions be allowed?
169  throw lang::IllegalArgumentException();
170  }
171  pAttrList->AddAttribute( pair.First, aCDATAString, pair.Second );
172  }
173 
174  xWriter->startElement( aRelElement, pAttrList );
175  xWriter->ignorableWhitespace( aWhiteSpace );
176  xWriter->endElement( aRelElement );
177  }
178 
179  xWriter->ignorableWhitespace( aWhiteSpace );
180  xWriter->endElement( aRelListElement );
181  xWriter->endDocument();
182 }
183 
184 
186  const uno::Reference< io::XOutputStream >& xOutStream,
187  const uno::Sequence< beans::StringPair >& aDefaultsSequence,
188  const uno::Sequence< beans::StringPair >& aOverridesSequence,
189  const uno::Reference< uno::XComponentContext >& rContext )
190 {
191  if ( !xOutStream.is() )
192  throw uno::RuntimeException();
193 
194  uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(rContext);
195 
196  xWriter->setOutputStream( xOutStream );
197 
198  static constexpr OUStringLiteral aTypesElement(u"Types");
199  static constexpr OUStringLiteral aDefaultElement(u"Default");
200  static constexpr OUStringLiteral aOverrideElement(u"Override");
201  static constexpr OUStringLiteral aContentTypeAttr(u"ContentType");
202  static constexpr OUStringLiteral aCDATAString(u"CDATA");
203  static constexpr OUStringLiteral aWhiteSpace(u" ");
204 
205  // write the namespace
206  rtl::Reference<AttributeList> pRootAttrList = new AttributeList;
207  pRootAttrList->AddAttribute(
208  "xmlns",
209  aCDATAString,
210  "http://schemas.openxmlformats.org/package/2006/content-types" );
211 
212  xWriter->startDocument();
213  xWriter->startElement( aTypesElement, pRootAttrList );
214 
215  for ( const beans::StringPair & pair : aDefaultsSequence )
216  {
218  pAttrList->AddAttribute( "Extension", aCDATAString, pair.First );
219  pAttrList->AddAttribute( aContentTypeAttr, aCDATAString, pair.Second );
220 
221  xWriter->startElement( aDefaultElement, pAttrList );
222  xWriter->ignorableWhitespace( aWhiteSpace );
223  xWriter->endElement( aDefaultElement );
224  }
225 
226  for ( const beans::StringPair & pair : aOverridesSequence )
227  {
229  pAttrList->AddAttribute( "PartName", aCDATAString, pair.First );
230  pAttrList->AddAttribute( aContentTypeAttr, aCDATAString, pair.Second );
231 
232  xWriter->startElement( aOverrideElement, pAttrList );
233  xWriter->ignorableWhitespace( aWhiteSpace );
234  xWriter->endElement( aOverrideElement );
235  }
236 
237  xWriter->ignorableWhitespace( aWhiteSpace );
238  xWriter->endElement( aTypesElement );
239  xWriter->endDocument();
240 
241 }
242 
243 uno::Sequence< uno::Sequence< beans::StringPair > > ReadSequence_Impl(
244  const uno::Reference< io::XInputStream >& xInStream,
245  const OUString& aStringID, sal_uInt16 nFormat,
246  const uno::Reference< uno::XComponentContext >& rContext )
247 {
248  if ( !rContext.is() || !xInStream.is() || nFormat > FORMAT_MAX_ID )
249  throw uno::RuntimeException();
250 
251  uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create( rContext );
252 
253  rtl::Reference<OFOPXMLHelper_Impl> pHelper = new OFOPXMLHelper_Impl( nFormat );
254  css::xml::sax::InputSource aParserInput;
255  aParserInput.aInputStream = xInStream;
256  aParserInput.sSystemId = aStringID;
257  xParser->setDocumentHandler( pHelper );
258  xParser->parseStream( aParserInput );
259  xParser->setDocumentHandler( uno::Reference < css::xml::sax::XDocumentHandler > () );
260 
261  return pHelper->GetParsingResult();
262 }
263 
264 } // namespace OFOPXMLHelper
265 
266 // Relations info related strings
267 constexpr OUStringLiteral g_aRelListElement(u"Relationships");
268 constexpr OUStringLiteral g_aRelElement( u"Relationship" );
269 constexpr OUStringLiteral g_aIDAttr( u"Id" );
270 constexpr OUStringLiteral g_aTypeAttr( u"Type" );
271 constexpr OUStringLiteral g_aTargetModeAttr( u"TargetMode" );
272 constexpr OUStringLiteral g_aTargetAttr( u"Target" );
273 
274 // ContentType related strings
275 constexpr OUStringLiteral g_aTypesElement( u"Types" );
276 constexpr OUStringLiteral g_aDefaultElement( u"Default" );
277 constexpr OUStringLiteral g_aOverrideElement( u"Override" );
278 constexpr OUStringLiteral g_aExtensionAttr( u"Extension" );
279 constexpr OUStringLiteral g_aPartNameAttr( u"PartName" );
280 constexpr OUStringLiteral g_aContentTypeAttr( u"ContentType" );
281 
282 OFOPXMLHelper_Impl::OFOPXMLHelper_Impl( sal_uInt16 nFormat )
283 : m_nFormat( nFormat )
284 {
285 }
286 
287 uno::Sequence< uno::Sequence< beans::StringPair > > const & OFOPXMLHelper_Impl::GetParsingResult() const
288 {
289  if ( !m_aElementsSeq.empty() )
290  throw uno::RuntimeException(); // the parsing has still not finished!
291 
292  return m_aResultSeq;
293 }
294 
295 
296 void SAL_CALL OFOPXMLHelper_Impl::startDocument()
297 {
298 }
299 
300 
301 void SAL_CALL OFOPXMLHelper_Impl::endDocument()
302 {
303 }
304 
305 
306 void SAL_CALL OFOPXMLHelper_Impl::startElement( const OUString& aName, const uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
307 {
309  {
310  if ( aName == g_aRelListElement )
311  {
312  sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
313 
314  if ( nNewLength != 1 )
315  throw css::xml::sax::SAXException(); // TODO: this element must be the first level element
316 
317  m_aElementsSeq.push_back( aName );
318 
319  return; // nothing to do
320  }
321  else if ( aName == g_aRelElement )
322  {
323  sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
324  if ( nNewLength != 2 )
325  throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
326 
327  m_aElementsSeq.push_back( aName );
328 
329  sal_Int32 nNewEntryNum = m_aResultSeq.getLength() + 1;
330  m_aResultSeq.realloc( nNewEntryNum );
331  sal_Int32 nAttrNum = 0;
332  m_aResultSeq[nNewEntryNum-1].realloc( 4 ); // the maximal expected number of arguments is 4
333 
334  OUString aIDValue = xAttribs->getValueByName( g_aIDAttr );
335  if ( aIDValue.isEmpty() )
336  throw css::xml::sax::SAXException(); // TODO: the ID value must present
337 
338  OUString aTypeValue = xAttribs->getValueByName( g_aTypeAttr );
339  OUString aTargetValue = xAttribs->getValueByName( g_aTargetAttr );
340  OUString aTargetModeValue = xAttribs->getValueByName( g_aTargetModeAttr );
341 
342  m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = g_aIDAttr;
343  m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aIDValue;
344 
345  if ( !aTypeValue.isEmpty() )
346  {
347  m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = g_aTypeAttr;
348  m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aTypeValue;
349  }
350 
351  if ( !aTargetValue.isEmpty() )
352  {
353  m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = g_aTargetAttr;
354  m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aTargetValue;
355  }
356 
357  if ( !aTargetModeValue.isEmpty() )
358  {
359  m_aResultSeq[nNewEntryNum-1][++nAttrNum - 1].First = g_aTargetModeAttr;
360  m_aResultSeq[nNewEntryNum-1][nAttrNum - 1].Second = aTargetModeValue;
361  }
362 
363  m_aResultSeq[nNewEntryNum-1].realloc( nAttrNum );
364  }
365  else
366  throw css::xml::sax::SAXException(); // TODO: no other elements expected!
367  }
368  else if ( m_nFormat == CONTENTTYPE_FORMAT )
369  {
370  if ( aName == g_aTypesElement )
371  {
372  sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
373 
374  if ( nNewLength != 1 )
375  throw css::xml::sax::SAXException(); // TODO: this element must be the first level element
376 
377  m_aElementsSeq.push_back( aName );
378 
379  if ( !m_aResultSeq.hasElements() )
380  m_aResultSeq.realloc( 2 );
381 
382  return; // nothing to do
383  }
384  else if ( aName == g_aDefaultElement )
385  {
386  sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
387  if ( nNewLength != 2 )
388  throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
389 
390  m_aElementsSeq.push_back( aName );
391 
392  if ( !m_aResultSeq.hasElements() )
393  m_aResultSeq.realloc( 2 );
394 
395  if ( m_aResultSeq.getLength() != 2 )
396  throw uno::RuntimeException();
397 
398  const OUString aExtensionValue = xAttribs->getValueByName( g_aExtensionAttr );
399  if ( aExtensionValue.isEmpty() )
400  throw css::xml::sax::SAXException(); // TODO: the Extension value must present
401 
402  const OUString aContentTypeValue = xAttribs->getValueByName( g_aContentTypeAttr );
403  if ( aContentTypeValue.isEmpty() )
404  throw css::xml::sax::SAXException(); // TODO: the ContentType value must present
405 
406  const sal_Int32 nNewResultLen = m_aResultSeq[0].getLength() + 1;
407  m_aResultSeq[0].realloc( nNewResultLen );
408 
409  m_aResultSeq[0][nNewResultLen-1].First = aExtensionValue;
410  m_aResultSeq[0][nNewResultLen-1].Second = aContentTypeValue;
411  }
412  else if ( aName == g_aOverrideElement )
413  {
414  sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
415  if ( nNewLength != 2 )
416  throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
417 
418  m_aElementsSeq.push_back( aName );
419 
420  if ( !m_aResultSeq.hasElements() )
421  m_aResultSeq.realloc( 2 );
422 
423  if ( m_aResultSeq.getLength() != 2 )
424  throw uno::RuntimeException();
425 
426  OUString aPartNameValue = xAttribs->getValueByName( g_aPartNameAttr );
427  if ( aPartNameValue.isEmpty() )
428  throw css::xml::sax::SAXException(); // TODO: the PartName value must present
429 
430  OUString aContentTypeValue = xAttribs->getValueByName( g_aContentTypeAttr );
431  if ( aContentTypeValue.isEmpty() )
432  throw css::xml::sax::SAXException(); // TODO: the ContentType value must present
433 
434  sal_Int32 nNewResultLen = m_aResultSeq[1].getLength() + 1;
435  m_aResultSeq[1].realloc( nNewResultLen );
436 
437  m_aResultSeq[1][nNewResultLen-1].First = aPartNameValue;
438  m_aResultSeq[1][nNewResultLen-1].Second = aContentTypeValue;
439  }
440  else
441  throw css::xml::sax::SAXException(); // TODO: no other elements expected!
442  }
443  else
444  throw css::xml::sax::SAXException(); // TODO: no other elements expected!
445 }
446 
447 
448 void SAL_CALL OFOPXMLHelper_Impl::endElement( const OUString& aName )
449 {
451  {
452  sal_Int32 nLength = m_aElementsSeq.size();
453  if ( nLength <= 0 )
454  throw css::xml::sax::SAXException(); // TODO: no other end elements expected!
455 
456  if ( m_aElementsSeq[nLength-1] != aName )
457  throw css::xml::sax::SAXException(); // TODO: unexpected element ended
458 
459  m_aElementsSeq.resize( nLength - 1 );
460  }
461 }
462 
463 
464 void SAL_CALL OFOPXMLHelper_Impl::characters( const OUString& /*aChars*/ )
465 {
466 }
467 
468 
469 void SAL_CALL OFOPXMLHelper_Impl::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
470 {
471 }
472 
473 
474 void SAL_CALL OFOPXMLHelper_Impl::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
475 {
476 }
477 
478 
479 void SAL_CALL OFOPXMLHelper_Impl::setDocumentLocator( const uno::Reference< css::xml::sax::XLocator >& /*xLocator*/ )
480 {
481 }
482 
483 } // namespace comphelper
484 
485 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt16 const m_nFormat
constexpr OUStringLiteral g_aDefaultElement(u"Default")
constexpr OUStringLiteral g_aIDAttr(u"Id")
constexpr OUStringLiteral g_aRelListElement(u"Relationships")
css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aResultSeq
constexpr OUStringLiteral g_aOverrideElement(u"Override")
#define CONTENTTYPE_FORMAT
constexpr OUStringLiteral g_aExtensionAttr(u"Extension")
void WriteContentSequence(const uno::Reference< io::XOutputStream > &xOutStream, const uno::Sequence< beans::StringPair > &aDefaultsSequence, const uno::Sequence< beans::StringPair > &aOverridesSequence, const uno::Reference< uno::XComponentContext > &rContext)
constexpr OUStringLiteral g_aTypeAttr(u"Type")
OUString GetContentTypeByName(const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair >> &rContentTypes, const OUString &rFilename)
int i
std::vector< OUString > m_aElementsSeq
float u
void WriteRelationsInfoSequence(const uno::Reference< io::XOutputStream > &xOutStream, const uno::Sequence< uno::Sequence< beans::StringPair > > &aSequence, const uno::Reference< uno::XComponentContext > &rContext)
constexpr OUStringLiteral g_aPartNameAttr(u"PartName")
constexpr OUStringLiteral g_aContentTypeAttr(u"ContentType")
constexpr OUStringLiteral g_aTargetModeAttr(u"TargetMode")
uno::Sequence< uno::Sequence< beans::StringPair > > ReadRelationsInfoSequence(const uno::Reference< io::XInputStream > &xInStream, std::u16string_view aStreamName, const uno::Reference< uno::XComponentContext > &rContext)
OUString aName
constexpr OUStringLiteral g_aTargetAttr(u"Target")
constexpr OUStringLiteral g_aRelElement(u"Relationship")
#define RELATIONINFO_FORMAT
constexpr OUStringLiteral g_aTypesElement(u"Types")
sal_Int32 nLength
uno::Sequence< uno::Sequence< beans::StringPair > > ReadContentTypeSequence(const uno::Reference< io::XInputStream > &xInStream, const uno::Reference< uno::XComponentContext > &rContext)
#define FORMAT_MAX_ID
static uno::Sequence< uno::Sequence< beans::StringPair > > ReadSequence_Impl(const uno::Reference< io::XInputStream > &xInStream, const OUString &aStringID, sal_uInt16 nFormat, const uno::Reference< uno::XComponentContext > &xContext)