File indexing completed on 2024-05-12 16:29:10

0001 /*
0002  * This file is part of Office 2007 Filters for Calligra
0003  *
0004  * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
0005  *
0006  * Contact: Suresh Chande suresh.chande@nokia.com
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Lesser General Public License
0010  * version 2.1 as published by the Free Software Foundation.
0011  *
0012  * This library is distributed in the hope that it will be useful, but
0013  * WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015  * Lesser General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Lesser General Public
0018  * License along with this library; if not, write to the Free Software
0019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020  * 02110-1301 USA
0021  *
0022  */
0023 
0024 #ifndef MSOOXMLREADER_P_H
0025 #define MSOOXMLREADER_P_H
0026 
0027 #include <klocalizedstring.h>
0028 
0029 #ifndef MSOOXML_CURRENT_CLASS
0030 #error Please include MsooXmlReader_p.h after defining MSOOXML_CURRENT_CLASS and MSOOXML_CURRENT_NS!
0031 #endif
0032 
0033 #define PASTE2(a, b) a##b
0034 #define PASTE(a, b) PASTE2( a, b) // indirection needed because only function-like macro parameters can be pasted
0035 
0036 #define PASTE3_(a, b, c) a##b##c
0037 #define PASTE3(a, b, c) PASTE3_( a, b, c) // indirection needed because only function-like macro parameters can be pasted
0038 
0039 #define JOIN2(a, b) a#b
0040 #define JOIN(a, b) JOIN2( a, b) // indirection needed because only function-like macro parameters can be pasted
0041 
0042 #define STRINGIFY(s) JOIN("", s)
0043 
0044 //! Used to pass context: creates enum value {el}_{CURRENT_EL}
0045 #define PASS_CONTEXT(el) PASTE3(el, _, CURRENT_EL)
0046 
0047 #define TRY_READ_WITH_ARGS_INTERNAL(name, args, context) \
0048     args \
0049     RETURN_IF_ERROR( read_ ## name (context) )
0050 
0051 #define TRY_READ_WITH_ARGS(name, args) \
0052     TRY_READ_WITH_ARGS_INTERNAL(name, m_read_ ## name ## _args = args,)
0053 
0054 #define TRY_READ_WITH_ARGS_IN_CONTEXT(name, args) \
0055     TRY_READ_WITH_ARGS_INTERNAL(name, m_read_ ## name ## _args = args, PASS_CONTEXT(name))
0056 
0057 #define TRY_READ(name) \
0058     TRY_READ_WITH_ARGS_INTERNAL(name, ,)
0059 
0060 #define TRY_READ_IN_CONTEXT(name) \
0061     TRY_READ_WITH_ARGS_INTERNAL(name, , PASS_CONTEXT(name))
0062 
0063 #ifdef MSOOXML_CURRENT_NS
0064 # define QUALIFIED_NAME(name) \
0065     JOIN(MSOOXML_CURRENT_NS ":",name)
0066 #else
0067 # define QUALIFIED_NAME(name) \
0068     STRINGIFY(name)
0069 #endif
0070 
0071 #ifdef NDEBUG
0072 # define PUSH_NAME_INTERNAL
0073 # define POP_NAME_INTERNAL
0074 #else // DEBUG
0075 //! returns caller name at the current scope or "top level"
0076 #define CALL_STACK_TOP_NAME (m_callsNamesDebug.isEmpty() \
0077     ? QByteArray("top level") : m_callsNamesDebug.top()).constData()
0078 # define PUSH_NAME
0079 //! put at beginning of each read_*() method on call stack, only in debug mode
0080 # define PUSH_NAME_INTERNAL \
0081     /*debugMsooXml << CALL_STACK_TOP_NAME << "==>" << QUALIFIED_NAME(CURRENT_EL); */\
0082     m_callsNamesDebug.push(STRINGIFY(CURRENT_EL));
0083 //! put at the end of each read_*() method on call stack, only in debug mode
0084 # define POP_NAME_INTERNAL \
0085     m_callsNamesDebug.pop(); \
0086     /*debugMsooXml << CALL_STACK_TOP_NAME << "<==" << QUALIFIED_NAME(CURRENT_EL); */
0087 #endif
0088 
0089 #define READ_PROLOGUE2(method) \
0090     if (!expectEl(QUALIFIED_NAME(CURRENT_EL))) { \
0091         return KoFilter::WrongFormat; \
0092     } \
0093     PUSH_NAME_INTERNAL \
0094 
0095 #define READ_PROLOGUE \
0096     READ_PROLOGUE2(CURRENT_EL)
0097 
0098 #define READ_PROLOGUE_IF_NS(ns) \
0099     if (!expectEl(JOIN(STRINGIFY(ns) ":",CURRENT_EL))) { \
0100         return KoFilter::WrongFormat; \
0101     } \
0102     PUSH_NAME_INTERNAL \
0103 
0104 #define READ_EPILOGUE_WITHOUT_RETURN \
0105     POP_NAME_INTERNAL \
0106     if (!expectElEnd(QUALIFIED_NAME(CURRENT_EL))) { \
0107         /*debugMsooXml << "READ_EPILOGUE:" << QUALIFIED_NAME(CURRENT_EL) << "not found!"; */\
0108         return KoFilter::WrongFormat; \
0109     } \
0110     /*debugMsooXml << "/READ_EPILOGUE_WITHOUT_RETURN";*/
0111 
0112 #define READ_EPILOGUE \
0113     /*debugMsooXml << "READ_EPILOGUE";*/ \
0114     READ_EPILOGUE_WITHOUT_RETURN \
0115     return KoFilter::OK;
0116 
0117 #define READ_EPILOGUE_IF_NS(ns) \
0118     POP_NAME_INTERNAL \
0119     if (!expectElEnd((JOIN(STRINGIFY(ns) ":",CURRENT_EL)))) { \
0120         return KoFilter::WrongFormat; \
0121     } \
0122     return KoFilter::OK;
0123 
0124 #define BREAK_IF_END_OF_QSTRING(name) \
0125     /*debugMsooXml << "BREAK_IF_END_OF" << name << "found:" << qualifiedName();*/ \
0126     if (isEndElement() && qualifiedName() == name) { \
0127         break; \
0128     }
0129 
0130 #define BREAK_IF_END_OF(name) \
0131     BREAK_IF_END_OF_QSTRING(QLatin1String(QUALIFIED_NAME(name)))
0132 
0133 #define BREAK_IF_END_OF_WITH_NS(ns, name) \
0134     BREAK_IF_END_OF_QSTRING(QLatin1String(JOIN(STRINGIFY(ns) ":",name)))
0135 
0136 //inline bool aaaa(const char * aa) { debugMsooXml << "aa" << aa; return true; }
0137 
0138 #define QUALIFIED_NAME_IS(name) \
0139     (qualifiedName() == QLatin1String(QUALIFIED_NAME(name)))
0140 
0141 #define TRY_READ_IF_INTERNAL(name, qname, context) \
0142     if (qualifiedName() == QLatin1String(qname)) { \
0143         if (!isStartElement()) { /* sanity check */ \
0144             raiseError(i18n("Start element \"%1\" expected, found \"%2\"", \
0145                        QLatin1String(STRINGIFY(name)), tokenString())); \
0146             return KoFilter::WrongFormat; \
0147         } \
0148         /*debugMsooXml << "TRY_READ_IF " STRINGIFY(name) " started";*/ \
0149         TRY_READ_WITH_ARGS_INTERNAL(name, , context) \
0150         /*debugMsooXml << "TRY_READ_IF " STRINGIFY(name) " finished";*/ \
0151     }
0152 
0153 #define TRY_READ_IF_IN_CONTEXT_INTERNAL(name, context) \
0154     TRY_READ_IF_INTERNAL(name, QUALIFIED_NAME(name), context)
0155 
0156 #define TRY_READ_IF_NS_IN_CONTEXT_INTERNAL(ns, name, context) \
0157     TRY_READ_IF_INTERNAL(name, JOIN(STRINGIFY(ns) ":", name), context)
0158 
0159 //! Tries to read element @a name by entering into read_{name} function.
0160 //! Must be enclosed within if (isStartElement()), otherwise error will be returned.
0161 #define TRY_READ_IF_IN_CONTEXT(name) \
0162     TRY_READ_IF_IN_CONTEXT_INTERNAL(name, PASS_CONTEXT(name))
0163 
0164 #define TRY_READ_IF_NS_IN_CONTEXT(ns, name) \
0165     TRY_READ_IF_NS_IN_CONTEXT_INTERNAL(ns, name, PASS_CONTEXT(name))
0166 
0167 #define TRY_READ_IF(name) \
0168     TRY_READ_IF_IN_CONTEXT_INTERNAL(name,)
0169 
0170 #define ELSE_TRY_READ_IF(name) \
0171     else TRY_READ_IF_IN_CONTEXT_INTERNAL(name,)
0172 
0173 #define ELSE_TRY_READ_IF_IN_CONTEXT(name) \
0174     else TRY_READ_IF_IN_CONTEXT_INTERNAL(name, PASS_CONTEXT(name))
0175 
0176 #define ELSE_TRY_READ_IF_NS_IN_CONTEXT(ns, name) \
0177     else TRY_READ_IF_NS_IN_CONTEXT_INTERNAL(ns, name, PASS_CONTEXT(name))
0178 
0179 #define TRY_READ_IF_NS_INTERNAL(ns, name) \
0180     if (qualifiedName() == QLatin1String(JOIN(STRINGIFY(ns) ":", name))) { \
0181         /*debugMsooXml << "TRY_READ_IF_NS " JOIN(STRINGIFY(ns) ":", name) " started";*/ \
0182         TRY_READ(name); \
0183         /*debugMsooXml << "TRY_READ_IF_NS " JOIN(STRINGIFY(ns) ":", name) " finished";*/ \
0184     }
0185 
0186 //! Like TRY_READ_IF() but namespace for explicit namespace @a ns.
0187 #define TRY_READ_IF_NS(ns, name) \
0188     if (!isStartElement()) { /* sanity check */ \
0189         raiseError(i18n("Start element \"%1\" expected, found \"%2\"", QLatin1String(JOIN(STRINGIFY(ns) ":", name)), tokenString())); \
0190         return KoFilter::WrongFormat; \
0191     } \
0192     else TRY_READ_IF_NS_INTERNAL(ns, name)
0193 
0194 #define ELSE_TRY_READ_IF_NS(ns, name) \
0195     else TRY_READ_IF_NS_INTERNAL(ns, name)
0196 
0197 #define ELSE_WRONG_FORMAT \
0198     else { \
0199         return KoFilter::WrongFormat; \
0200     }
0201 
0202 #define ELSE_WRONG_FORMAT_DEBUG(dbg) \
0203     else { \
0204         debugMsooXml << dbg; \
0205         return KoFilter::WrongFormat; \
0206     }
0207 
0208 //! Reads optional attribute of name @a atrname and allocates variable of the same name.
0209 /*! Requires the following line to be present above:
0210     @code
0211     const QXmlStreamAttributes attrs( attributes() );
0212     @endcode
0213 */
0214 #define TRY_READ_ATTR(atrname) \
0215     QString atrname( attrs.value(QUALIFIED_NAME(atrname)).toString() );
0216 
0217 //! Reads optional attribute of name @a atrname into the variable @a destination.
0218 /*! Requires the following line to be present above:
0219     @code
0220     const QXmlStreamAttributes attrs( attributes() );
0221     @endcode
0222 */
0223 #define TRY_READ_ATTR_INTO(atrname, destination) \
0224     destination = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
0225     /*debugMsooXml << "TRY_READ_ATTR_INTO: " STRINGIFY(destination) << "=" << destination;*/
0226 
0227 //! Reads optional attribute of name @a atrname with explicitly specified namespace @a ns.
0228 /*! Creates QString variable with name \<ns\>_\<atrame\>
0229 */
0230 /*! Requires the following line to be present above:
0231     @code
0232     const QXmlStreamAttributes attrs( attributes() );
0233     @endcode
0234 */
0235 #define TRY_READ_ATTR_WITH_NS(ns, atrname) \
0236     QString PASTE3(ns, _, atrname)( attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString() );
0237 
0238 //! Reads optional attribute of name @a atrname with explicitly specified namespace @a ns
0239 //! into the variable @a destination.
0240 /*! Requires the following line to be present above:
0241     @code
0242     const QXmlStreamAttributes attrs( attributes() );
0243     @endcode
0244 */
0245 #define TRY_READ_ATTR_WITH_NS_INTO(ns, atrname, destination) \
0246     destination = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
0247     /*debugMsooXml << "TRY_READ_ATTR_WITH_NS_INTO: " STRINGIFY(destination) << "=" << destination;*/
0248 
0249 inline QString atrToString(const QXmlStreamAttributes& attrs, const char* atrname)
0250 {
0251     const QStringRef v(attrs.value(atrname));
0252     return v.isNull() ? QString() : v.toString();
0253 }
0254 
0255 //! Reads optional attribute of name @a atrname without namespace.
0256 /*! Creates QString variable with name \<atrname\>
0257 */
0258 /*! Requires the following line to be present above:
0259     @code
0260     const QXmlStreamAttributes attrs( attributes() );
0261     @endcode
0262 */
0263 #define TRY_READ_ATTR_WITHOUT_NS(atrname) \
0264     QString atrname( atrToString(attrs, STRINGIFY(atrname)) );
0265 
0266 //! Reads required attribute of name @a atrname and allocates variable of the same name
0267 //! If there is no such attribute, returns KoFilter::WrongFormat.
0268 /*! Requires the following line to be present above:
0269     @code
0270     const QXmlStreamAttributes attrs( attributes() );
0271     @endcode
0272 */
0273 #define READ_ATTR(atrname) \
0274     QString atrname; \
0275     if (attrs.hasAttribute(QUALIFIED_NAME(atrname))) { \
0276         atrname = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
0277     } \
0278     ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR: " QUALIFIED_NAME(atrname) " not found" )
0279 
0280 //! Like @ref READ_ATTR(atrname) but reads the attribute into the variable @a destination.
0281 #define READ_ATTR_INTO(atrname, destination) \
0282     if (attrs.hasAttribute(QUALIFIED_NAME(atrname))) { \
0283         destination = attrs.value(QUALIFIED_NAME(atrname)).toString(); \
0284     } \
0285     ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_INTO: " QUALIFIED_NAME(atrname) " not found" )
0286 
0287 //! Reads required attribute of name @a atrname with explicitly specified namespace @a ns
0288 /*! into the variable @a destination.
0289 */
0290 /*! Requires the following line to be present above:
0291     @code
0292     const QXmlStreamAttributes attrs( attributes() );
0293     @endcode
0294 */
0295 #define READ_ATTR_WITH_NS_INTO(ns, atrname, destination) \
0296     if (attrs.hasAttribute(JOIN(STRINGIFY(ns) ":", atrname))) { \
0297         destination = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
0298     } \
0299     ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITH_NS_INTO: " JOIN(STRINGIFY(ns) ":", atrname) " not found" )
0300 
0301 //! Reads required attribute of name @a atrname with explicitly specified namespace @a ns.
0302 /*! Creates QString variable with name \<ns\>_\<atrame\>
0303 */
0304 /*! Requires the following line to be present above:
0305     @code
0306     const QXmlStreamAttributes attrs( attributes() );
0307     @endcode
0308 */
0309 #define READ_ATTR_WITH_NS(ns, atrname) \
0310     QString PASTE3(ns, _, atrname); \
0311     if (attrs.hasAttribute(JOIN(STRINGIFY(ns) ":", atrname))) { \
0312         PASTE3(ns, _, atrname) = attrs.value(JOIN(STRINGIFY(ns) ":", atrname)).toString(); \
0313     } \
0314     ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITH_NS: " JOIN(STRINGIFY(ns) ":", atrname) " not found" )
0315 
0316 //! Reads required attribute of name @a atrname without namespace.
0317 /*! Creates QString variable with name \<atrname\>
0318 */
0319 /*! Requires the following line to be present above:
0320     @code
0321     const QXmlStreamAttributes attrs( attributes() );
0322     @endcode
0323 */
0324 #define READ_ATTR_WITHOUT_NS(atrname) \
0325     QString atrname; \
0326     if (attrs.hasAttribute(STRINGIFY(atrname))) { \
0327         atrname = attrs.value(STRINGIFY(atrname)).toString(); \
0328     } \
0329     ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITHOUT_NS: " STRINGIFY(atrname) " not found" )
0330 
0331 //! Like @ref READ_ATTR_WITHOUT_NS(atrname) but reads the attribute into the variable @a destination.
0332 #define READ_ATTR_WITHOUT_NS_INTO(atrname, destination) \
0333     if (attrs.hasAttribute(STRINGIFY(atrname))) { \
0334         destination = attrs.value(STRINGIFY(atrname)).toString(); \
0335     } \
0336     ELSE_WRONG_FORMAT_DEBUG( "READ_ATTR_WITHOUT_NS_INTO: " STRINGIFY(atrname) " not found" )
0337 
0338 
0339 /*! Requires the following line to be present above:
0340     @code
0341     const QXmlStreamAttributes attrs( attributes() );
0342     @endcode
0343 */
0344 #define TRY_READ_ATTR_QSTRING(atrname) \
0345     QString atrname( attrs.value(m_defaultNamespace + atrname).toString() );
0346 
0347 //! Like @ref TRY_READ_ATTR_WITHOUT_NS(atrname) but reads the attribute into the variable @a destination.
0348 #define TRY_READ_ATTR_WITHOUT_NS_INTO(atrname, destination) \
0349     destination = attrs.value(STRINGIFY(atrname)).toString();
0350 
0351 //! reads boolean attribute "val" prefixed with default namespace, defaults to true
0352 #define READ_BOOLEAN_VAL \
0353     readBooleanAttr(QUALIFIED_NAME(val), true)
0354 
0355 //! Converts @a string into integer @a destination; returns KoFilter::WrongFormat on failure.
0356 //! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
0357 #define STRING_TO_INT(string, destination, debugElement) \
0358     if (string.isEmpty()) {} else { \
0359         bool ok; \
0360         const int val_tmp = string.toInt(&ok); \
0361         if (!ok) { \
0362             debugMsooXml << "STRING_TO_INT: error converting" << string << "to int (attribute" << debugElement << ")"; \
0363             return KoFilter::WrongFormat; \
0364         } \
0365         destination = val_tmp; \
0366     }
0367 
0368 //! Converts @a string into longlong @a destination; returns KoFilter::WrongFormat on failure.
0369 //! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
0370 #define STRING_TO_LONGLONG(string, destination, debugElement) \
0371     if (string.isEmpty()) {} else { \
0372         bool ok; \
0373         const quint64 val_tmp = string.toLongLong(&ok); \
0374         if (!ok) { \
0375             debugMsooXml << "STRING_TO_LONGLONG: error converting" << string << "to LONGLONG (attribute" << debugElement << ")"; \
0376             return KoFilter::WrongFormat; \
0377         } \
0378         destination = val_tmp; \
0379     }
0380 
0381 //! Converts @a string into a qreal value in @a destination; returns KoFilter::WrongFormat on failure.
0382 //! @warning @a destination is left unchanged if @a string is empty, so it is up to developer to initialize it.
0383 #define STRING_TO_QREAL(string, destination, debugElement) \
0384     if (string.isEmpty()) {} else { \
0385         bool ok; \
0386         const qreal val_tmp = string.toDouble(&ok); \
0387         if (!ok) { \
0388             debugMsooXml << "STRING_TO_DOUBLE: error converting" << string << "to qreal (attribute" << debugElement << ")"; \
0389             return KoFilter::WrongFormat; \
0390         } \
0391         destination = val_tmp; \
0392     }
0393 
0394 //! Skips everything until end of CURRENT_EL is pulled
0395 #define SKIP_EVERYTHING \
0396     /*debugMsooXml << "Skipping everything in element" << qualifiedName() << "...";*/ \
0397     const QString qn(qualifiedName().toString()); \
0398     /*debugMsooXml << *this; */\
0399     while (true) { \
0400         readNext(); \
0401         if (atEnd()) \
0402             break; \
0403         if (isEndElement() && qualifiedName() == qn) { \
0404             break; \
0405         } \
0406     }
0407 
0408 //! Skips elements, which cannot be interpreted at this time in order to avoid them
0409 // being read somewhere else
0410 #define SKIP_UNKNOWN \
0411     else { \
0412         skipCurrentElement(); \
0413     }
0414 
0415 #define SKIP_EVERYTHING_AND_RETURN \
0416     SKIP_EVERYTHING \
0417     return KoFilter::OK;
0418 
0419 #define BIND_READ_METHOD(name, method) \
0420     m_readMethods.insert(QLatin1String(name), &MSOOXML_CURRENT_CLASS::read_ ## method);
0421 
0422 #define BIND_READ(name) \
0423     BIND_READ_METHOD(STRINGIFY(name), name)
0424 
0425 #define BIND_READ_SKIP(name) \
0426     BIND_READ_METHOD(STRINGIFY(name), SKIP)
0427 
0428 #define BIND_READ_METHOD_HASH(hash, name, method) \
0429     hash.insert(QLatin1String(name), &MSOOXML_CURRENT_CLASS::read_ ## method);
0430 
0431 #define BIND_READ_HASH(hash, name) \
0432     BIND_READ_METHOD_HASH(hash, STRINGIFY(name), name)
0433 
0434 #define BIND_READ_HASH_SKIP(hash, name) \
0435     BIND_READ_METHOD_HASH(hash, STRINGIFY(name), SKIP)
0436 
0437 #endif