bool ParseElement (ParserCtx *pCtx, CXMLElement **retpElement) // ParseElement // // Parses an element and returns it. We assume that we've already // parsed an open tag { CXMLElement *pElement; ASSERT(pCtx->iToken == tkTagOpen); // Parse the tag name if (ParseToken(pCtx) != tkText) { pCtx->sError = ERR_ELEMENT_TAG_EXPECTED; return false; } // Create a new element with the tag pElement = new CXMLElement(pCtx->sToken, pCtx->pElement); if (pElement == NULL) throw CException(errOutOfMemory); // Keep parsing until the tag is done ParseToken(pCtx); while (pCtx->iToken != tkTagClose && pCtx->iToken != tkSimpleTagClose) { // If we've got an identifier then this must be an attribute if (pCtx->iToken == tkText) { CString sAttribute = pCtx->sToken; CString sValue; // Expect an equals sign if (ParseToken(pCtx) != tkEquals) { pCtx->sError = ERR_EQUAL_EXPECTED; delete pElement; return false; } // Expect a quote ParseToken(pCtx); if (pCtx->iToken != tkQuote && pCtx->iToken != tkSingleQuote) { pCtx->sError = ERR_ATTRIB_NEEDS_QUOTES; delete pElement; return false; } // Remember what kind of qoute we used so that we can match it // (and so we ignore the other kind inside it). pCtx->iAttribQuote = pCtx->iToken; // Expect the value ParseToken(pCtx, AttributeState); if (pCtx->iToken == tkText) { sValue = pCtx->sToken; ParseToken(pCtx); } else sValue = NULL_STR; // Now expect an end-quote if (pCtx->iToken != pCtx->iAttribQuote) { if (pCtx->iToken != tkError || pCtx->sError.IsEmpty()) pCtx->sError = ERR_MISMATCHED_ATTRIB_QUOTE; delete pElement; return false; } // Add the attribute to the element pElement->AddAttribute(sAttribute, sValue); // Parse the next token ParseToken(pCtx); } // Otherwise this is an error else { if (pCtx->iToken != tkError || pCtx->sError.IsEmpty()) pCtx->sError = ERR_ATTRIB_EXPECTED; delete pElement; return false; } } // Give our controller a chance to deal with an element // (We use this in Transcendence to parse the <Library> element, which // contains external entities). // // NOTE: We only worry about top-level elements (i.e., elements immediately // under the root). if (pCtx->m_pController && pCtx->pElement && pCtx->pElement->GetParentElement() == NULL) { if (!pCtx->m_pController->OnOpenTag(pElement, &pCtx->sError)) { delete pElement; return false; } } // If we don't have an empty element then keep parsing until // we find a close tag if (!pCtx->m_bParseRootElement && pCtx->iToken == tkTagClose) { CXMLElement *pParentElement; // We are recursing pParentElement = pCtx->pElement; pCtx->pElement = pElement; // Parse until we've got the begin close tag while (ParseToken(pCtx, ContentState) != tkEndTagOpen) { // If this is text then append it as content if (pCtx->iToken == tkText) pElement->AppendContent(pCtx->sToken); // Otherwise, append an element else if (pCtx->iToken == tkTagOpen) { CXMLElement *pSubElement; if (!ParseElement(pCtx, &pSubElement)) { pCtx->pElement = pParentElement; delete pElement; return false; } pElement->AppendSubElement(pSubElement); } // Otherwise we're in trouble else { pCtx->pElement = pParentElement; if (pCtx->iToken != tkError || pCtx->sError.IsEmpty()) pCtx->sError = ERR_CONTENT_EXPECTED; delete pElement; return false; } } // Done pCtx->pElement = pParentElement; // The element tag should match ours if (ParseToken(pCtx) != tkText || strEqualsNoCase(pCtx->sToken, pElement->GetTag())) { pCtx->sError = ERR_UNMATCHED_CLOSE_TAG; delete pElement; return false; } // Parse the end tag if (ParseToken(pCtx) != tkTagClose) { pCtx->sError = ERR_CLOSE_TAG_EXPECTED; delete pElement; return false; } } // Done *retpElement = pElement; return true; }