~ubuntu-branches/ubuntu/precise/koffice/precise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/*
 * This file is part of Office 2007 Filters for KOffice
 *
 * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: Suresh Chande suresh.chande@nokia.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#ifndef MSOOXMLREADER_P_H
#define MSOOXMLREADER_P_H

#ifndef MSOOXML_CURRENT_CLASS
#error Please include MsooXmlReader_p.h after defining MSOOXML_CURRENT_CLASS and MSOOXML_CURRENT_NS!
#endif

#define PASTE2(a, b) a##b
#define PASTE(a, b) PASTE2( a, b) // indirection needed because only function-like macro parameters can be pasted

#define PASTE3_(a, b, c) a##b##c
#define PASTE3(a, b, c) PASTE3_( a, b, c) // indirection needed because only function-like macro parameters can be pasted

#define JOIN2(a, b) a#b
#define JOIN(a, b) JOIN2( a, b) // indirection needed because only function-like macro parameters can be pasted

#define STRINGIFY(s) JOIN("", s)

//! Used to pass context: creates enum value {el}_{CURRENT_EL}
#define PASS_CONTEXT(el) PASTE3(el, _, CURRENT_EL)

#define TRY_READ_WITH_ARGS_INTERNAL(name, args, context) \
    args \
    RETURN_IF_ERROR( read_ ## name (context) )

#define TRY_READ_WITH_ARGS(name, args) \
    TRY_READ_WITH_ARGS_INTERNAL(name, m_read_ ## name ## _args = args,)

#define TRY_READ_WITH_ARGS_IN_CONTEXT(name, args) \
    TRY_READ_WITH_ARGS_INTERNAL(name, m_read_ ## name ## _args = args, PASS_CONTEXT(name))

#define TRY_READ(name) \
    TRY_READ_WITH_ARGS_INTERNAL(name, ,)

#define TRY_READ_IN_CONTEXT(name) \
    TRY_READ_WITH_ARGS_INTERNAL(name, , PASS_CONTEXT(name))

#ifdef MSOOXML_CURRENT_NS
# define QUALIFIED_NAME(name) \
    JOIN(MSOOXML_CURRENT_NS ":",name)
#else
# define QUALIFIED_NAME(name) \
    STRINGIFY(name)
#endif

#ifdef NDEBUG
# define PUSH_NAME_INTERNAL
# define POP_NAME_INTERNAL
#else // DEBUG
//! returns caller name at the current scope or "top level"
#define CALL_STACK_TOP_NAME (m_callsNamesDebug.isEmpty() \
    ? QByteArray("top level") : m_callsNamesDebug.top()).constData()
# define PUSH_NAME
//! put at beginning of each read_*() method on call stack, only in debug mode
# define PUSH_NAME_INTERNAL \
    kDebug() << CALL_STACK_TOP_NAME << "==>" << QUALIFIED_NAME(CURRENT_EL); \
    m_callsNamesDebug.push(STRINGIFY(CURRENT_EL));
//! put at the end of each read_*() method on call stack, only in debug mode
# define POP_NAME_INTERNAL \
    m_callsNamesDebug.pop(); \
    kDebug() << CALL_STACK_TOP_NAME << "<==" << QUALIFIED_NAME(CURRENT_EL);
#endif

#define READ_PROLOGUE2(method) \
    PUSH_NAME_INTERNAL \
    if (!expectEl(QUALIFIED_NAME(CURRENT_EL))) { \
        return KoFilter::WrongFormat; \
    }

#define READ_PROLOGUE \
    READ_PROLOGUE2(CURRENT_EL)

#define READ_EPILOGUE_WITHOUT_RETURN \
    POP_NAME_INTERNAL \
    if (!expectElEnd(QUALIFIED_NAME(CURRENT_EL))) { \
        kDebug() << "READ_EPILOGUE:" << QUALIFIED_NAME(CURRENT_EL) << "not found!"; \
        return KoFilter::WrongFormat; \
    } \
    kDebug() << "/READ_EPILOGUE_WITHOUT_RETURN";

#define READ_EPILOGUE \
    kDebug() << "READ_EPILOGUE"; \
    READ_EPILOGUE_WITHOUT_RETURN \
    return KoFilter::OK;

#define BREAK_IF_END_OF_QSTRING(name) \
    kDebug() << "BREAK_IF_END_OF" << name << "found:" << qualifiedName(); \
    if (isEndElement() && qualifiedName() == name) { \
        break; \
    }

#define BREAK_IF_END_OF(name) \
    BREAK_IF_END_OF_QSTRING(QLatin1String(QUALIFIED_NAME(name)))

//inline bool aaaa(const char * aa) { kDebug() << "aa" << aa; return true; }

#define QUALIFIED_NAME_IS(name) \
    (/*aaaa(STRINGIFY(name)) &&*/ qualifiedName() == QLatin1String(QUALIFIED_NAME(name)))

