/src/libreoffice/oox/source/mathml/importutils.cxx
Line | Count | Source |
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 | | |
10 | | #include <oox/mathml/importutils.hxx> |
11 | | |
12 | | #include <assert.h> |
13 | | |
14 | | #include <com/sun/star/xml/FastAttribute.hpp> |
15 | | #include <com/sun/star/xml/sax/XFastAttributeList.hpp> |
16 | | #include <oox/token/tokenmap.hxx> |
17 | | #include <oox/token/tokens.hxx> |
18 | | #include <oox/token/namespaces.hxx> |
19 | | #include <rtl/ustring.hxx> |
20 | | #include <sal/log.hxx> |
21 | | #include <utility> |
22 | | |
23 | 16.2k | #define OPENING( token ) XML_STREAM_OPENING( token ) |
24 | 16.2k | #define CLOSING( token ) XML_STREAM_CLOSING( token ) |
25 | | |
26 | | using namespace com::sun::star; |
27 | | |
28 | | namespace oox::formulaimport |
29 | | { |
30 | | |
31 | | namespace |
32 | | { |
33 | | // a class that inherits from AttributeList, builds the internal data and then will be sliced off |
34 | | // during conversion to the base class |
35 | | class AttributeListBuilder |
36 | | : public XmlStream::AttributeList |
37 | | { |
38 | | public: |
39 | | explicit AttributeListBuilder( const uno::Reference< xml::sax::XFastAttributeList >& a ); |
40 | | }; |
41 | | |
42 | | AttributeListBuilder::AttributeListBuilder( const uno::Reference< xml::sax::XFastAttributeList >& a ) |
43 | 32.4k | { |
44 | 32.4k | if( !a ) |
45 | 16.4k | return; |
46 | 15.9k | const uno::Sequence< xml::FastAttribute > aFastAttrSeq = a->getFastAttributes(); |
47 | 15.9k | for( const xml::FastAttribute& rFastAttr : aFastAttrSeq ) |
48 | 10.3k | { |
49 | 10.3k | attrs[ rFastAttr.Token ] = rFastAttr.Value; |
50 | 10.3k | } |
51 | 15.9k | } |
52 | | |
53 | | OString tokenToString( int token ) |
54 | 0 | { |
55 | 0 | uno::Sequence<sal_Int8> const& aTokenNameSeq = TokenMap::getUtf8TokenName(token & TOKEN_MASK); |
56 | 0 | OString tokenname( reinterpret_cast< const char* >( aTokenNameSeq.getConstArray() ), aTokenNameSeq.getLength() ); |
57 | 0 | if( tokenname.isEmpty()) |
58 | 0 | tokenname = "???"_ostr; |
59 | 0 | int nmsp = ( token & NMSP_MASK & ~( TAG_OPENING | TAG_CLOSING )); |
60 | | #if 0 // this is awfully long |
61 | | OString namespacename = StaticNamespaceMap::get().count( nmsp ) != 0 |
62 | | ? StaticNamespaceMap::get()[ nmsp ] : OString( "???" ); |
63 | | #else |
64 | 0 | OString namespacename; |
65 | | // only few are needed actually |
66 | 0 | switch( nmsp ) |
67 | 0 | { |
68 | 0 | case NMSP_officeMath: |
69 | 0 | namespacename = "m"_ostr; |
70 | 0 | break; |
71 | 0 | case NMSP_doc: |
72 | 0 | namespacename = "w"_ostr; |
73 | 0 | break; |
74 | 0 | default: |
75 | 0 | namespacename = "?"_ostr; |
76 | 0 | break; |
77 | 0 | } |
78 | 0 | #endif |
79 | 0 | if( token == OPENING( token )) |
80 | 0 | return "<" + namespacename + ":" + tokenname + ">"; |
81 | 0 | if( token == CLOSING( token )) |
82 | 0 | return "</" + namespacename + ":" + tokenname + ">"; |
83 | | // just the name itself, not specified whether opening or closing |
84 | 0 | return namespacename + ":" + tokenname; |
85 | 0 | } |
86 | | |
87 | | } // namespace |
88 | | |
89 | | OUString& XmlStream::AttributeList::operator[] (int token) |
90 | 0 | { |
91 | 0 | return attrs[token]; |
92 | 0 | } |
93 | | |
94 | | OUString XmlStream::AttributeList::attribute( int token, const OUString& def ) const |
95 | 0 | { |
96 | 0 | std::map< int, OUString >::const_iterator find = attrs.find( token ); |
97 | 0 | if( find != attrs.end()) |
98 | 0 | return find->second; |
99 | 0 | return def; |
100 | 0 | } |
101 | | |
102 | | bool XmlStream::AttributeList::attribute( int token, bool def ) const |
103 | 0 | { |
104 | 0 | std::map< int, OUString >::const_iterator find = attrs.find( token ); |
105 | 0 | if( find != attrs.end()) |
106 | 0 | { |
107 | 0 | const OUString sValue = find->second; |
108 | 0 | if( sValue.equalsIgnoreAsciiCase("true") || |
109 | 0 | sValue.equalsIgnoreAsciiCase("on") || |
110 | 0 | sValue.equalsIgnoreAsciiCase("t") || |
111 | 0 | sValue.equalsIgnoreAsciiCase("1") ) |
112 | 0 | return true; |
113 | 0 | if( sValue.equalsIgnoreAsciiCase("false") || |
114 | 0 | sValue.equalsIgnoreAsciiCase("off") || |
115 | 0 | sValue.equalsIgnoreAsciiCase("f") || |
116 | 0 | sValue.equalsIgnoreAsciiCase("0") ) |
117 | 0 | return false; |
118 | 0 | SAL_WARN( "oox.xmlstream", "Cannot convert \'" << sValue << "\' to bool." ); |
119 | 0 | } |
120 | 0 | return def; |
121 | 0 | } |
122 | | |
123 | | sal_Unicode XmlStream::AttributeList::attribute( int token, sal_Unicode def ) const |
124 | 0 | { |
125 | 0 | std::map< int, OUString >::const_iterator find = attrs.find( token ); |
126 | 0 | if( find != attrs.end()) |
127 | 0 | { |
128 | 0 | if( !find->second.isEmpty() ) |
129 | 0 | { |
130 | 0 | if( find->second.getLength() != 1 ) |
131 | 0 | SAL_WARN( "oox.xmlstream", "Cannot convert \'" << find->second << "\' to sal_Unicode, stripping." ); |
132 | 0 | return find->second[ 0 ]; |
133 | 0 | } |
134 | 0 | } |
135 | 0 | return def; |
136 | 0 | } |
137 | | |
138 | | XmlStream::Tag::Tag( int t, const uno::Reference< xml::sax::XFastAttributeList >& a ) |
139 | 32.4k | : token( t ) |
140 | 32.4k | , attributes( AttributeListBuilder( a )) |
141 | 32.4k | { |
142 | 32.4k | } |
143 | | |
144 | | XmlStream::Tag::Tag( int t, AttributeList a ) |
145 | 0 | : token( t ) |
146 | 0 | , attributes(std::move( a )) |
147 | 0 | { |
148 | 0 | } |
149 | | |
150 | | XmlStream::Tag::operator bool() const |
151 | 0 | { |
152 | 0 | return token != XML_TOKEN_INVALID; |
153 | 0 | } |
154 | | |
155 | | XmlStream::XmlStream() |
156 | 10.7k | : pos( 0 ) |
157 | 10.7k | { |
158 | | // make sure our extra bit does not conflict with values used by oox |
159 | 10.7k | assert( TAG_OPENING > ( 1024 << NMSP_SHIFT )); |
160 | 10.7k | } |
161 | | |
162 | | bool XmlStream::atEnd() const |
163 | 0 | { |
164 | 0 | return pos >= tags.size(); |
165 | 0 | } |
166 | | |
167 | | XmlStream::Tag XmlStream::currentTag() const |
168 | 0 | { |
169 | 0 | if( pos >= tags.size()) |
170 | 0 | return Tag(); |
171 | 0 | return tags[ pos ]; |
172 | 0 | } |
173 | | |
174 | | int XmlStream::currentToken() const |
175 | 0 | { |
176 | 0 | if( pos >= tags.size()) |
177 | 0 | return XML_TOKEN_INVALID; |
178 | 0 | return tags[ pos ].token; |
179 | 0 | } |
180 | | |
181 | | void XmlStream::moveToNextTag() |
182 | 0 | { |
183 | 0 | if( pos < tags.size()) |
184 | 0 | ++pos; |
185 | 0 | } |
186 | | |
187 | | XmlStream::Tag XmlStream::ensureOpeningTag( int token ) |
188 | 0 | { |
189 | 0 | return checkTag( OPENING( token ), false ); |
190 | 0 | } |
191 | | |
192 | | XmlStream::Tag XmlStream::checkOpeningTag( int token ) |
193 | 0 | { |
194 | 0 | return checkTag( OPENING( token ), true ); |
195 | 0 | } |
196 | | |
197 | | void XmlStream::ensureClosingTag( int token ) |
198 | 0 | { |
199 | 0 | checkTag( CLOSING( token ), false ); |
200 | 0 | } |
201 | | |
202 | | XmlStream::Tag XmlStream::checkTag( int token, bool optional ) |
203 | 0 | { |
204 | | // either it's the following tag, or find it |
205 | 0 | int savedPos = pos; |
206 | 0 | if( optional ) |
207 | 0 | { // avoid printing debug messages about skipping tags if the optional one |
208 | | // will not be found and the position will be reset back |
209 | 0 | if( currentToken() != token && !findTagInternal( token, true )) |
210 | 0 | { |
211 | 0 | pos = savedPos; |
212 | 0 | return Tag(); |
213 | 0 | } |
214 | 0 | } |
215 | 0 | if( currentToken() == token || findTag( token )) |
216 | 0 | { |
217 | 0 | Tag ret = currentTag(); |
218 | 0 | moveToNextTag(); |
219 | 0 | return ret; // ok |
220 | 0 | } |
221 | 0 | if( optional ) |
222 | 0 | { // not a problem, just rewind |
223 | 0 | pos = savedPos; |
224 | 0 | return Tag(); |
225 | 0 | } |
226 | 0 | SAL_WARN( "oox.xmlstream", "Expected tag " << tokenToString( token ) << " not found." ); |
227 | 0 | return Tag(); |
228 | 0 | } |
229 | | |
230 | | bool XmlStream::findTag( int token ) |
231 | 0 | { |
232 | 0 | return findTagInternal( token, false ); |
233 | 0 | } |
234 | | |
235 | | bool XmlStream::findTagInternal( int token, bool silent ) |
236 | 0 | { |
237 | 0 | int depth = 0; |
238 | 0 | for(; |
239 | 0 | !atEnd(); |
240 | 0 | moveToNextTag()) |
241 | 0 | { |
242 | 0 | if( depth > 0 ) // we're inside a nested element, skip those |
243 | 0 | { |
244 | 0 | if( currentToken() == OPENING( currentToken())) |
245 | 0 | { |
246 | 0 | if( !silent ) |
247 | 0 | SAL_INFO( "oox.xmlstream", "Skipping tag " << tokenToString( currentToken())); |
248 | 0 | ++depth; |
249 | 0 | } |
250 | 0 | else if( currentToken() == CLOSING( currentToken())) |
251 | 0 | { |
252 | 0 | if( !silent ) |
253 | 0 | SAL_INFO( "oox.xmlstream", "Skipping tag " << tokenToString( currentToken())); |
254 | 0 | --depth; |
255 | 0 | } |
256 | 0 | else |
257 | 0 | { |
258 | 0 | if( !silent ) |
259 | 0 | SAL_WARN( "oox.xmlstream", "Malformed token " << currentToken() << " (" |
260 | 0 | << tokenToString( currentToken()) << ")" ); |
261 | 0 | abort(); |
262 | 0 | } |
263 | 0 | continue; |
264 | 0 | } |
265 | 0 | if( currentToken() == token ) |
266 | 0 | return true; // ok, found |
267 | 0 | if( currentToken() == CLOSING( currentToken())) |
268 | 0 | return false; // that would be leaving current element, so not found |
269 | 0 | if( currentToken() == OPENING( currentToken())) |
270 | 0 | { |
271 | 0 | if( !silent ) |
272 | 0 | SAL_INFO( "oox.xmlstream", "Skipping tag " << tokenToString( currentToken())); |
273 | 0 | ++depth; |
274 | 0 | } |
275 | 0 | else |
276 | 0 | abort(); |
277 | 0 | } |
278 | 0 | if( !silent ) |
279 | 0 | SAL_WARN( "oox.xmlstream", "Unexpected end of stream reached." ); |
280 | 0 | return false; |
281 | 0 | } |
282 | | |
283 | | void XmlStream::skipElementInternal( int token, bool silent ) |
284 | 0 | { |
285 | 0 | int closing = ( token & ~TAG_OPENING ) | TAG_CLOSING; // make it a closing tag |
286 | 0 | assert( currentToken() == OPENING( token )); |
287 | 0 | if( !silent ) |
288 | 0 | SAL_INFO( "oox.xmlstream", "Skipping unexpected element " << tokenToString( currentToken())); |
289 | 0 | moveToNextTag(); |
290 | | // and just find the matching closing tag |
291 | 0 | if( findTag( closing )) |
292 | 0 | { |
293 | 0 | if( !silent ) |
294 | 0 | SAL_INFO( "oox.xmlstream", "Skipped unexpected element " << tokenToString( token )); |
295 | 0 | moveToNextTag(); // and skip it too |
296 | 0 | return; |
297 | 0 | } |
298 | | // this one is an unexpected problem, do not silent it |
299 | 0 | SAL_WARN( "oox.xmlstream", "Expected end of element " << tokenToString( token ) << " not found." ); |
300 | 0 | } |
301 | | |
302 | | void XmlStream::handleUnexpectedTag() |
303 | 0 | { |
304 | 0 | if( atEnd()) |
305 | 0 | return; |
306 | 0 | if( currentToken() == CLOSING( currentToken())) |
307 | 0 | { |
308 | 0 | SAL_INFO( "oox.xmlstream", "Skipping unexpected tag " << tokenToString( currentToken())); |
309 | 0 | moveToNextTag(); // just skip it |
310 | 0 | return; |
311 | 0 | } |
312 | 0 | skipElementInternal( currentToken(), false ); // otherwise skip the entire element |
313 | 0 | } |
314 | | |
315 | | std::vector<OUString> XmlStream::tagAttributesWithin( int nEnclosingToken, int nTagToken, |
316 | | int nAttributeToken ) const |
317 | 0 | { |
318 | 0 | std::vector<OUString> aValues; |
319 | 0 | int nDepth = 0; |
320 | 0 | for( const Tag& rTag : tags ) |
321 | 0 | { |
322 | 0 | if( rTag.token == OPENING( nEnclosingToken )) |
323 | 0 | ++nDepth; |
324 | 0 | else if( rTag.token == CLOSING( nEnclosingToken )) |
325 | 0 | --nDepth; |
326 | 0 | else if( nDepth > 0 && rTag.token == OPENING( nTagToken )) |
327 | 0 | aValues.push_back( rTag.attribute( nAttributeToken )); |
328 | 0 | } |
329 | 0 | return aValues; |
330 | 0 | } |
331 | | |
332 | | void XmlStreamBuilder::appendOpeningTag( int token, const uno::Reference< xml::sax::XFastAttributeList >& attrs ) |
333 | 16.2k | { |
334 | 16.2k | tags.emplace_back( OPENING( token ), attrs ); |
335 | 16.2k | } |
336 | | |
337 | | void XmlStreamBuilder::appendOpeningTag( int token, const AttributeList& attrs ) |
338 | 0 | { |
339 | 0 | tags.emplace_back( OPENING( token ), attrs ); |
340 | 0 | } |
341 | | |
342 | | void XmlStreamBuilder::appendClosingTag( int token ) |
343 | 16.2k | { |
344 | 16.2k | tags.emplace_back( CLOSING( token )); |
345 | 16.2k | } |
346 | | |
347 | | void XmlStreamBuilder::appendCharacters( std::u16string_view chars ) |
348 | 1.99k | { |
349 | | assert( !tags.empty()); |
350 | 1.99k | tags.back().text += chars; |
351 | 1.99k | } |
352 | | |
353 | | } // namespace |
354 | | |
355 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |