/* * ConfigParser_XML * * Author: Andrew Salamon * Date: Wed Mar 5, 2008 * * Copyright (c) 2008 Machine Perception Laboratory * University of California San Diego. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "ConfigParser_XML.h" #ifdef WIN32 #include #else extern "C" { #include } #define xmlFree(x) free(x) #endif #include "xmlSmartPtr.h" namespace CERT { std::vector< std::string > getArrayNode( xmlNodePtr parent ); ConfigItem::PropertyType getMapNode( xmlNodePtr parent ); bool getIntProperty( xmlNodePtr cur, const char *name, int *val ); bool getStringProperty( xmlNodePtr cur, const char *name, std::string &val ); void getItemProperties( ConfigItem *item, xmlNodePtr cur ); ConfigParser_XML::ConfigParser_XML( Config *_config, const std::string &_path ) : config(_config), path(_path) { parseFile(); } ConfigParser_XML::~ConfigParser_XML() { } void ConfigParser_XML::parseFile() { // We can use this alternate API to suppress error and warning messages, so we can do our own error handling xmlParserCtxtPtr xmlCtxt = xmlNewParserCtxt(); // Make sure the parser context get's deleted no matter how we exit the method xmlSmartPtr ctxtDel( xmlCtxt, (xmlSmartPtr::deleter)xmlFreeParserCtxt ); if( xmlInitParserCtxt( xmlCtxt ) ) { throw exception( "Couldn't init Parser context" ); } if( xmlCtxtUseOptions( xmlCtxt, (XML_PARSE_NOERROR | XML_PARSE_NOWARNING) ) ) { throw exception( "Couldn't set Parser context options" ); } xmlDocPtr doc = xmlCtxtReadFile( xmlCtxt, path.c_str(), NULL, (XML_PARSE_NOERROR | XML_PARSE_NOWARNING) ); if( doc == NULL ) { // Does libxml2 provide any error messages for us to access? throw exception( "Couldn't read (or parse) xml config file" ); } // Make sure the doc get's free'd no matter what xmlSmartPtr docDel( doc, xmlFreeDoc ); xmlNodePtr cur = xmlDocGetRootElement(doc); if( !cur ) { throw exception( "Empty Document" ); } if(xmlStrcmp(cur->name, (const xmlChar*) "config")) { throw exception( "Invalid root element" ); } int version; if( !getIntProperty( cur, "version", &version ) ) { throw exception( "Missing 'version' attribute" ); } for( cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next ) { ConfigItem *item = NULL; if(!xmlStrcmp(cur->name, (const xmlChar*)"plugin")) { item = new ConfigItem(); config->addItem( item ); } if(!xmlStrcmp(cur->name, (const xmlChar*)"global")) item = &config->getGlobalItem(); if( item ) getItemProperties( item, cur ); } } void getItemProperties( ConfigItem *item, xmlNodePtr cur ) { // walk through all of the properties of this plugin for( xmlNodePtr prop = cur->xmlChildrenNode; prop != NULL; prop = prop->next ) { std::string name( (char *)prop->name ); std::string nodeType( "string" ); char *content = NULL; getStringProperty( prop, "type", nodeType ); if( "string" == nodeType ) content = (char *)xmlNodeGetContent( prop ); if( "id" == name ) item->setPluginID( content ); else if( "name" == name ) item->setName( content ); else if( "iname" == name ) item->setInternalName( content ); else if( "type" == name ) item->setPluginType( content ); else if( "enabled" == name ) item->setEnabled( true ); else if( "disabled" == name ) item->setEnabled( false ); else if( "display" == name ) item->setDisplay( true ); else if( "nodisplay" == name ) item->setDisplay( false ); else if( "text" == name ) ; // We probably ought to see if the text is all white space. If not, then maybe keep it? else { // We need to use the generic {get|set}ValueForKey if( "string" == nodeType ) { std::string contStr( (char *)content ); // If the node is empty, then assume it's a boolean and set it to true, since it's present if( 0 == contStr.length() ) contStr = "true"; item->setValueForKey( contStr, name ); } else if( "array" == nodeType ) { std::vector< std::string > array = getArrayNode( prop ); if( array.size() > 0 ) item->setValueForKey( array, name ); } else if( "map" == nodeType ) { ConfigItem::PropertyType mapNode = getMapNode( prop ); if( mapNode.size() > 0 ) item->setValueForKey( mapNode, name ); } } if( content ) xmlFree( content ); } } ConfigItem::PropertyType getMapNode( xmlNodePtr parent ) { ConfigItem::PropertyType values; for( xmlNodePtr node = parent->xmlChildrenNode; node != NULL; node = node->next ) { std::string key( (char *)node->name ); if( (key.length() > 0) && ("text" != key) && ("comment" != key) ) { std::vector< std::string > value; std::string nodeType( "string" ); getStringProperty( node, "type", nodeType ); if( "string" == nodeType ) { char *content = (char *)xmlNodeGetContent( node ); std::string contStr( (char *)content ); // If the node is empty, then assume it's a boolean and set it to true, since it's present if( 0 == contStr.length() ) contStr = "true"; value.push_back( contStr ); xmlFree( content ); } else if( "array" == nodeType ) { value = getArrayNode( node ); } else { std::cerr << "Unknown map property node type for key '" << key << "': " << nodeType << std::endl; } if( key.length() > 0 && value.size() > 0 ) { values[key] = value; } } } return values; } /* ConfigItem::PropertyType getMapNode( xmlNodePtr parent ) { ConfigItem::PropertyType values; xmlNodePtr node = parent->xmlChildrenNode; while( node != NULL ) { std::string name( (char *)node->name ); std::string key; std::vector< std::string > value; // There can be 'text' nodes (mostly whitespace) and possibly comment nodes to skip while( node && (name != "key") ) { node = node->next; if( node ) name = (char *)node->name; } if( "key" == name ) { char *content = (char *)xmlNodeGetContent( node ); if( content ) { key = content; xmlFree( content ); } } else { // throw exception std::cerr << "Error in map property, " << name << ". Missing key" << std::endl; } while( node && (name != "value") ) { node = node->next; if( node ) name = (char *)node->name; } if( "value" == name ) { std::string nodeType( "string" ); getStringProperty( node, "type", nodeType ); if( "string" == nodeType ) { char *content = (char *)xmlNodeGetContent( node ); std::string contStr( (char *)content ); // If the node is empty, then assume it's a boolean and set it to true, since it's present if( 0 == contStr.length() ) contStr = "true"; value.push_back( contStr ); xmlFree( content ); } else if( "array" == nodeType ) { value = getArrayNode( node ); } } else { // throw exception std::cerr << "Error in map property, " << name << ". Missing value" << std::endl; } if( key.length() > 0 && value.size() > 0 ) { values[key] = value; } if( node ) node = node->next; } return values; } */ std::vector< std::string > getArrayNode( xmlNodePtr parent ) { std::vector< std::string > values; for( xmlNodePtr value = parent->xmlChildrenNode; value != NULL; value = value->next ) { std::string name( (char *)value->name ); if( "item" == name ) { char *content = (char *)xmlNodeGetContent( value ); if( content ) { values.push_back( content ); xmlFree( content ); } } } return values; } bool getIntProperty( xmlNodePtr cur, const char *name, int *val ) { char *prop = (char*)xmlGetProp( cur, (const xmlChar *)name ); bool good=true; if(!prop) return false; if( sscanf( prop, "%d", val ) != 1 ) good = false; xmlFree( prop ); return good; } bool getStringProperty( xmlNodePtr cur, const char *name, std::string &val ) { char *prop = (char*)xmlGetProp( cur, (const xmlChar *)name ); bool good=true; if(!prop) return false; val = prop; xmlFree( prop ); return good; } // Config *config; // std::string path; }