#define TRY_READ_IF_INTERNAL(name, context) \
    if (QUALIFIED_NAME_IS(name)) { \
        if (!isStartElement()) { /* sanity check */ \
            raiseError(i18n("Start element \"%1\" expected, found \"%2\"", \
                       QLatin1String(STRINGIFY(name)), tokenString())); \
            return KoFilter::WrongFormat; \
        } \
        kDebug() << "TRY_READ_IF " STRINGIFY(name) " started"; \
        TRY_READ_WITH_ARGS_INTERNAL(name, , context) \
        kDebug() << "TRY_READ_IF " STRINGIFY(name) " finished"; \
    }

#define TRY_READ_IF_IN_CONTEXT_INTERNAL(name, context) \
    TRY_READ_IF_INTERNAL(name, context)

//! Tries to read element @a name by entering into read_{name} function.
//! Must be enclosed within if (isStartElement()), otherwise error will be returned.
#define TRY_READ_IF_IN_CONTEXT(name) \
    TRY_READ_IF_IN_CONTEXT_INTERNAL(name, PASS_CONTEXT(name))

#define TRY_READ_IF(name) \
    TRY_READ_IF_IN_CONTEXT_INTERNAL(name,)

#define ELSE_TRY_READ_IF(name) \
    else TRY_READ_IF_IN_CONTEXT_INTERNAL(name,)

#define ELSE_TRY_READ_IF_IN_CONTEXT(name) \
    else TRY_READ_IF_IN_CONTEXT_INTERNAL(name, PASS_CONTEXT(name))

#define TRY_READ_IF_NS_INTERNAL(ns, name) \
    if (qualifiedName() == QLatin1String(JOIN(STRINGIFY(ns) ":", name))) { \
        kDebug() << "TRY_READ_IF_NS " JOIN(STRINGIFY(ns) ":", name) " started"; \
        TRY_READ(name); \
        kDebug() << "TRY_READ_IF_NS " JOIN(STRINGIFY(ns) ":", name) " finished"; \
    }

//! Like TRY_READ_IF() but namespace for explicit namespace @a ns.
#define TRY_READ_IF_NS(ns, name) \
    if (!isStartElement()) { /* sanity check */ \
        raiseError(i18n("Start element \"%1\" expected, found \"%2\"", QLatin1String(JOIN(STRINGIFY(ns) ":", name)), tokenString())); \
        return KoFilter::WrongFormat; \
    } \
    else TRY_READ_IF_NS_INTERNAL(ns, name)

#define ELSE_TRY_READ_IF_NS(ns, name) \
    else TRY_READ_IF_NS_INTERNAL(ns, name)

#define ELSE_WRONG_FORMAT \
    else { \
        return KoFilter::WrongFormat; \
    }

#define ELSE_WRONG_FORMAT_DEBUG(dbg) \
    else { \
        kDebug() << dbg; \
        return KoFilter::WrongFormat; \
    }

//! Reads optional attribute of name @a atrname and allocates variable of the same name.
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define TRY_READ_ATTR(atrname) \
    QString atrname( attrs.value(QUALIFIED_NAME(atrname)).toString() );

//! Reads optional attribute of name @a atrname into the variable @a destination.
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define TRY_READ_ATTR_INTO(atrname, destination) \
    destination = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
    kDebug() << "TRY_READ_ATTR_INTO: " STRINGIFY(destination) << "=" << destination;

//! Reads optional attribute of name @a atrname with explicitly specified namespace @a ns.
/*! Creates QString variable with name \<ns\>_\<atrame\>
*/
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define TRY_READ_ATTR_WITH_NS(ns, atrname) \
    QString PASTE3(ns, _, atrname)( attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString() );

//! Reads optional attribute of name @a atrname with explicitly specified namespace @a ns
//! into the variable @a destination.
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define TRY_READ_ATTR_WITH_NS_INTO(ns, atrname, destination) \
    destination = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
    kDebug() << "TRY_READ_ATTR_WITH_NS_INTO: " STRINGIFY(destination) << "=" << destination;

inline QString atrToString(const QXmlStreamAttributes& attrs, const char* atrname)
{
    const QStringRef v(attrs.value(atrname));
    return v.isNull() ? QString() : v.toString();
}

//! Reads optional attribute of name @a atrname without namespace.
/*! Creates QString variable with name \<atrname\>
*/
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define TRY_READ_ATTR_WITHOUT_NS(atrname) \
    QString atrname( atrToString(attrs, STRINGIFY(atrname)) );

//! Reads required attribute of name @a atrname and allocates variable of the same name
//! If there is no such attribute, returns KoFilter::WrongFormat.
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define READ_ATTR(atrname) \
    QString atrname; \
    if (attrs.hasAttribute(QUALIFIED_NAME(atrname))) { \
        atrname = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
    } \
    ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR: " QUALIFIED_NAME(atrname) " not found" )

//! Like @ref READ_ATTR(atrname) but reads the attribute into the variable @a destination.
#define READ_ATTR_INTO(atrname, destination) \
    if (attrs.hasAttribute(QUALIFIED_NAME(atrname))) { \
        destination = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
    } \
    ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_INTO: " QUALIFIED_NAME(atrname) " not found" )

