1 /*
2 * This file is part of Office 2007 Filters for Calligra
3 *
4 * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
5 *
6 * Contact: Suresh Chande suresh.chande@nokia.com
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * version 2.1 as published by the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24 #ifndef MSOOXMLREADER_P_H
25 #define MSOOXMLREADER_P_H
26
27 #include <klocalizedstring.h>
28
29 #ifndef MSOOXML_CURRENT_CLASS
30 #error Please include MsooXmlReader_p.h after defining MSOOXML_CURRENT_CLASS and MSOOXML_CURRENT_NS!
31 #endif
32
33 #define PASTE2(a, b) a##b
34 #define PASTE(a, b) PASTE2( a, b) // indirection needed because only function-like macro parameters can be pasted
35
36 #define PASTE3_(a, b, c) a##b##c
37 #define PASTE3(a, b, c) PASTE3_( a, b, c) // indirection needed because only function-like macro parameters can be pasted
38
39 #define JOIN2(a, b) a#b
40 #define JOIN(a, b) JOIN2( a, b) // indirection needed because only function-like macro parameters can be pasted
41
42 #define STRINGIFY(s) JOIN("", s)
43
44 //! Used to pass context: creates enum value {el}_{CURRENT_EL}
45 #define PASS_CONTEXT(el) PASTE3(el, _, CURRENT_EL)
46
47 #define TRY_READ_WITH_ARGS_INTERNAL(name, args, context) \
48 args \
49 RETURN_IF_ERROR( read_ ## name (context) )
50
51 #define TRY_READ_WITH_ARGS(name, args) \
52 TRY_READ_WITH_ARGS_INTERNAL(name, m_read_ ## name ## _args = args,)
53
54 #define TRY_READ_WITH_ARGS_IN_CONTEXT(name, args) \
55 TRY_READ_WITH_ARGS_INTERNAL(name, m_read_ ## name ## _args = args, PASS_CONTEXT(name))
56
57 #define TRY_READ(name) \
58 TRY_READ_WITH_ARGS_INTERNAL(name, ,)
59
60 #define TRY_READ_IN_CONTEXT(name) \
61 TRY_READ_WITH_ARGS_INTERNAL(name, , PASS_CONTEXT(name))
62
63 #ifdef MSOOXML_CURRENT_NS
64 # define QUALIFIED_NAME(name) \
65 JOIN(MSOOXML_CURRENT_NS ":",name)
66 #else
67 # define QUALIFIED_NAME(name) \
68 STRINGIFY(name)
69 #endif
70
71 #ifdef NDEBUG
72 # define PUSH_NAME_INTERNAL
73 # define POP_NAME_INTERNAL
74 #else // DEBUG
75 //! returns caller name at the current scope or "top level"
76 #define CALL_STACK_TOP_NAME (m_callsNamesDebug.isEmpty() \
77 ? QByteArray("top level") : m_callsNamesDebug.top()).constData()
78 # define PUSH_NAME
79 //! put at beginning of each read_*() method on call stack, only in debug mode
80 # define PUSH_NAME_INTERNAL \
81 /*debugMsooXml << CALL_STACK_TOP_NAME << "==>" << QUALIFIED_NAME(CURRENT_EL); */\
82 m_callsNamesDebug.push(STRINGIFY(CURRENT_EL));
83 //! put at the end of each read_*() method on call stack, only in debug mode
84 # define POP_NAME_INTERNAL \
85 m_callsNamesDebug.pop(); \
86 /*debugMsooXml << CALL_STACK_TOP_NAME << "<==" << QUALIFIED_NAME(CURRENT_EL); */
87 #endif
88
89 #define READ_PROLOGUE2(method) \
90 if (!expectEl(QUALIFIED_NAME(CURRENT_EL))) { \
91 return KoFilter::WrongFormat; \
92 } \
93 PUSH_NAME_INTERNAL \
94
95 #define READ_PROLOGUE \
96 READ_PROLOGUE2(CURRENT_EL)
97
98 #define READ_PROLOGUE_IF_NS(ns) \
99 if (!expectEl(JOIN(STRINGIFY(ns) ":",CURRENT_EL))) { \
100 return KoFilter::WrongFormat; \
101 } \
102 PUSH_NAME_INTERNAL \
103
104 #define READ_EPILOGUE_WITHOUT_RETURN \
105 POP_NAME_INTERNAL \
106 if (!expectElEnd(QUALIFIED_NAME(CURRENT_EL))) { \
107 /*debugMsooXml << "READ_EPILOGUE:" << QUALIFIED_NAME(CURRENT_EL) << "not found!"; */\
108 return KoFilter::WrongFormat; \
109 } \
110 /*debugMsooXml << "/READ_EPILOGUE_WITHOUT_RETURN";*/
111
112 #define READ_EPILOGUE \
113 /*debugMsooXml << "READ_EPILOGUE";*/ \
114 READ_EPILOGUE_WITHOUT_RETURN \
115 return KoFilter::OK;
116
117 #define READ_EPILOGUE_IF_NS(ns) \
118 POP_NAME_INTERNAL \
119 if (!expectElEnd((JOIN(STRINGIFY(ns) ":",CURRENT_EL)))) { \
120 return KoFilter::WrongFormat; \
121 } \
122 return KoFilter::OK;
123
124 #define BREAK_IF_END_OF_QSTRING(name) \
125 /*debugMsooXml << "BREAK_IF_END_OF" << name << "found:" << qualifiedName();*/ \
126 if (isEndElement() && qualifiedName() == name) { \
127 break; \
128 }
129
130 #define BREAK_IF_END_OF(name) \
131 BREAK_IF_END_OF_QSTRING(QLatin1String(QUALIFIED_NAME(name)))
132
133 #define BREAK_IF_END_OF_WITH_NS(ns, name) \
134 BREAK_IF_END_OF_QSTRING(QLatin1String(JOIN(STRINGIFY(ns) ":",name)))
135
136 //inline bool aaaa(const char * aa) { debugMsooXml << "aa" << aa; return true; }
137
138 #define QUALIFIED_NAME_IS(name) \
139 (qualifiedName() == QLatin1String(QUALIFIED_NAME(name)))
140
141 #define TRY_READ_IF_INTERNAL(name, qname, context) \
142 if (qualifiedName() == QLatin1String(qname)) { \
143 if (!isStartElement()) { /* sanity check */ \
144 raiseError(i18n("Start element \"%1\" expected, found \"%2\"", \
145 QLatin1String(STRINGIFY(name)), tokenString())); \
146 return KoFilter::WrongFormat; \
147 } \
148 /*debugMsooXml << "TRY_READ_IF " STRINGIFY(name) " started";*/ \
149 TRY_READ_WITH_ARGS_INTERNAL(name, , context) \
150 /*debugMsooXml << "TRY_READ_IF " STRINGIFY(name) " finished";*/ \
151 }
152
153 #define TRY_READ_IF_IN_CONTEXT_INTERNAL(name, context) \
154 TRY_READ_IF_INTERNAL(name, QUALIFIED_NAME(name), context)
155
156 #define TRY_READ_IF_NS_IN_CONTEXT_INTERNAL(ns, name, context) \
157 TRY_READ_IF_INTERNAL(name, JOIN(STRINGIFY(ns) ":", name), context)
158
159 //! Tries to read element @a name by entering into read_{name} function.
160 //! Must be enclosed within if (isStartElement()), otherwise error will be returned.
161 #define TRY_READ_IF_IN_CONTEXT(name) \
162 TRY_READ_IF_IN_CONTEXT_INTERNAL(name, PASS_CONTEXT(name))
163
164 #define TRY_READ_IF_NS_IN_CONTEXT(ns, name) \
165 TRY_READ_IF_NS_IN_CONTEXT_INTERNAL(ns, name, PASS_CONTEXT(name))
166
167 #define TRY_READ_IF(name) \
168 TRY_READ_IF_IN_CONTEXT_INTERNAL(name,)
169
170 #define ELSE_TRY_READ_IF(name) \
171 else TRY_READ_IF_IN_CONTEXT_INTERNAL(name,)
172
173 #define ELSE_TRY_READ_IF_IN_CONTEXT(name) \
174 else TRY_READ_IF_IN_CONTEXT_INTERNAL(name, PASS_CONTEXT(name))
175
176 #define ELSE_TRY_READ_IF_NS_IN_CONTEXT(ns, name) \
177 else TRY_READ_IF_NS_IN_CONTEXT_INTERNAL(ns, name, PASS_CONTEXT(name))
178
179 #define TRY_READ_IF_NS_INTERNAL(ns, name) \
180 if (qualifiedName() == QLatin1String(JOIN(STRINGIFY(ns) ":", name))) { \
181 /*debugMsooXml << "TRY_READ_IF_NS " JOIN(STRINGIFY(ns) ":", name) " started";*/ \
182 TRY_READ(name); \
183 /*debugMsooXml << "TRY_READ_IF_NS " JOIN(STRINGIFY(ns) ":", name) " finished";*/ \
184 }
185
186 //! Like TRY_READ_IF() but namespace for explicit namespace @a ns.
187 #define TRY_READ_IF_NS(ns, name) \
188 if (!isStartElement()) { /* sanity check */ \
189 raiseError(i18n("Start element \"%1\" expected, found \"%2\"", QLatin1String(JOIN(STRINGIFY(ns) ":", name)), tokenString())); \
190 return KoFilter::WrongFormat; \
191 } \
192 else TRY_READ_IF_NS_INTERNAL(ns, name)
193
194 #define ELSE_TRY_READ_IF_NS(ns, name) \
195 else TRY_READ_IF_NS_INTERNAL(ns, name)
196
197 #define ELSE_WRONG_FORMAT \
198 else { \
199 return KoFilter::WrongFormat; \
200 }
201
202 #define ELSE_WRONG_FORMAT_DEBUG(dbg) \
203 else { \
204 debugMsooXml << dbg; \
205 return KoFilter::WrongFormat; \
206 }
207
208 //! Reads optional attribute of name @a atrname and allocates variable of the same name.
209 /*! Requires the following line to be present above:
210 @code
211 const QXmlStreamAttributes attrs( attributes() );
212 @endcode
213 */
214 #define TRY_READ_ATTR(atrname) \
215 QString atrname( attrs.value(QUALIFIED_NAME(atrname)).toString() );
216
217 //! Reads optional attribute of name @a atrname into the variable @a destination.
218 /*! Requires the following line to be present above:
219 @code
220 const QXmlStreamAttributes attrs( attributes() );
221 @endcode
222 */
223 #define TRY_READ_ATTR_INTO(atrname, destination) \
224 destination = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
225 /*debugMsooXml << "TRY_READ_ATTR_INTO: " STRINGIFY(destination) << "=" << destination;*/
226
227 //! Reads optional attribute of name @a atrname with explicitly specified namespace @a ns.
228 /*! Creates QString variable with name \<ns\>_\<atrame\>
229 */
230 /*! Requires the following line to be present above:
231 @code
232 const QXmlStreamAttributes attrs( attributes() );
233 @endcode
234 */
235 #define TRY_READ_ATTR_WITH_NS(ns, atrname) \
236 QString PASTE3(ns, _, atrname)( attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString() );
237
238 //! Reads optional attribute of name @a atrname with explicitly specified namespace @a ns
239 //! into the variable @a destination.
240 /*! Requires the following line to be present above:
241 @code
242 const QXmlStreamAttributes attrs( attributes() );
243 @endcode
244 */
245 #define TRY_READ_ATTR_WITH_NS_INTO(ns, atrname, destination) \
246 destination = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
247 /*debugMsooXml << "TRY_READ_ATTR_WITH_NS_INTO: " STRINGIFY(destination) << "=" << destination;*/
248
atrToString(const QXmlStreamAttributes & attrs,const char * atrname)249 inline QString atrToString(const QXmlStreamAttributes& attrs, const char* atrname)
250 {
251 const QStringRef v(attrs.value(atrname));
252 return v.isNull() ? QString() : v.toString();
253 }
254
255 //! Reads optional attribute of name @a atrname without namespace.
256 /*! Creates QString variable with name \<atrname\>
257 */
258 /*! Requires the following line to be present above:
259 @code
260 const QXmlStreamAttributes attrs( attributes() );
261 @endcode
262 */
263 #define TRY_READ_ATTR_WITHOUT_NS(atrname) \
264 QString atrname( atrToString(attrs, STRINGIFY(atrname)) );
265
266 //! Reads required attribute of name @a atrname and allocates variable of the same name
267 //! If there is no such attribute, returns KoFilter::WrongFormat.
268 /*! Requires the following line to be present above:
269 @code
270 const QXmlStreamAttributes attrs( attributes() );
271 @endcode
272 */
273 #define READ_ATTR(atrname) \
274 QString atrname; \
275 if (attrs.hasAttribute(QUALIFIED_NAME(atrname))) { \
276 atrname = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
277 } \
278 ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR: " QUALIFIED_NAME(atrname) " not found" )
279
280 //! Like @ref READ_ATTR(atrname) but reads the attribute into the variable @a destination.
281 #define READ_ATTR_INTO(atrname, destination) \
282 if (attrs.hasAttribute(QUALIFIED_NAME(atrname))) { \
283 destination = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
284 } \
285 ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_INTO: " QUALIFIED_NAME(atrname) " not found" )
286
287 //! Reads required attribute of name @a atrname with explicitly specified namespace @a ns
288 /*! into the variable @a destination.
289 */
290 /*! Requires the following line to be present above:
291 @code
292 const QXmlStreamAttributes attrs( attributes() );
293 @endcode
294 */
295 #define READ_ATTR_WITH_NS_INTO(ns, atrname, destination) \
296 if (attrs.hasAttribute(JOIN(STRINGIFY(ns) ":", atrname))) { \
297 destination = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
298 } \
299 ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITH_NS_INTO: " JOIN(STRINGIFY(ns) ":", atrname) " not found" )
300
301 //! Reads required attribute of name @a atrname with explicitly specified namespace @a ns.
302 /*! Creates QString variable with name \<ns\>_\<atrame\>
303 */
304 /*! Requires the following line to be present above:
305 @code
306 const QXmlStreamAttributes attrs( attributes() );
307 @endcode
308 */
309 #define READ_ATTR_WITH_NS(ns, atrname) \
310 QString PASTE3(ns, _, atrname); \
311 if (attrs.hasAttribute(JOIN(STRINGIFY(ns) ":", atrname))) { \
312 PASTE3(ns, _, atrname) = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
313 } \
314 ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITH_NS: " JOIN(STRINGIFY(ns) ":", atrname) " not found" )
315
316 //! Reads required attribute of name @a atrname without namespace.
317 /*! Creates QString variable with name \<atrname\>
318 */
319 /*! Requires the following line to be present above:
320 @code
321 const QXmlStreamAttributes attrs( attributes() );
322 @endcode
323 */
324 #define READ_ATTR_WITHOUT_NS(atrname) \
325 QString atrname; \
326 if (attrs.hasAttribute(STRINGIFY(atrname))) { \
327 atrname = attrs.value(STRINGIFY(atrname)).toString(); \
328 } \
329 ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITHOUT_NS: " STRINGIFY(atrname) " not found" )
330
331 //! Like @ref READ_ATTR_WITHOUT_NS(atrname) but reads the attribute into the variable @a destination.
332 #define READ_ATTR_WITHOUT_NS_INTO(atrname, destination) \
333 if (attrs.hasAttribute(STRINGIFY(atrname))) { \
334 destination = attrs.value(STRINGIFY(atrname)).toString(); \
335 } \
336 ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITHOUT_NS_INTO: " STRINGIFY(atrname) " not found" )
337
338
339 /*! Requires the following line to be present above:
340 @code
341 const QXmlStreamAttributes attrs( attributes() );
342 @endcode
343 */
344 #define TRY_READ_ATTR_QSTRING(atrname) \
345 QString atrname( attrs.value(m_defaultNamespace + atrname).toString() );
346
347 //! Like @ref TRY_READ_ATTR_WITHOUT_NS(atrname) but reads the attribute into the variable @a destination.
348 #define TRY_READ_ATTR_WITHOUT_NS_INTO(atrname, destination) \
349 destination = attrs.value(STRINGIFY(atrname)).toString();
350
351 //! reads boolean attribute "val" prefixed with default namespace, defaults to true
352 #define READ_BOOLEAN_VAL \
353 readBooleanAttr(QUALIFIED_NAME(val), true)
354
355 //! Converts @a string into integer @a destination; returns KoFilter::WrongFormat on failure.
356 //! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
357 #define STRING_TO_INT(string, destination, debugElement) \
358 if (string.isEmpty()) {} else { \
359 bool ok; \
360 const int val_tmp = string.toInt(&ok); \
361 if (!ok) { \
362 debugMsooXml << "STRING_TO_INT: error converting" << string << "to int (attribute" << debugElement << ")"; \
363 return KoFilter::WrongFormat; \
364 } \
365 destination = val_tmp; \
366 }
367
368 //! Converts @a string into longlong @a destination; returns KoFilter::WrongFormat on failure.
369 //! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
370 #define STRING_TO_LONGLONG(string, destination, debugElement) \
371 if (string.isEmpty()) {} else { \
372 bool ok; \
373 const quint64 val_tmp = string.toLongLong(&ok); \
374 if (!ok) { \
375 debugMsooXml << "STRING_TO_LONGLONG: error converting" << string << "to LONGLONG (attribute" << debugElement << ")"; \
376 return KoFilter::WrongFormat; \
377 } \
378 destination = val_tmp; \
379 }
380
381 //! Converts @a string into a qreal value in @a destination; returns KoFilter::WrongFormat on failure.
382 //! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
383 #define STRING_TO_QREAL(string, destination, debugElement) \
384 if (string.isEmpty()) {} else { \
385 bool ok; \
386 const qreal val_tmp = string.toDouble(&ok); \
387 if (!ok) { \
388 debugMsooXml << "STRING_TO_DOUBLE: error converting" << string << "to qreal (attribute" << debugElement << ")"; \
389 return KoFilter::WrongFormat; \
390 } \
391 destination = val_tmp; \
392 }
393
394 //! Skips everything until end of CURRENT_EL is pulled
395 #define SKIP_EVERYTHING \
396 /*debugMsooXml << "Skipping everything in element" << qualifiedName() << "...";*/ \
397 const QString qn(qualifiedName().toString()); \
398 /*debugMsooXml << *this; */\
399 while (true) { \
400 readNext(); \
401 if (atEnd()) \
402 break; \
403 if (isEndElement() && qualifiedName() == qn) { \
404 break; \
405 } \
406 }
407
408 //! Skips elements, which cannot be interpreted at this time in order to avoid them
409 // being read somewhere else
410 #define SKIP_UNKNOWN \
411 else { \
412 skipCurrentElement(); \
413 }
414
415 #define SKIP_EVERYTHING_AND_RETURN \
416 SKIP_EVERYTHING \
417 return KoFilter::OK;
418
419 #define BIND_READ_METHOD(name, method) \
420 m_readMethods.insert(QLatin1String(name), &MSOOXML_CURRENT_CLASS::read_ ## method);
421
422 #define BIND_READ(name) \
423 BIND_READ_METHOD(STRINGIFY(name), name)
424
425 #define BIND_READ_SKIP(name) \
426 BIND_READ_METHOD(STRINGIFY(name), SKIP)
427
428 #define BIND_READ_METHOD_HASH(hash, name, method) \
429 hash.insert(QLatin1String(name), &MSOOXML_CURRENT_CLASS::read_ ## method);
430
431 #define BIND_READ_HASH(hash, name) \
432 BIND_READ_METHOD_HASH(hash, STRINGIFY(name), name)
433
434 #define BIND_READ_HASH_SKIP(hash, name) \
435 BIND_READ_METHOD_HASH(hash, STRINGIFY(name), SKIP)
436
437 #endif
438