src/tools/tXmlParser.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2005  by
00007 and the AA DevTeam (see the file AUTHORS(.txt) in the main source directory)
00008 
00009 **************************************************************************
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 GNU General Public License for more details.
00020 
00021 You should have received a copy of the GNU General Public License
00022 along with this program; if not, write to the Free Software
00023 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024 
00025 ***************************************************************************
00026 
00027 */
00028 
00029 #include "tXmlParser.h"
00030 #include "tResourceManager.h"
00031 #include "tDirectories.h"
00032 #include "tConsole.h"
00033 #include "tArray.h"
00034 
00035 #ifdef WIN32
00036 #define vsnprintf _vsnprintf
00037 #endif
00038 
00039 #ifdef __MINGW32__
00040 #define xmlFree(x) free(x)
00041 #endif
00042 
00043 // We'll use this macro to call callbacks
00044 #define CALL_MEMBER_FN(object,ptrToMember)  ((object)->*(ptrToMember))
00045 
00046 namespace tXmlParserNamespace {
00047 
00048 // These are the global callbacks
00049 
00050 void cb_startDocument(void *userData) {
00051     CALL_MEMBER_FN((tXmlParser*)userData,&tXmlParser::cb_startDocument)();
00052 }
00053 
00054 void cb_endDocument(void *userData) {
00055     CALL_MEMBER_FN((tXmlParser*)userData,&tXmlParser::cb_endDocument)();
00056 }
00057 
00058 void cb_startElement(void *userData, const xmlChar *name, const xmlChar **attrs) {
00059     CALL_MEMBER_FN((tXmlParser*)userData,&tXmlParser::cb_startElement)(name, attrs);
00060 }
00061 
00062 void cb_endElement(void *userData, const xmlChar *name) {
00063     CALL_MEMBER_FN((tXmlParser*)userData,&tXmlParser::cb_endElement)(name);
00064 }
00065 
00066 
00067 // These next 3 just ripped from libxml2
00068 void cb_warning(void *ctx , const char *msg, ...)
00069 {
00070     va_list args;
00071 
00072     va_start(args, msg);
00073     fprintf(stdout, "SAX.warning: ");
00074     vfprintf(stdout, msg, args);
00075     va_end(args);
00076 }
00077 
00078 void cb_error(void *ctx , const char *msg, ...)
00079 {
00080     va_list args;
00081 
00082     va_start(args, msg);
00083     fprintf(stdout, "SAX.error: ");
00084     vfprintf(stdout, msg, args);
00085     va_end(args);
00086 }
00087 
00088 void cb_fatalError(void *ctx , const char *msg, ...)
00089 {
00090     va_list args;
00091 
00092     va_start(args, msg);
00093     fprintf(stdout, "SAX.fatalError: ");
00094     vfprintf(stdout, msg, args);
00095     va_end(args);
00096 }
00097 
00098 
00099 /*
00100     internalSubsetSAXFunc internalSubset;
00101     isStandaloneSAXFunc isStandalone;
00102     hasInternalSubsetSAXFunc hasInternalSubset;
00103     hasExternalSubsetSAXFunc hasExternalSubset;
00104     resolveEntitySAXFunc resolveEntity;
00105     getEntitySAXFunc getEntity;
00106     entityDeclSAXFunc entityDecl;
00107     notationDeclSAXFunc notationDecl;
00108     attributeDeclSAXFunc attributeDecl;
00109     elementDeclSAXFunc elementDecl;
00110     unparsedEntityDeclSAXFunc unparsedEntityDecl;
00111     setDocumentLocatorSAXFunc setDocumentLocator;
00112     startDocumentSAXFunc startDocument;
00113     endDocumentSAXFunc endDocument;
00114     startElementSAXFunc startElement;
00115     endElementSAXFunc endElement;
00116     referenceSAXFunc reference;
00117     charactersSAXFunc characters;
00118     ignorableWhitespaceSAXFunc ignorableWhitespace;
00119     processingInstructionSAXFunc processingInstruction;
00120     commentSAXFunc comment;
00121     warningSAXFunc warning;
00122     errorSAXFunc error;
00123     fatalErrorSAXFunc fatalError;
00124 
00125 */
00126 
00127 xmlSAXHandler aaSaxCallsback = {
00128                                    NULL,
00129                                    NULL,
00130                                    NULL,
00131                                    NULL,
00132                                    NULL,
00133                                    NULL,
00134                                    NULL,
00135                                    NULL,
00136                                    NULL,
00137                                    NULL,
00138                                    NULL,
00139                                    NULL,
00140                                    cb_startDocument,
00141                                    cb_endDocument,
00142                                    cb_startElement,
00143                                    cb_endElement,
00144                                    NULL,
00145                                    NULL,
00146                                    NULL,
00147                                    NULL,
00148                                    NULL,
00149                                    cb_warning,
00150                                    cb_error,
00151                                    cb_fatalError,
00152                                    NULL,
00153                                    NULL,
00154                                    NULL,
00155                                    1,
00156                                    NULL,
00157                                    NULL,
00158                                    NULL,
00159                                    NULL
00160                                };
00161 
00162 int myxmlInputReadFILE (void *context, char *buffer, int len) {
00163     return fread(buffer, 1, len, (FILE *)context);
00164 }
00165 
00166 int myxmlInputCloseFILE (void *context) {
00167     return (fclose((FILE *)context) == 0) ? 0 : -1;
00168 }
00169 
00170 #ifndef HAVE_LIBXML2_WO_PIBCREATE
00171 static bool sg_IgnoreRequest( tString const & URI )
00172 {
00173 #ifdef WIN32
00174     return URI.EndsWith( "../etc/catalog" );
00175 #else
00176     return URI.StartsWith( "file:///" ) && strstr( URI, "xml" ) && URI.EndsWith( "catalog" );
00177 #endif
00178 }
00179 
00180 xmlParserInputBufferPtr myxmlParserInputBufferCreateFilenameFunc (const char *URI, xmlCharEncoding enc) {
00181     if ( sg_IgnoreRequest( tString( URI ) ) )
00182     {
00183 #ifdef DEBUG
00184         printf("Ignoring xml request for %s\n", URI);
00185 #endif
00186         return NULL;
00187     }
00188 #ifdef DEBUG
00189     //  con << "xml wants " << URI << "\n";
00190 #endif
00191     FILE *f = tResourceManager::openResource(URI, NULL);
00192     if (f == NULL)
00193         return NULL;
00194     xmlParserInputBufferPtr ret = xmlAllocParserInputBuffer(enc);
00195     ret->context = f;
00196     ret->readcallback = myxmlInputReadFILE;
00197     ret->closecallback = myxmlInputCloseFILE;
00198     return ret;
00199 }
00200 #endif
00201 
00202 void tXmlParser::cb_startDocument() {
00203     startDocument();
00204 }
00205 
00206 void tXmlParser::startDocument() {
00207     // Do nothing
00208 }
00209 
00210 void tXmlParser::cb_endDocument() {
00211     endDocument();
00212 }
00213 
00214 void tXmlParser::endDocument() {
00215     // Do nothing
00216 }
00217 
00218 void tXmlParser::cb_startElement(const xmlChar* name, const xmlChar** attrs) {
00219     tAttributeList attributes;
00220     if (attrs != NULL) {
00221         for (int i = 0; (attrs[i] != NULL); i++) {
00222             tString attributeName( (const char*) attrs[i] );
00223             tString attributeValue;
00224             i++;
00225 
00226             if (attrs[i] != NULL) {
00227                 attributeValue = tString( (const char*)attrs[i] );
00228             } else {
00229                 attributeValue = tString();
00230             }
00231             attributes[attributeName] = attributeValue;
00232         }
00233         tString elementName((const char*)name);
00234         startElement(elementName, attributes );
00235     }
00236 }
00237 
00238 void tXmlParser::startElement(tString &element, tAttributeList &attributes) {
00239     // Do nothing
00240 }
00241 
00242 tXmlParser::~tXmlParser() {
00243     if (m_Doc)
00244     {
00245         xmlFreeDoc(m_Doc);
00246         m_Doc=0;
00247     }
00248 }
00249 
00250 void tXmlParser::cb_endElement(const xmlChar *name) {
00251     tString elementName((const char*)name);
00252     endElement(elementName);
00253 }
00254 
00255 void tXmlParser::endElement(tString &element) {
00256     // Do nothing
00257 }
00258 
00259 bool tXmlParser::LoadWithoutParsing(const char* filename, const char* uri) {
00260     bool success=false;
00261     FILE* docfd;
00262 
00263     docfd = tResourceManager::openResource(filename, uri);
00264     m_Filename = tResourceManager::locateResource(filename, uri);
00265 
00266     if ( docfd )
00267     {
00268         success = ValidateXml(docfd, uri, filename);
00269         fclose(docfd);
00270     }
00271 
00272     return success;
00273 }
00274 
00275 bool tXmlParser::LoadWithParsing(const char* filename, const char *uri) {
00276     bool success=false;
00277     FILE* docfd;
00278 
00279     if(!(docfd = tResourceManager::openResource(filename, uri))) {
00280         con << "Loading XML file '" << filename << "' failed!\n";
00281         return false;
00282     }
00283     m_Filename = tResourceManager::locateResource(filename, uri);
00284 
00285     success = ValidateXml(docfd, uri, filename);
00286     fclose(docfd);
00287     if(success) {
00288         return Parse();
00289     } else {
00290         return false;
00291     }
00292 }
00293 
00294 bool tXmlResource::LoadFile(const char* filename, const char* uri) {
00295     m_Filename = tResourceManager::locateResource(filename, uri);
00296     return LoadXmlFile(m_Filename, uri);
00297 }
00298 
00299 bool tXmlParser::LoadFile(const char* filename, const char* uri) {
00300     return LoadXmlFile(filename, uri);
00301 }
00302 
00303 bool tXmlParser::LoadXmlFile(const char* filename, const char* uri) {
00304     bool goOn;
00305 
00306     FILE* docfd;
00307 
00308     docfd = fopen(filename, "r");
00309 
00310     goOn = ValidateXml(docfd, uri, filename);
00311 
00312     if(goOn) {
00313         return Parse();
00314     } else {
00315         return false;
00316     }
00317 }
00318 
00319 bool tXmlParser::Parse() {
00320     if(m_Mode == DOM) {
00321         return ParseDom();
00322     } else {
00323         return ParseSax();
00324     }
00325 }
00326 
00327 // Subclasses need to override this, default method provided that does nothing
00328 bool tXmlParser::ParseDom() {
00329     return true;
00330 }
00331 
00332 bool tXmlParser::ParseSax() {
00333     if (xmlSAXUserParseFile(&aaSaxCallsback, this, m_Filename) < 0) {
00334         return false;
00335     } else
00336         return true;
00337 }
00338 
00339 #ifndef DEDICATED
00340 static tString st_errorLeadIn("");
00341 
00342 static void st_ErrorFunc( void * ctx,
00343                           const char * msg,
00344                           ... )
00345 {
00346     // print formatted message into buffer
00347     static int maxlen = 100;
00348     tArray<char> buffer;
00349     bool retry = true;
00350     while ( retry )
00351     {
00352         buffer.SetLen( maxlen );
00353         va_list ap;
00354         va_start(ap, msg);
00355         retry = vsnprintf(&buffer[0], maxlen, msg, ap) >= maxlen;
00356         va_end(ap);
00357 
00358         if ( retry )
00359             maxlen *= 2;
00360     }
00361     char * message = &buffer[0];
00362 
00363     // print buffer to stderr and console
00364     if ( st_errorLeadIn.Len() > 2 )
00365     {
00366         con << st_errorLeadIn;
00367 #ifndef DEBUG
00368         std::cerr << st_errorLeadIn;
00369 #endif
00370         st_errorLeadIn = "";
00371     }
00372 
00373 #ifndef DEBUG
00374     std::cerr << message;
00375 #endif
00376 
00377     con << message;
00378 }
00379 #endif
00380 
00381 bool tXmlParser::ValidateXml(FILE* docfd, const char* uri, const char* filepath)
00382 {
00383 #ifndef DEDICATED
00384     /* register error handler */
00385     xmlGenericErrorFunc errorFunc = &st_ErrorFunc;
00386     initGenericErrorDefaultFunc( &errorFunc );
00387     st_errorLeadIn = "XML validation error in ";
00388     st_errorLeadIn += filepath;
00389     st_errorLeadIn += ":\n\n";
00390 #endif
00391 
00392     bool validated = false;
00393 
00394     if (docfd == NULL) {
00395         printf("LoadAndValidateMapXML passed a NULL docfd (we should really trap this somewhere else!)\n");
00396         return false;
00397     }
00398 
00399 #ifndef HAVE_LIBXML2_WO_PIBCREATE
00400     //xmlSetExternalEntityLoader(myxmlResourceEntityLoader);
00401     xmlParserInputBufferCreateFilenameDefault(myxmlParserInputBufferCreateFilenameFunc);    //should be moved to some program init area
00402 #endif
00403 
00404     if (m_Doc)
00405     {
00406         xmlFreeDoc(m_Doc);
00407         m_Doc=0;
00408     }
00409 
00410     /*Validate the xml*/
00411     xmlParserCtxtPtr ctxt; /*Parser context*/
00412 
00413     ctxt = xmlNewParserCtxt();
00414 
00415     if (ctxt == 0) {
00416         fprintf(stderr, "Failed to allocate parser context\n");
00417         return false;
00418     }
00419 
00420     /* parse the file, activating the DTD validation option */
00421     m_Doc = xmlCtxtReadIO(ctxt, myxmlInputReadFILE, 0, docfd,
00422 #if HAVE_LIBXML2_WO_PIBCREATE
00423                           (const char *)tDirectories::Resource().GetReadPath("map-0.1.dtd")
00424                           // TODO: don't hardcode the file
00425 #else
00426                           uri
00427 #endif
00428                           , NULL, XML_PARSE_DTDVALID);
00429     // NOTE: Do *not* pass myxmlInputCloseFILE; we close the file *later*
00430 
00431     /* check if parsing suceeded */
00432     if (m_Doc == NULL) {
00433         fprintf(stderr, "Failed to parse \n");
00434     } else {
00435         /* check if validation suceeded */
00436         if (ctxt->valid == 0) {
00437             fprintf(stderr, "Failed to validate \n");
00438             xmlFreeDoc(m_Doc);
00439             m_Doc=NULL;
00440         }
00441         else
00442         {
00443             validated = true;
00444         }
00445     }
00446 
00447     /* free up the parser context */
00448     xmlFreeParserCtxt(ctxt);
00449 
00450 #ifndef DEDICATED
00451     /* reset error handler */
00452     initGenericErrorDefaultFunc( NULL );
00453 #endif
00454     return validated;
00455 }
00456 
00457 bool tXmlResource::ValidateXml(FILE* docfd, const char* uri, const char* filepath) {
00458     bool validated = tXmlParser::ValidateXml(docfd, uri, filepath);
00459 
00460     /* check filepath */
00461     if ( validated && filepath )
00462     {
00463         node root = GetRoot();
00464 
00465         if (!root) {
00466             con << "Empty document\n";
00467             return false;
00468         } else if (root.IsOfType("Resource")) {
00469             m_Path = tResourcePath (
00470                 root.GetProp("author"),
00471                 root.GetProp("category"),
00472                 root.GetProp("name"),
00473                 root.GetProp("version"),
00474                 root.GetProp("type"),
00475                 tString("xml"),
00476                 tString("")
00477             );
00478             tString rightFilepath( m_Path.Path() );
00479             tString pureFilepath( filepath );
00480             int pos;
00481             while((pos = pureFilepath.StrPos("//")) != -1) {
00482                 pureFilepath.RemoveSubStr(pos, 1);
00483             }
00484             tResourcePath purepath(pureFilepath);
00485             if ( purepath != m_Path )
00486             {
00487                 con << "\nWARNING: incorrect filepath. The resource wants to be at \"" << rightFilepath << "\", but was loaded from \"" << filepath << "\".\n\n";
00488             }
00489         }
00490         else {
00491             con << "Root node is not of type 'Resource' but '" << root.GetName() << "'.\n";
00492             return false;
00493         }
00494     }
00495 
00496     return validated;
00497 }
00498 
00499 tXmlParser::node tXmlResource::GetFileContents(void) {
00500     for(node cur = GetRoot().GetFirstChild(); cur; ++cur) {
00501         if(!cur.IsOfType("comment") && !cur.IsOfType("text")) {
00502             return cur;
00503         }
00504     }
00505     return 0;
00506 }
00507 
00508 tXmlParser::node tXmlParser::GetRoot() {
00509     return node(xmlDocGetRootElement(m_Doc));
00510 }
00511 
00513 tXmlParser::node::node(xmlNode *cur) : m_cur(cur) {
00514     //tASSERT(m_cur);
00515 }
00516 
00519 bool tXmlParser::node::IsOfType(CHAR const *name) const {
00520     tASSERT(m_cur);
00521     return(!xmlStrcmp(m_cur->name, reinterpret_cast<xmlChar const *>(name)));
00522 }
00523 
00525 tString tXmlParser::node::GetName(void) const {
00526     tASSERT(m_cur);
00527     return tString(reinterpret_cast<const char *>(m_cur->name));
00528 }
00529 
00532 bool tXmlParser::node::HasProp(CHAR const *prop) const {
00533     tASSERT(m_cur);
00534     return xmlHasProp(m_cur,
00535                       reinterpret_cast<const xmlChar *>
00536                       (prop)
00537                      );
00538 }
00539 
00543 tString tXmlParser::node::GetProp(CHAR const *prop) const {
00544     tASSERT(m_cur);
00545     xmlChar *val = xmlGetProp(m_cur,
00546                               reinterpret_cast<const xmlChar *>
00547                               (prop)
00548                              );
00549     if(val == 0) {
00550         tERR_WARN(tString("Call for non- existent Attribute '") + tString(prop) + "' of element of type '" + GetName() + '"');
00551         st_Breakpoint();
00552         return tString();
00553     }
00554     tString ret(reinterpret_cast<const char *>(val));
00555     xmlFree(val);
00556     return(ret);
00557 }
00558 
00562 bool tXmlParser::node::GetPropBool(CHAR const *prop) const {
00563     tString string(GetProp(prop));
00564     if (string.empty()) return false;
00565     switch(string[0]) {
00566 case 't': case 'T':
00567 case 'y': case 'Y':
00568         return true;
00569     default:
00570         if(string == "on") return true;
00571         int i;
00572         return string.Convert(i) && i;
00573     }
00574 }
00575 
00577 tXmlParser::node &tXmlParser::node::operator++() {
00578     tASSERT(m_cur);
00579     m_cur=m_cur->next;
00580     return *this;
00581 }
00583 tXmlParser::node const tXmlParser::node::operator++(int) {
00584     tASSERT(m_cur);
00585     xmlNode *old = m_cur;
00586     m_cur=m_cur->next;
00587     return old;
00588 }
00589 
00591 tXmlParser::node tXmlParser::node::GetFirstChild(void) const {
00592     tASSERT(m_cur);
00593     return m_cur->xmlChildrenNode;
00594 }
00595 
00597 tXmlParser::node::operator bool() const {
00598     return(m_cur != 0);
00599 }
00600 
00601 }
00602 
00603 #ifdef _MSC_VER
00604 void tXmlParser::node::GetProp(CHAR const *prop, int &target) const {
00605     if(!(GetProp(prop).Convert(target))) {
00606         tERR_WARN( "Property '" + tString(prop) + "' of node of type '" + GetName() + "' is '" + GetProp(prop) + "' which isn't of type '" + typeid(int).name() + "' as needed.");
00607     }
00608 }
00609 void tXmlParser::node::GetProp(CHAR const *prop, REAL &target) const {
00610     if(!(GetProp(prop).Convert(target))) {
00611         tERR_WARN( "Property '" + tString(prop) + "' of node of type '" + GetName() + "' is '" + GetProp(prop) + "' which isn't of type '" + typeid(REAL).name() + "' as needed.");
00612     }
00613 }
00614 #endif
00615 

Generated on Sat Mar 15 22:56:02 2008 for Armagetron Advanced by  doxygen 1.5.4