//! Reads required attribute of name @a atrname with explicitly specified namespace @a ns
/*! into the variable @a destination.
*/
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define READ_ATTR_WITH_NS_INTO(ns, atrname, destination) \
    if (attrs.hasAttribute(JOIN(STRINGIFY(ns) ":", atrname))) { \
        destination = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
    } \
    ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITH_NS_INTO: " JOIN(STRINGIFY(ns) ":", atrname) " not found" )

//! Reads required attribute of name @a atrname with explicitly specified namespace @a ns.
/*! Creates QString variable with name \<ns\>_\<atrame\>
*/
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define READ_ATTR_WITH_NS(ns, atrname) \
    QString PASTE3(ns, _, atrname); \
    if (attrs.hasAttribute(JOIN(STRINGIFY(ns) ":", atrname))) { \
        PASTE3(ns, _, atrname) = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
    } \
    ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITH_NS: " JOIN(STRINGIFY(ns) ":", atrname) " not found" )

//! Reads required attribute of name @a atrname without namespace.
/*! Creates QString variable with name \<atrname\>
*/
/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define READ_ATTR_WITHOUT_NS(atrname) \
    QString atrname; \
    if (attrs.hasAttribute(STRINGIFY(atrname))) { \
        atrname = attrs.value(STRINGIFY(atrname)).toString(); \
    } \
    ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITHOUT_NS: " STRINGIFY(atrname) " not found" )

//! Like @ref READ_ATTR_WITHOUT_NS(atrname) but reads the attribute into the variable @a destination.
#define READ_ATTR_WITHOUT_NS_INTO(atrname, destination) \
    if (attrs.hasAttribute(STRINGIFY(atrname))) { \
        destination = attrs.value(STRINGIFY(atrname)).toString(); \
    } \
    ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITHOUT_NS_INTO: " STRINGIFY(atrname) " not found" )


/*! Requires the following line to be present above:
    @code
    const QXmlStreamAttributes attrs( attributes() );
    @endcode
*/
#define TRY_READ_ATTR_QSTRING(atrname) \
    QString atrname( attrs.value(m_defaultNamespace + atrname).toString() );

//! Like @ref TRY_READ_ATTR_WITHOUT_NS(atrname) but reads the attribute into the variable @a destination.
#define TRY_READ_ATTR_WITHOUT_NS_INTO(atrname, destination) \
    destination = attrs.value(STRINGIFY(atrname)).toString();

//! reads boolean attribute "val" prefixed with default namespace, defaults to true
#define READ_BOOLEAN_VAL \
    readBooleanAttr(QUALIFIED_NAME(val), true)

//! Converts @a string into integer @a destination; returns KoFilter::WrongFormat on failure.
//! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
#define STRING_TO_INT(string, destination, debugElement) \
    if (string.isEmpty()) {} else { \
        bool ok; \
        const int val_tmp = string.toInt(&ok); \
        if (!ok) { \
            kDebug() << "STRING_TO_INT: error converting" << string << "to int (attribute" << debugElement << ")"; \
            return KoFilter::WrongFormat; \
        } \
        destination = val_tmp; \
    }

//! Converts @a string into a qreal value in @a destination; returns KoFilter::WrongFormat on failure.
//! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
#define STRING_TO_QREAL(string, destination, debugElement) \
    if (string.isEmpty()) {} else { \
        bool ok; \
        const qreal val_tmp = string.toDouble(&ok); \
        if (!ok) { \
            kDebug() << "STRING_TO_DOUBLE: error converting" << string << "to qreal (attribute" << debugElement << ")"; \
            return KoFilter::WrongFormat; \
        } \
        destination = val_tmp; \
    }

//! Skips everything until end of CURRENT_EL is pulled
#define SKIP_EVERYTHING \
    kDebug() << "Skipping everything in element" << qualifiedName() << "..."; \
    const QString qn(qualifiedName().toString()); \
    kDebug() << *this; \
    while (true) { \
        if (atEnd()) \
            break; \
        if (isEndElement() && qualifiedName() == qn) { \
            break; \
        } \
        readNext(); \
    }

#define SKIP_EVERYTHING_AND_RETURN \
    SKIP_EVERYTHING \
    return KoFilter::OK;

#define BIND_READ_METHOD(name, method) \
    m_readMethods.insert(QLatin1String(name), &MSOOXML_CURRENT_CLASS::read_ ## method);

#define BIND_READ(name) \
    BIND_READ_METHOD(STRINGIFY(name), name)

#define BIND_READ_SKIP(name) \
    BIND_READ_METHOD(STRINGIFY(name), SKIP)

#define BIND_READ_METHOD_HASH(hash, name, method) \
    hash.insert(QLatin1String(name), &MSOOXML_CURRENT_CLASS::read_ ## method);

#define BIND_READ_HASH(hash, name) \
    BIND_READ_METHOD_HASH(hash, STRINGIFY(name), name)

#define BIND_READ_HASH_SKIP(hash, name) \
    BIND_READ_METHOD_HASH(hash, STRINGIFY(name), SKIP)

#endif