LibreOffice Module oox (master)  1
contexthandler2.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 
23 #include <oox/token/namespaces.hxx>
24 #include <oox/token/tokens.hxx>
25 #include <rtl/ustrbuf.hxx>
26 #include <o3tl/safeint.hxx>
27 #include <osl/diagnose.h>
28 #include <com/sun/star/frame/XModel.hpp>
29 #include <com/sun/star/lang/XServiceInfo.hpp>
30 
31 namespace oox::core {
32 
33 using namespace ::com::sun::star::uno;
34 using namespace ::com::sun::star::lang;
35 using namespace ::com::sun::star::xml::sax;
36 
39 {
40  OUStringBuffer maChars;
41  sal_Int32 mnElement;
42  bool mbTrimSpaces;
43 
44  explicit ElementInfo() : maChars( 0), mnElement( XML_TOKEN_INVALID ), mbTrimSpaces( false ) {}
45 };
46 
47 ContextHandler2Helper::ContextHandler2Helper( bool bEnableTrimSpace, XmlFilterBase& rFilter ) :
48  mxContextStack( std::make_shared<ContextStack>() ),
49  mnRootStackSize( 0 ),
50  mbEnableTrimSpace( bEnableTrimSpace ),
51  mrFilter( rFilter )
52 {
54 }
55 
57  mxContextStack( rParent.mxContextStack ),
58  mnRootStackSize( rParent.mxContextStack->size() ),
59  mbEnableTrimSpace( rParent.mbEnableTrimSpace ),
60  mrFilter(rParent.mrFilter)
61 {
62 }
63 
65 {
66 }
67 
69 {
70  return mxContextStack->empty() ? XML_ROOT_CONTEXT : mxContextStack->back().mnElement;
71 }
72 
74 {
75  auto It = std::find_if(mxContextStack->rbegin(), mxContextStack->rend(),
76  [](const ElementInfo& rItem) { return getNamespace(rItem.mnElement) != NMSP_mce; });
77  if (It != mxContextStack->rend())
78  return It->mnElement;
79  return XML_ROOT_CONTEXT;
80 }
81 
82 sal_Int32 ContextHandler2Helper::getParentElement( sal_Int32 nCountBack ) const
83 {
84  if( (nCountBack < 0) || (mxContextStack->size() < o3tl::make_unsigned( nCountBack )) )
85  return XML_TOKEN_INVALID;
86  return (mxContextStack->size() == static_cast< size_t >( nCountBack )) ?
87  XML_ROOT_CONTEXT : (*mxContextStack)[ mxContextStack->size() - nCountBack - 1 ].mnElement;
88 }
89 
91 {
92  return mxContextStack->size() == mnRootStackSize + 1;
93 }
94 
95 Reference< XFastContextHandler > ContextHandler2Helper::implCreateChildContext(
96  sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
97 {
98  // #i76091# process collected characters (calls onCharacters() if needed)
100  ContextHandlerRef xContext = onCreateContext( nElement, AttributeList( rxAttribs ) );
101  return xContext;
102 }
103 
104 void ContextHandler2Helper::implStartElement( sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
105 {
106  AttributeList aAttribs( rxAttribs );
107  pushElementInfo( nElement ).mbTrimSpaces = aAttribs.getToken( XML_TOKEN( space ), XML_TOKEN_INVALID ) != XML_preserve;
108  onStartElement( aAttribs );
109 }
110 
111 void ContextHandler2Helper::implCharacters( std::u16string_view rChars )
112 {
113  // #i76091# collect characters until new element starts or this element ends
114  if( !mxContextStack->empty() )
115  mxContextStack->back().maChars.append(rChars);
116 }
117 
118 void ContextHandler2Helper::implEndElement( sal_Int32 nElement )
119 {
120  OSL_ENSURE( getCurrentElementWithMce() == nElement, "ContextHandler2Helper::implEndElement - context stack broken" );
121  if( !mxContextStack->empty() )
122  {
123  // #i76091# process collected characters (calls onCharacters() if needed)
125  onEndElement();
126  popElementInfo();
127  }
128 }
129 
131 {
132  return onCreateRecordContext( nRecId, rStrm );
133 }
134 
136 {
137  pushElementInfo( nRecId );
138  onStartRecord( rStrm );
139 }
140 
141 void ContextHandler2Helper::implEndRecord( sal_Int32 nRecId )
142 {
143  OSL_ENSURE( getCurrentElementWithMce() == nRecId, "ContextHandler2Helper::implEndRecord - context stack broken" );
144  if( !mxContextStack->empty() )
145  {
146  onEndRecord();
147  popElementInfo();
148  }
149 }
150 
152 {
153  mxContextStack->emplace_back();
154  ElementInfo& rInfo = mxContextStack->back();
155  rInfo.mnElement = nElement;
156  return rInfo;
157 }
158 
160 {
161  OSL_ENSURE( !mxContextStack->empty(), "ContextHandler2Helper::popElementInfo - context stack broken" );
162  if( !mxContextStack->empty() )
163  mxContextStack->pop_back();
164 }
165 
167 {
168  OSL_ENSURE( !mxContextStack->empty(), "ContextHandler2Helper::processCollectedChars - no context info" );
169  if (mxContextStack->empty())
170  return;
171  ElementInfo& rInfo = mxContextStack->back();
172  if( !rInfo.maChars.isEmpty() )
173  {
174  OUString aChars = rInfo.maChars.makeStringAndClear();
175  if( mbEnableTrimSpace && rInfo.mbTrimSpaces )
176  aChars = aChars.trim();
177  if( !aChars.isEmpty() )
178  onCharacters( aChars );
179  }
180 }
181 
183  ContextHandler( dynamic_cast< ContextHandler const & >( rParent ) ),
184  ContextHandler2Helper( rParent )
185 {
186 }
187 
189 {
190 }
191 
192 // com.sun.star.xml.sax.XFastContextHandler interface -------------------------
193 
194 Reference< XFastContextHandler > SAL_CALL ContextHandler2::createFastChildContext(
195  sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
196 {
197  if( getNamespace( nElement ) == NMSP_mce ) // TODO for checking 'Ignorable'
198  {
199  if( prepareMceContext( nElement, AttributeList( rxAttribs ) ) )
200  return this;
201  return nullptr;
202  }
203 
204  return implCreateChildContext( nElement, rxAttribs );
205 }
206 
208  sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
209 {
210  implStartElement( nElement, rxAttribs );
211 }
212 
213 void SAL_CALL ContextHandler2::characters( const OUString& rChars )
214 {
215  implCharacters( rChars );
216 }
217 
218 void SAL_CALL ContextHandler2::endFastElement( sal_Int32 nElement )
219 {
220  implEndElement( nElement );
221 }
222 
223 bool ContextHandler2Helper::prepareMceContext( sal_Int32 nElement, const AttributeList& rAttribs )
224 {
225  switch( nElement )
226  {
227  case MCE_TOKEN( AlternateContent ):
229  break;
230 
231  case MCE_TOKEN( Choice ):
232  {
234  return false;
235 
236  OUString aRequires = rAttribs.getString( XML_Requires, "none" );
237 
238  // At this point we can't access namespaces as the correct xml filter
239  // is long gone. For now let's decide depending on a list of supported
240  // namespaces like we do in writerfilter
241 
242  std::vector<OUString> aSupportedNS =
243  {
244  "a14", // Impress needs this to import math formulas.
245  "p14",
246  "p15",
247  "x12ac",
248  "v"
249  };
250 
251  Reference<XServiceInfo> xModel(getDocFilter().getModel(), UNO_QUERY);
252  if (xModel.is() && xModel->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
253  {
254  // No a14 for Calc documents, it would cause duplicated shapes as-is.
255  auto it = std::find(aSupportedNS.begin(), aSupportedNS.end(), "a14");
256  if (it != aSupportedNS.end())
257  {
258  aSupportedNS.erase(it);
259  }
260  }
261 
262  if (std::find(aSupportedNS.begin(), aSupportedNS.end(), aRequires) != aSupportedNS.end())
264  else
265  return false;
266  }
267  break;
268 
269  case MCE_TOKEN( Fallback ):
271  break;
272  return false;
273  default:
274  {
275  OUString str = rAttribs.getString( MCE_TOKEN( Ignorable ), OUString() );
276  if( !str.isEmpty() )
277  {
278  // Sequence< css::xml::FastAttribute > attrs = rAttribs.getFastAttributeList()->getFastAttributes();
279  // printf("MCE: %s\n", OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
280  // TODO: Check & Get the namespaces in "Ignorable"
281  // printf("NS: %d : %s\n", attrs.getLength(), OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
282  }
283  }
284  return false;
285  }
286  return true;
287 }
288 
289 // oox.core.RecordContext interface -------------------------------------------
290 
292 {
293  return implCreateRecordContext( nRecId, rStrm );
294 }
295 
296 void ContextHandler2::startRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
297 {
298  implStartRecord( nRecId, rStrm );
299 }
300 
301 void ContextHandler2::endRecord( sal_Int32 nRecId )
302 {
303  implEndRecord( nRecId );
304 }
305 
306 // oox.core.ContextHandler2Helper interface -----------------------------------
307 
309 {
310  return nullptr;
311 }
312 
314 {
315 }
316 
317 void ContextHandler2::onCharacters( const OUString& )
318 {
319 }
320 
322 {
323 }
324 
326 {
327  return nullptr;
328 }
329 
331 {
332 }
333 
335 {
336 }
337 
338 } // namespace oox::core
339 
340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Information about a processed element.
virtual void onEndElement() override
Will be called when the current element is about to be left.
void implEndRecord(sal_Int32 nRecId)
Must be called from endRecord() in derived classes.
css::uno::Reference< css::xml::sax::XFastContextHandler > implCreateChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &rxAttribs)
Must be called from createFastChildContext() in derived classes.
virtual void onStartElement(const AttributeList &rAttribs) override
Will be called when a new element has been started.
std::vector< ElementInfo > ContextStack
void implStartElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &rxAttribs)
Must be called from startFastElement() in derived classes.
bool mbTrimSpaces
The element identifier.
OptValue< OUString > getString(sal_Int32 nAttrToken) const
Returns the string value of the specified attribute.
std::shared_ptr< T > make_shared(Args &&...args)
sal_Int32 getParentElement(sal_Int32 nCountBack=1) const
Returns the identifier of the specified parent element.
bool prepareMceContext(sal_Int32 nElement, const AttributeList &rAttribs)
size_t mnRootStackSize
Stack size on construction time.
sal_Int32 getCurrentElementWithMce() const
Returns the identifier of the currently processed element - Including MCE root elements.
virtual ContextHandlerRef onCreateRecordContext(sal_Int32 nRecId, SequenceInputStream &rStrm) override
Will be called to create a context handler for the passed record.
bool mbEnableTrimSpace
True = trim whitespace in characters().
virtual void SAL_CALL endFastElement(sal_Int32 nElement) final override
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &rxAttribs) final override
virtual void onEndRecord()=0
Will be called when the current record block is about to be left.
XML_TOKEN_INVALID
virtual void onEndElement()=0
Will be called when the current element is about to be left.
virtual void SAL_CALL characters(const OUString &rChars) final override
virtual void onCharacters(const OUString &rChars) override
Will be called before a new child element starts, or if the current element is about to be left...
void implStartRecord(sal_Int32 nRecId, SequenceInputStream &rStrm)
Must be called from startRecord() in derived classes.
ContextHandler2Helper(bool bEnableTrimSpace, XmlFilterBase &rFilter)
XmlFilterBase & getDocFilter() const
virtual ~ContextHandler2() override
virtual ContextHandlerRef onCreateContext(sal_Int32 nElement, const AttributeList &rAttribs) override
Will be called to create a context handler for the passed element.
sal_Int32 mnElement
Collected element characters.
ContextStackRef mxContextStack
Stack of all processed elements.
sal_Int32 getCurrentElement() const
Returns the identifier of the currently processed element.
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
size
virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &rxAttribs) final override
virtual void onEndRecord() override
Will be called when the current record block is about to be left.
virtual ContextHandlerRef onCreateRecordContext(sal_Int32 nRecId, SequenceInputStream &rStrm)=0
Will be called to create a context handler for the passed record.
virtual void onStartElement(const AttributeList &rAttribs)=0
Will be called when a new element has been started.
Provides access to attribute values of an element.
bool isRootElement() const
Returns true, if the element currently processed is the root element of the context or fragment handl...
ElementInfo()
True = trims leading/trailing spaces from text data.
virtual void startRecord(sal_Int32 nRecId, SequenceInputStream &rStrm) override
void implEndElement(sal_Int32 nElement)
Must be called from endFastElement() in derived classes.
virtual void endRecord(sal_Int32 nRecId) override
virtual ContextHandlerRef onCreateContext(sal_Int32 nElement, const AttributeList &rAttribs)=0
Will be called to create a context handler for the passed element.
void implCharacters(std::u16string_view rChars)
Must be called from characters() in derived classes.
ContextHandler2(ContextHandler2Helper const &rParent)
void setMCEState(MCE_STATE aState)
virtual void onStartRecord(SequenceInputStream &rStrm)=0
Will be called when a new record block in a binary stream has been started.
Wraps a StreamDataSequence and provides convenient access functions.
virtual void onCharacters(const OUString &rChars)=0
Will be called before a new child element starts, or if the current element is about to be left...
virtual ContextHandlerRef createRecordContext(sal_Int32 nRecId, SequenceInputStream &rStrm) override
const sal_Int32 XML_ROOT_CONTEXT
void addMCEState(MCE_STATE aState)
ContextHandlerRef implCreateRecordContext(sal_Int32 nRecId, SequenceInputStream &rStrm)
Must be called from createRecordContext() in derived classes.
Reference< XModel > xModel
const PowerPointImport & mrFilter
Definition: pptimport.cxx:253
virtual void onStartRecord(SequenceInputStream &rStrm) override
Will be called when a new record block in a binary stream has been started.
Helper class that provides a context stack.
OptValue< sal_Int32 > getToken(sal_Int32 nAttrToken) const
Returns the token identifier of the value of the specified attribute.
ElementInfo & pushElementInfo(sal_Int32 nElement)