\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
{\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
\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
// (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
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
{\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
}\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
}\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