Exemplo n.º 1
0
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;
	}