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