/*------------------------------------------------------------------------- * drawElements Quality Program Test Executor * ------------------------------------------ * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief XML Parser. *//*--------------------------------------------------------------------*/ #include "xeXMLParser.hpp" #include "deInt32.h" namespace xe { namespace xml { enum { TOKENIZER_INITIAL_BUFFER_SIZE = 1024 }; static inline bool isIdentifierStartChar (int ch) { return de::inRange(ch, 'a', 'z') || de::inRange(ch, 'A', 'Z'); } static inline bool isIdentifierChar (int ch) { return isIdentifierStartChar(ch) || de::inRange(ch, '0', '9') || (ch == '-') || (ch == '_'); } static inline bool isWhitespaceChar (int ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; } static int getNextBufferSize (int curSize, int minNewSize) { return de::max(curSize*2, 1< 0) { // Report data token. m_curToken = TOKEN_DATA; return; } else if (curChar == END_OF_STRING || curChar == (int)END_OF_BUFFER) { // Just return incomplete token, no data parsed. return; } else { DE_ASSERT(m_state == STATE_TAG || m_state == STATE_ENTITY); continue; } } } else { // Eat all whitespace if present. if (m_curTokenLen == 0) { while (isWhitespaceChar(curChar)) { m_buf.popBack(); curChar = getChar(0); } } // Handle end of string / buffer. if (curChar == END_OF_STRING) error("Unexpected end of string"); else if (curChar == (int)END_OF_BUFFER) { DE_ASSERT(m_curToken == TOKEN_INCOMPLETE); return; } if (m_curTokenLen == 0) { // Expect start of identifier, value or special tag token. if (curChar == '\'' || curChar == '"') m_state = STATE_VALUE; else if (isIdentifierStartChar(curChar)) m_state = STATE_IDENTIFIER; else if (curChar == '<' || curChar == '?' || curChar == '/') m_state = STATE_TAG; else if (curChar == '&') DE_ASSERT(m_state == STATE_ENTITY); else if (curChar == '=') { m_curToken = TOKEN_EQUAL; m_curTokenLen = 1; return; } else if (curChar == '>') { m_curToken = TOKEN_TAG_END; m_curTokenLen = 1; return; } else error("Unexpected character"); } else if (m_state == STATE_IDENTIFIER) { if (!isIdentifierChar(curChar)) { m_curToken = TOKEN_IDENTIFIER; return; } } else if (m_state == STATE_VALUE) { // \todo [2012-06-07 pyry] Escapes. if (curChar == '\'' || curChar == '"') { // \todo [2012-10-17 pyry] Should we actually do the check against getChar(0)? if (curChar != getChar(0)) error("Mismatched quote"); m_curToken = TOKEN_STRING; m_curTokenLen += 1; return; } } else if (m_state == STATE_COMMENT) { DE_ASSERT(m_curTokenLen >= 2); // 2 characters have been parsed if we are in comment state. if (m_curTokenLen <= 3) { if (curChar != '-') error("Invalid comment start"); } else { int prev2 = m_curTokenLen > 5 ? getChar(m_curTokenLen-2) : 0; int prev1 = m_curTokenLen > 4 ? getChar(m_curTokenLen-1) : 0; if (prev2 == '-' && prev1 == '-') { if (curChar != '>') error("Invalid comment end"); m_curToken = TOKEN_COMMENT; m_curTokenLen += 1; return; } } } else if (m_state == STATE_ENTITY) { if (m_curTokenLen >= 1) { if (curChar == ';') { m_curToken = TOKEN_ENTITY; m_curTokenLen += 1; return; } else if (!de::inRange(curChar, '0', '9') && !de::inRange(curChar, 'a', 'z') && !de::inRange(curChar, 'A', 'Z')) error("Invalid entity"); } } else { // Special tokens are at most 2 characters. DE_ASSERT(m_state == STATE_TAG && m_curTokenLen == 1); int prevChar = getChar(m_curTokenLen-1); if (prevChar == '<') { // Tag start. if (curChar == '/') { m_curToken = TOKEN_END_TAG_START; m_curTokenLen = 2; return; } else if (curChar == '?') { m_curToken = TOKEN_PROCESSING_INSTRUCTION_START; m_curTokenLen = 2; return; } else if (curChar == '!') { m_state = STATE_COMMENT; } else { m_curToken = TOKEN_TAG_START; m_curTokenLen = 1; return; } } else if (prevChar == '?') { if (curChar != '>') error("Invalid processing instruction end"); m_curToken = TOKEN_PROCESSING_INSTRUCTION_END; m_curTokenLen = 2; return; } else if (prevChar == '/') { if (curChar != '>') error("Invalid empty element end"); m_curToken = TOKEN_EMPTY_ELEMENT_END; m_curTokenLen = 2; return; } else error("Could not parse special token"); } } m_curTokenLen += 1; curChar = getChar(m_curTokenLen); } } void Tokenizer::getString (std::string& dst) const { DE_ASSERT(m_curToken == TOKEN_STRING); dst.resize(m_curTokenLen-2); for (int ndx = 0; ndx < m_curTokenLen-2; ndx++) dst[ndx] = m_buf.peekBack(ndx+1); } Parser::Parser (void) : m_element (ELEMENT_INCOMPLETE) , m_state (STATE_DATA) { } Parser::~Parser (void) { } void Parser::clear (void) { m_tokenizer.clear(); m_elementName.clear(); m_attributes.clear(); m_attribName.clear(); m_entityValue.clear(); m_element = ELEMENT_INCOMPLETE; m_state = STATE_DATA; } void Parser::error (const std::string& what) { throw ParseError(what); } void Parser::feed (const deUint8* bytes, int numBytes) { m_tokenizer.feed(bytes, numBytes); if (m_element == ELEMENT_INCOMPLETE) advance(); } void Parser::advance (void) { if (m_element == ELEMENT_START) m_attributes.clear(); // \note No token is advanced when element end is reported. if (m_state == STATE_YIELD_EMPTY_ELEMENT_END) { DE_ASSERT(m_element == ELEMENT_START); m_element = ELEMENT_END; m_state = STATE_DATA; return; } if (m_element != ELEMENT_INCOMPLETE) { m_tokenizer.advance(); m_element = ELEMENT_INCOMPLETE; } for (;;) { Token curToken = m_tokenizer.getToken(); // Skip comments. while (curToken == TOKEN_COMMENT) { m_tokenizer.advance(); curToken = m_tokenizer.getToken(); } if (curToken == TOKEN_INCOMPLETE) { DE_ASSERT(m_element == ELEMENT_INCOMPLETE); return; } switch (m_state) { case STATE_ENTITY: m_state = STATE_DATA; // Fall-through case STATE_DATA: switch (curToken) { case TOKEN_DATA: m_element = ELEMENT_DATA; return; case TOKEN_END_OF_STRING: m_element = ELEMENT_END_OF_STRING; return; case TOKEN_TAG_START: m_state = STATE_START_TAG_OPEN; break; case TOKEN_END_TAG_START: m_state = STATE_END_TAG_OPEN; break; case TOKEN_PROCESSING_INSTRUCTION_START: m_state = STATE_IN_PROCESSING_INSTRUCTION; break; case TOKEN_ENTITY: m_state = STATE_ENTITY; m_element = ELEMENT_DATA; parseEntityValue(); return; default: error("Unexpected token"); } break; case STATE_IN_PROCESSING_INSTRUCTION: if (curToken == TOKEN_PROCESSING_INSTRUCTION_END) m_state = STATE_DATA; else if (curToken != TOKEN_IDENTIFIER && curToken != TOKEN_EQUAL && curToken != TOKEN_STRING) error("Unexpected token in processing instruction"); break; case STATE_START_TAG_OPEN: if (curToken != TOKEN_IDENTIFIER) error("Expected identifier"); m_tokenizer.getTokenStr(m_elementName); m_state = STATE_ATTRIBUTE_LIST; break; case STATE_END_TAG_OPEN: if (curToken != TOKEN_IDENTIFIER) error("Expected identifier"); m_tokenizer.getTokenStr(m_elementName); m_state = STATE_EXPECTING_END_TAG_CLOSE; break; case STATE_EXPECTING_END_TAG_CLOSE: if (curToken != TOKEN_TAG_END) error("Expected tag end"); m_state = STATE_DATA; m_element = ELEMENT_END; return; case STATE_ATTRIBUTE_LIST: if (curToken == TOKEN_IDENTIFIER) { m_tokenizer.getTokenStr(m_attribName); m_state = STATE_EXPECTING_ATTRIBUTE_EQ; } else if (curToken == TOKEN_EMPTY_ELEMENT_END) { m_state = STATE_YIELD_EMPTY_ELEMENT_END; m_element = ELEMENT_START; return; } else if (curToken == TOKEN_TAG_END) { m_state = STATE_DATA; m_element = ELEMENT_START; return; } else error("Unexpected token"); break; case STATE_EXPECTING_ATTRIBUTE_EQ: if (curToken != TOKEN_EQUAL) error("Expected '='"); m_state = STATE_EXPECTING_ATTRIBUTE_VALUE; break; case STATE_EXPECTING_ATTRIBUTE_VALUE: if (curToken != TOKEN_STRING) error("Expected value"); if (hasAttribute(m_attribName.c_str())) error("Duplicate attribute"); m_tokenizer.getString(m_attributes[m_attribName]); m_state = STATE_ATTRIBUTE_LIST; break; default: DE_ASSERT(false); } m_tokenizer.advance(); } } static char getEntityValue (const std::string& entity) { static const struct { const char* name; char value; } s_entities[] = { { "<", '<' }, { ">", '>' }, { "&", '&' }, { "'", '\''}, { """, '"' }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_entities); ndx++) { if (entity == s_entities[ndx].name) return s_entities[ndx].value; } return 0; } void Parser::parseEntityValue (void) { DE_ASSERT(m_state == STATE_ENTITY && m_tokenizer.getToken() == TOKEN_ENTITY); std::string entity; m_tokenizer.getTokenStr(entity); const char value = getEntityValue(entity); if (value == 0) error("Invalid entity '" + entity + "'"); m_entityValue.resize(1); m_entityValue[0] = value; } } // xml } // xe