//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "rl.h" #include #include #define CHECKHR(x) {hr = x; if (FAILED(hr)) goto CleanUp;} #define SAFERELEASE(p) {if (p) {(p)->Release(); p = NULL;}} namespace Xml { Node * Node::TopNode; //----------------------------------------------------------------------------- // // Description: // // Constructor for Attribute class. // //----------------------------------------------------------------------------- Attribute::Attribute ( Char * name, Char * value ) : Name(name) , Value(value) , Next(NULL) {} Char * Attribute::GetValue ( const Char * name ) { for (Attribute * p = this; p != NULL; p = p->Next) { if (strcmp(p->Name, name) == 0) { return p->Value; } } return NULL; } void Attribute::Dump() { for (Attribute * attr = this; attr != NULL; attr = attr->Next) { printf("%s=\"%s\"", attr->Name, attr->Value); if (attr->Next != NULL) { printf(" "); } } printf("\n"); } //----------------------------------------------------------------------------- // // Description: // // Constructor for Node class. // // //----------------------------------------------------------------------------- Node::Node ( const Char * name, Attribute * attributeList ) : Name(name) , AttributeList(attributeList) , Next(NULL) , ChildList(NULL) , Data(NULL) // , LineNumber(Xml::LineNumber) {} Node * Node::GetChild ( const Char * name ) { for (Node * p = this->ChildList; p != NULL; p = p->Next) { if (strcmp(p->Name, name) == 0) { return p; } } return NULL; } Char * Node::GetAttributeValue ( const Char * name ) { return this->AttributeList->GetValue(name); } void Node::Dump ( int indent ) { for (int i = 0; i < indent; i++) { printf(" "); } printf("Node %s ", this->Name); this->AttributeList->Dump(); if (this->Data != NULL) { for (int i = 0; i <= indent; i++) { printf(" "); } printf("Data: %s\n", this->Data); } else { for (Node * child = this->ChildList; child != NULL; child = child->Next) { child->Dump(indent + 1); } } } void Node::Dump() { this->Dump(0); } Char * ConvertWCHAR ( const WCHAR * pWchar ) { int len = ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pWchar, -1, NULL, 0, NULL, NULL); Char * newStr = new char[len + 1]; ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pWchar, -1, newStr, len + 1, NULL, NULL); return newStr; } HRESULT ParseAttributes ( IXmlReader * pReader, Attribute ** ppAttrList ) { HRESULT hr; const WCHAR * tmpString; Attribute * attrLast = nullptr; while (S_OK == (hr = pReader->MoveToNextAttribute())) { pReader->GetLocalName(&tmpString, nullptr); Char * attrName = ConvertWCHAR(tmpString); pReader->GetValue(&tmpString, nullptr); Char * attrValue = ConvertWCHAR(tmpString); Attribute * attrItem = new Attribute(attrName, attrValue); if (attrLast != nullptr) { attrLast->Next = attrItem; } else { *ppAttrList = attrItem; } attrLast = attrItem; } return hr; } HRESULT ParseNode ( IXmlReader * pReader, Node ** ppNode ) { HRESULT hr; XmlNodeType nodeType; Char * nodeName = nullptr; Attribute * attrList = nullptr; Node * childList = nullptr; Node * childLast = nullptr; const WCHAR * tmpString; #define APPEND_CHILD(childNode) \ if (childLast == nullptr) \ { \ childList = childLast = childNode; \ } \ else \ { \ childLast->Next = childNode; \ childLast = childNode; \ } // This call won't fail we make sure the reader is positioned at a valid // node before ParseNode() is called. pReader->GetNodeType(&nodeType); do { switch (nodeType) { case XmlNodeType_Element: { bool inOpenElement = nodeName != nullptr; if (inOpenElement) { Node * childNode; hr = ParseNode(pReader, &childNode); if (hr == S_OK) { APPEND_CHILD(childNode); } else { return hr; } } else { pReader->GetLocalName(&tmpString, nullptr); nodeName = ConvertWCHAR(tmpString); hr = ParseAttributes(pReader, &attrList); if (FAILED(hr)) { *ppNode = nullptr; return hr; } *ppNode = new Node(nodeName, attrList); if (pReader->IsEmptyElement()) { return S_OK; } } break; } case XmlNodeType_EndElement: { Node * node = *ppNode; // If we have a single child with data called "#text", then pull the data up to this node. if (childList != nullptr && childList == childLast && (childList->Data != nullptr) && (_stricmp(childList->Name, "#text") == 0)) { node->Data = childList->Data; node->ChildList = nullptr; } else { node->ChildList = childList; } return S_OK; } case XmlNodeType_Attribute: // Need to manually move to attributes when at an element node. break; case XmlNodeType_CDATA: case XmlNodeType_Text: { pReader->GetValue(&tmpString, nullptr); Node * node = new Node("#text", nullptr); node->Data = ConvertWCHAR(tmpString); APPEND_CHILD(node); break; } case XmlNodeType_Comment: case XmlNodeType_DocumentType: case XmlNodeType_None: case XmlNodeType_ProcessingInstruction: case XmlNodeType_Whitespace: case XmlNodeType_XmlDeclaration: // Ignored. break; } } while (S_OK == (hr = pReader->Read(&nodeType))); return hr; #undef APPEND_CHILD } HRESULT ParseXml ( IXmlReader * pReader, Node ** ppNode ) { HRESULT hr; XmlNodeType nodeType; // ParseNode() ignores the XML declaration node, so there can be only one // top level node. if (SUCCEEDED(hr = pReader->Read(&nodeType))) { return ParseNode(pReader, ppNode); } return hr; } HRESULT CreateStreamOnHandle ( HANDLE handle, IStream ** ppStream ) { // Note that this function reads the whole file into memory. // // There is no API on ARM similar to SHCreateStreamOnFileEx which creates // an IStream object that reads a file lazily. Rather than writing our own // IStream implementation that does this, we just read the whole file here // given that XML files don't get quite large and it should be okay to keep // everything in memory. DWORD fileSize, fileSizeHigh, bytesRead; HGLOBAL buffer; fileSize = GetFileSize(handle, &fileSizeHigh); if (fileSize == INVALID_FILE_SIZE || fileSizeHigh != 0) { return E_FAIL; } buffer = GlobalAlloc(GPTR, fileSize + 1); if (buffer == nullptr) { return E_FAIL; } if (!::ReadFile(handle, buffer, fileSize, &bytesRead, nullptr) || FAILED(CreateStreamOnHGlobal(buffer, /* fDeleteOnRelease */ true, ppStream))) { GlobalFree(buffer); return E_FAIL; } return S_OK; } Node * ReadFile ( const char * fileName ) { IStream * pStream; IXmlReader * pReader; HANDLE fileHandle = CreateFile(fileName, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { Fatal("Cannot open XML file %s", fileName); } if (FAILED(CreateStreamOnHandle(fileHandle, &pStream))) { Fatal("Cannot create stream from file"); } if (FAILED(CreateXmlReader(__uuidof(IXmlReader), (void**) &pReader, nullptr))) { Fatal("Cannot create XML reader"); } if (FAILED(pReader->SetProperty(XmlReaderProperty_DtdProcessing, DtdProcessing_Prohibit))) { Fatal("Cannot prohibit DTD processing"); } if (FAILED(pReader->SetInput(pStream))) { Fatal("Cannot set XML reader input"); } Node * topNode; if (FAILED(ParseXml(pReader, &topNode))) { unsigned int line, linePos; pReader->GetLineNumber(&line); pReader->GetLinePosition(&linePos); fprintf( stderr, "Error on line %d, position %d in \"%s\".\n", line, linePos, fileName); Fatal("Error parsing XML"); } SAFERELEASE(pReader); SAFERELEASE(pStream); CloseHandle(fileHandle); return topNode; } } // namespace Xml