added xi:fallback support
[reactos.git] / reactos / tools / rbuild / XML.cpp
index 16767a5..b936df7 100644 (file)
@@ -22,6 +22,28 @@ static const char* WSEQ = " =\t\r\n";
 \r
 string working_directory;\r
 \r
+class XMLInclude\r
+{\r
+public:\r
+       XMLElement *e;\r
+       Path path;\r
+\r
+       XMLInclude ( XMLElement* e_, const Path& path_ )\r
+               : e(e_), path(path_)\r
+       {\r
+       }\r
+};\r
+\r
+class XMLIncludes : public vector<XMLInclude*>\r
+{\r
+public:\r
+       ~XMLIncludes()\r
+       {\r
+               for ( size_t i = 0; i < this->size(); i++ )\r
+                       delete (*this)[i];\r
+       }\r
+};\r
+\r
 void\r
 InitWorkingDirectory()\r
 {\r
@@ -306,6 +328,19 @@ XMLAttribute::XMLAttribute(const string& name_,
 {\r
 }\r
 \r
+XMLAttribute::XMLAttribute ( const XMLAttribute& src )\r
+       : name(src.name), value(src.value)\r
+{\r
+\r
+}\r
+\r
+XMLAttribute& XMLAttribute::operator = ( const XMLAttribute& src )\r
+{\r
+       name = src.name;\r
+       value = src.value;\r
+       return *this;\r
+}\r
+\r
 XMLElement::XMLElement ( const string& location_ )\r
        : location(location_),\r
          parentElement(NULL)\r
@@ -451,7 +486,6 @@ XMLElement::GetAttribute ( const string& attribute,
 \r
 // XMLParse()\r
 // This function reads a "token" from the file loaded in XMLFile\r
-// REM TODO FIXME: At the moment it can't handle comments or non-xml tags.\r
 // if it finds a tag that is non-singular, it parses sub-elements and/or\r
 // inner text into the XMLElement that it is building to return.\r
 // Return Value: an XMLElement allocated via the new operator that contains\r
@@ -459,13 +493,14 @@ XMLElement::GetAttribute ( const string& attribute,
 // (no more data)\r
 XMLElement*\r
 XMLParse(XMLFile& f,\r
+         XMLIncludes* includes,\r
          const Path& path,\r
-         bool* pend_tag /*= NULL*/)\r
+         bool* pend_tag = NULL )\r
 {\r
        string token;\r
        if ( !f.get_token(token) )\r
                return NULL;\r
-       bool end_tag;\r
+       bool end_tag, is_include = false;\r
 \r
        while ( token[0] != '<'\r
                || !strncmp ( token.c_str(), "<!--", 4 )\r
@@ -482,32 +517,10 @@ XMLParse(XMLFile& f,
        XMLElement* e = new XMLElement ( f.Location() );\r
        bool bNeedEnd = e->Parse ( token, end_tag );\r
 \r
-       if ( e->name == "xi:include" )\r
+       if ( e->name == "xi:include" && includes )\r
        {\r
-               XMLAttribute* att;\r
-               att = e->GetAttribute("href",true);\r
-               assert(att);\r
-\r
-               string file ( path.Fixup(att->value,true) );\r
-               string top_file ( Path::RelativeFromWorkingDirectory ( file ) );\r
-               e->attributes.push_back ( new XMLAttribute ( "top_href", top_file ) );\r
-               XMLFile fInc;\r
-               if ( !fInc.open ( file ) )\r
-                       throw FileNotFoundException (\r
-                               ssprintf("%s (referenced from %s)",\r
-                                       file.c_str(),\r
-                                       f.Location().c_str() ) );\r
-               else\r
-               {\r
-                       Path path2 ( path, att->value );\r
-                       for ( ;; )\r
-                       {\r
-                               XMLElement* e2 = XMLParse ( fInc, path2 );\r
-                               if ( !e2 )\r
-                                       break;\r
-                               e->AddSubElement ( e2 );\r
-                       }\r
-               }\r
+               includes->push_back ( new XMLInclude ( e, path ) );\r
+               is_include = true;\r
        }\r
 \r
        if ( !bNeedEnd )\r
@@ -531,7 +544,9 @@ XMLParse(XMLFile& f,
                {\r
                        if ( !f.get_token ( token ) || !token.size() )\r
                        {\r
-                               throw Exception ( "internal tool error - get_token() failed when more_tokens() returned true" );\r
+                               throw InvalidBuildFileException (\r
+                                       f.Location(),\r
+                                       "internal tool error - get_token() failed when more_tokens() returned true" );\r
                                break;\r
                        }\r
                        if ( e->subElements.size() && !bThisMixingErrorReported )\r
@@ -556,12 +571,23 @@ XMLParse(XMLFile& f,
                }\r
                else\r
                {\r
-                       XMLElement* e2 = XMLParse ( f, path, &end_tag );\r
+                       XMLElement* e2 = XMLParse ( f, is_include ? NULL : includes, path, &end_tag );\r
+                       if ( !e2 )\r
+                       {\r
+                               throw InvalidBuildFileException (\r
+                                       e->location,\r
+                                       "end of file found looking for end tag" );\r
+                               break;\r
+                       }\r
                        if ( end_tag )\r
                        {\r
                                if ( e->name != e2->name )\r
+                               {\r
+                                       delete e2;\r
                                        throw XMLSyntaxErrorException ( f.Location(),\r
                                                                        "end tag name mismatch" );\r
+                                       break;\r
+                               }\r
                                delete e2;\r
                                break;\r
                        }\r
@@ -576,3 +602,118 @@ XMLParse(XMLFile& f,
        }\r
        return e;\r
 }\r
+\r
+void\r
+XMLReadFile ( XMLFile& f, XMLElement& head, XMLIncludes& includes, const Path& path )\r
+{\r
+       for ( ;; )\r
+       {\r
+               XMLElement* e = XMLParse ( f, &includes, path );\r
+               if ( !e )\r
+                       return;\r
+               head.AddSubElement ( e );\r
+       }\r
+}\r
+\r
+XMLElement*\r
+XMLLoadInclude ( XMLElement* e, const Path& path, XMLIncludes& includes )\r
+{\r
+       // TODO FIXME\r
+       XMLAttribute* att;\r
+       att = e->GetAttribute("href",true);\r
+       assert(att);\r
+\r
+       string file ( path.Fixup(att->value,true) );\r
+       string top_file ( Path::RelativeFromWorkingDirectory ( file ) );\r
+       e->attributes.push_back ( new XMLAttribute ( "top_href", top_file ) );\r
+       XMLFile fInc;\r
+       if ( !fInc.open ( file ) )\r
+       {\r
+               // look for xi:fallback element\r
+               for ( size_t i = 0; i < e->subElements.size(); i++ )\r
+               {\r
+                       XMLElement* e2 = e->subElements[i];\r
+                       if ( e2->name == "xi:fallback" )\r
+                       {\r
+                               // now look for xi:include below...\r
+                               for ( i = 0; i < e2->subElements.size(); i++ )\r
+                               {\r
+                                       XMLElement* e3 = e2->subElements[i];\r
+                                       if ( e3->name == "xi:include" )\r
+                                       {\r
+                                               return XMLLoadInclude ( e3, path, includes );\r
+                                       }\r
+                               }\r
+                               throw InvalidBuildFileException (\r
+                                       e2->location,\r
+                                       "<xi:fallback> must have a <xi:include> sub-element" );\r
+                               return NULL;\r
+                       }\r
+               }\r
+               return NULL;\r
+       }\r
+       else\r
+       {\r
+               XMLElement* new_e = new XMLElement ( e->location );\r
+               new_e->name = "xi:included";\r
+               Path path2 ( path, att->value );\r
+               XMLReadFile ( fInc, *new_e, includes, path2 );\r
+               return new_e;\r
+       }\r
+}\r
+\r
+XMLElement*\r
+XMLLoadFile ( const string& filename, const Path& path )\r
+{\r
+       XMLIncludes includes;\r
+       XMLFile f;\r
+\r
+       if ( !f.open ( filename ) )\r
+               throw FileNotFoundException ( filename );\r
+\r
+       XMLElement* head = new XMLElement("(virtual)");\r
+\r
+       XMLReadFile ( f, *head, includes, path );\r
+\r
+       for ( size_t i = 0; i < includes.size(); i++ )\r
+       {\r
+               XMLElement* e = includes[i]->e;\r
+               XMLElement* e2 = XMLLoadInclude ( includes[i]->e, includes[i]->path, includes );\r
+               if ( !e2 )\r
+               {\r
+                       throw FileNotFoundException (\r
+                               ssprintf("%s (referenced from %s)",\r
+                                       e->GetAttribute("top_href",true)->value.c_str(),\r
+                                       f.Location().c_str() ) );\r
+               }\r
+               XMLElement* parent = e->parentElement;\r
+               XMLElement** parent_container = NULL;\r
+               if ( !parent )\r
+               {\r
+                       delete e;\r
+                       throw Exception ( "internal tool error: xi:include doesn't have a parent" );\r
+                       return NULL;\r
+               }\r
+               for ( size_t j = 0; j < parent->subElements.size(); j++ )\r
+               {\r
+                       if ( parent->subElements[j] == e )\r
+                       {\r
+                               parent_container = &parent->subElements[j];\r
+                               break;\r
+                       }\r
+               }\r
+               if ( !parent_container )\r
+               {\r
+                       delete e;\r
+                       throw Exception ( "internal tool error: couldn't find xi:include in parent's sub-elements" );\r
+                       return NULL;\r
+               }\r
+               // replace inclusion tree with the imported tree\r
+               e2->name = e->name;\r
+               e2->attributes = e->attributes;\r
+               *parent_container = e2;\r
+               e->attributes.resize(0);\r
+               delete e;\r
+       }\r
+       return head;\r
+}\r