/* * * Copyright (c) 2020 Jief * All rights reserved. * */ #ifndef __XML_LITE_H__ #define __XML_LITE_H__ #include "../cpp_foundation/apd.h" #include "../cpp_foundation/XBool.h" #include "../cpp_foundation/XStringArray.h" #include "../cpp_lib/XmlLiteSimpleTypes.h" #include "../cpp_lib/XmlLiteParser.h" #if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__) #define __PRETTY_FUNCTION__ __FUNCSIG__ #endif XBool strnnIsEqual(const char* key, size_t keyLength, const char* value, size_t valueLength); XBool strnIsEqual(const char* key, size_t keyLength, const char* value); XBool strnnIsEqualIC(const char* key, size_t keyLength, const char* value, size_t valueLength); XBool strnIsEqualIC(const char* key, size_t keyLength, const char* value); enum class XmlParserMessageType { error, warning, info }; class XmlParserMessage { public: XmlParserMessageType type; XString8 msg; XmlParserMessage(XmlParserMessageType _type, const XString8& _msg) : type(_type), msg(_msg) {}; XString8 getFormattedMsg() const { if ( type == XmlParserMessageType::info ) return S8Printf("Info: %s", msg.c_str()); if ( type == XmlParserMessageType::warning ) return S8Printf("Warning: %s", msg.c_str()); if ( type == XmlParserMessageType::error ) return S8Printf("Error: %s", msg.c_str()); return msg; } }; class XmlLiteParser; class XmlParserPosition { friend class XmlLiteParser; public: char* p; int line; protected: int col; public: XmlParserPosition() : p(NULL), line(1), col(1) {}; XmlParserPosition(char* _p) : p(_p), line(1), col(1) {}; int getLine() const { return line; } int getCol() const { return col; } XBool operator == (const XmlParserPosition& other) const { return p == other.p; } }; class XmlLiteParser { friend class XmlParserPosition; apd p_start = NULL; char* p_end = NULL; XmlParserPosition currentPos = XmlParserPosition(); XObjArray XmlParserMessageArray = XObjArray(); XBool AddXmlParserMessage(XmlParserMessage* msg) { if ( XmlParserMessageArray.size() < 500 ) XmlParserMessageArray.AddReference(msg, true); if ( XmlParserMessageArray.size() == 500 ) XmlParserMessageArray.AddReference(new XmlParserMessage(XmlParserMessageType::error, "Too many error. Stopping"_XS8), true); return false; } public: XBool xmlParsingError = false; XBool productNameNeeded = false; XmlLiteParser() {}; XmlLiteParser(char* _p) : p_start(_p) { p_end = p_start + strlen(p_start); }; XmlLiteParser(const XmlLiteParser&) = delete; XmlLiteParser& operator = (const XmlLiteParser&) = delete; void init(const char* buf, size_t size); void init(const char* buf) { init(buf, strlen(buf)); }; int getLine() { return currentPos.line; } int getCol() { return currentPos.col; } XObjArray& getXmlParserMessageArray() { return XmlParserMessageArray; } size_t getXmlParserInfoMessageCount() const { size_t n=0 ; for ( size_t i=0 ; i < XmlParserMessageArray.size() ; i++ ) if ( XmlParserMessageArray[i].type == XmlParserMessageType::info ) ++n; return n; } // Add warning, error and xml error always return false so you can return addWarning(...) from validate function XBool addInfo(XBool generateErrors, const XString8& warning) { if ( generateErrors ) AddXmlParserMessage(new XmlParserMessage(XmlParserMessageType::info, warning)); return false; } XBool addWarning(XBool generateErrors, const XString8& warning) { if ( generateErrors ) AddXmlParserMessage(new XmlParserMessage(XmlParserMessageType::warning, warning)); return false; } XBool addError(XBool generateErrors, const XString8& warning) { if ( generateErrors ) AddXmlParserMessage(new XmlParserMessage(XmlParserMessageType::error, warning)); return false; } // Xml stuctural error. Parsing should probably stop. XBool addXmlError(XBool generateErrors, const XString8& warning) { if ( generateErrors ) {xmlParsingError = true; AddXmlParserMessage(new XmlParserMessage(XmlParserMessageType::error, warning));} return false; } void printfXmlParserMessage() { for ( size_t idx = 0 ; idx < getXmlParserMessageArray().size() ; idx++ ) { printf("%s: %s\n", getXmlParserMessageArray()[idx].type == XmlParserMessageType::error ? "Error" : getXmlParserMessageArray()[idx].type == XmlParserMessageType::warning ? "Warning" : getXmlParserMessageArray()[idx].type == XmlParserMessageType::info ? "Info" : "" , getXmlParserMessageArray()[idx].msg.c_str()); } } XmlParserPosition getPosition(); void restorePosition(XmlParserPosition& xml_position); XBool isEof() const { return currentPos.p >= p_end; } char getchar(); char* getcharPtr() { return currentPos.p; }; char moveForward(); char moveForward(int n); char moveBackward(); char moveForwardUntil(char until); char moveBackwardUntil(char until); char moveForwardPastNext(char until); char moveForwardUntilSignificant(); char moveBackwardUntilSignificant(); void skipHeader(); /* * The opening tag has been read. Skip until the closing tag */ XBool skipUntilClosingTag(const char* tagToSkip, size_t tagToSkipLength, XBool generateErrors); XBool skipNextTag(XBool generateErrors); /* * Get the string from current position until the next '<', or end of buffer. * Spaces at begining or end are skipped. * If current char is '<', empty string is returned. (*string=NULL and *length=0). * If current char is '/', empty string is returned. This is because getNextTag leaves to pinter to the '/' for empty tag. * If no '<', false is returned. * Position at the '<' when exit. */ void getString(const char** string, size_t* length); /* * Get the next tag, either opening or closing tag. * For empty tag (), first call return that it's an opening tag. Second call return that it's a closing tag. */ XBool getNextTag(const char** tag, size_t* length, XBool* isOpeningTag, XBool* isClosingTag, XBool generateErrors); XBool getSimpleTag(const char** tag, size_t* tagLength, const char** value, size_t* valueLength, const char* expectedTag/*, XBool valueCanBeEmpty*/, XBool generateErrors); XBool getSimpleTagValue(const char* tag, size_t tagLength, const char** value, size_t* valueLength, XmlParserPosition* xmlParserPosition, XBool generateErrors); /* * Get the next tag, check it's a key, get the value and consume the closing tag (""). */ XBool getKeyTagValue(const char** value, size_t* valueLength, XmlParserPosition* xmlParserPosition, XBool generateErrors); /* * Check and consume the tag * Returns true if it's the expected opening tag * Returns false, add error in error list and don't change position if it's not */ XBool consumeOpeningTag(const char* expectedTag, XBool generateErrors = true); /* * Check and consume the tag * Returns true if it's the expected closing tag * Returns false, add error in error list and don't change position if it's not */ XBool consumeClosingTag(const char* expectedTag, XBool generateErrors); /* * Check but do NOT consume the tag * Returns true if it's the expected opening tag * Returns false, DO NOT add error in error list and don't change position if it's not */ XBool nextTagIsOpeningTag(const char* expectedTag); XBool nextTagIsClosingTag(const char* expectedTag); // XBool getTagBool(XmlBool* XmlBoolean); }; #define RETURN_IF_FALSE(Expression) do { XBool b = Expression; if ( !b ) return false; } while (0); #endif // __XML_LITE_H__