osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/XmlUtils.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00020 #include <osgEarth/XmlUtils>
00021 #include <osgEarth/StringUtils>
00022 #include <osgEarth/HTTPClient>
00023 #include <osg/Notify>
00024 #include "tinyxml.h"
00025 #include <algorithm>
00026 #include <sstream>
00027 #include <iomanip>
00028 
00029 
00030 using namespace osgEarth;
00031 
00032 
00033 static std::string EMPTY_VALUE = "";
00034 
00035 //std::string
00036 //osgEarth::trim( const std::string& in )
00037 //{
00038 //    std::string whitespace (" \t\f\v\n\r");
00039 //    // by Rodrigo C F Dias
00040 //    // http://www.codeproject.com/KB/stl/stdstringtrim.aspx
00041 //    std::string str = in;
00042 //    std::string::size_type pos = str.find_last_not_of( whitespace );
00043 //    if(pos != std::string::npos) {
00044 //        str.erase(pos + 1);
00045 //        pos = str.find_first_not_of( whitespace );
00046 //        if(pos != std::string::npos) str.erase(0, pos);
00047 //    }
00048 //    else str.erase(str.begin(), str.end());
00049 //    return str;
00050 //}
00051 
00052 
00053 XmlNode::XmlNode()
00054 {
00055     //NOP
00056 }
00057 
00058 XmlElement::XmlElement( const std::string& _name )
00059 {
00060     name = _name;
00061 }
00062 
00063 XmlElement::XmlElement( const std::string& _name, const XmlAttributes& _attrs )
00064 {
00065     name = _name;
00066     attrs = _attrs;
00067 }
00068 
00069 XmlElement::XmlElement( const Config& conf )
00070 {
00071     name = conf.key();
00072     for( Properties::const_iterator i = conf.attrs().begin(); i != conf.attrs().end(); i++ )
00073         attrs[i->first] = i->second;
00074     for( ConfigSet::const_iterator j = conf.children().begin(); j != conf.children().end(); j++ )
00075     {
00076         if (!j->children().empty())
00077         {
00078             children.push_back( new XmlElement( *j ) );
00079         }
00080         else
00081         {
00082             addSubElement(j->key(), j->attrs(), j->value());
00083         }
00084     }
00085 }
00086 
00087 const std::string&
00088 XmlElement::getName() const
00089 {
00090     return name;
00091 }
00092 
00093 void
00094 XmlElement::setName( const std::string& name)
00095 {
00096     this->name = name;
00097 }
00098 
00099 XmlAttributes&
00100 XmlElement::getAttrs()
00101 {
00102     return attrs;
00103 }
00104 
00105 const XmlAttributes&
00106 XmlElement::getAttrs() const
00107 {
00108     return attrs;
00109 }
00110 
00111 const std::string&
00112 XmlElement::getAttr( const std::string& key ) const
00113 {
00114     XmlAttributes::const_iterator i = attrs.find( key );
00115     return i != attrs.end()? i->second : EMPTY_VALUE;
00116 }
00117 
00118 XmlNodeList&
00119 XmlElement::getChildren()
00120 {
00121     return children;
00122 }
00123 
00124 const XmlNodeList&
00125 XmlElement::getChildren() const
00126 {
00127     return children;
00128 }
00129         
00130 XmlElement*
00131 XmlElement::getSubElement( const std::string& name ) const
00132 {
00133     std::string name_lower = name;
00134     std::transform( name_lower.begin(), name_lower.end(), name_lower.begin(), tolower );
00135 
00136     for( XmlNodeList::const_iterator i = getChildren().begin(); i != getChildren().end(); i++ )
00137     {
00138         if ( i->get()->isElement() )
00139         {
00140             XmlElement* e = (XmlElement*)i->get();
00141             std::string name = e->getName();
00142             std::transform( name.begin(), name.end(), name.begin(), tolower );
00143             if ( name == name_lower )
00144                 return e;
00145         }
00146     }
00147     return NULL;
00148 }
00149 
00150 
00151 std::string
00152 XmlElement::getText() const
00153 {
00154     std::stringstream builder;
00155 
00156     for( XmlNodeList::const_iterator i = getChildren().begin(); i != getChildren().end(); i++ )
00157     {
00158         if ( i->get()->isText() )
00159         {
00160             builder << ( static_cast<XmlText*>( i->get() ) )->getValue();
00161         }
00162     }
00163 
00164         std::string builderStr;
00165         builderStr = builder.str();
00166     std::string result = trim( builderStr );
00167     return result;
00168 }
00169 
00170 
00171 std::string
00172 XmlElement::getSubElementText( const std::string& name ) const
00173 {
00174     XmlElement* e = getSubElement( name );
00175     return e? e->getText() : EMPTY_VALUE;
00176 }
00177 
00178 
00179 XmlNodeList 
00180 XmlElement::getSubElements( const std::string& name ) const
00181 {
00182     XmlNodeList results;
00183 
00184     std::string name_lower = name;
00185     std::transform( name_lower.begin(), name_lower.end(), name_lower.begin(), tolower );
00186 
00187     for( XmlNodeList::const_iterator i = getChildren().begin(); i != getChildren().end(); i++ )
00188     {
00189         if ( i->get()->isElement() )
00190         {
00191             XmlElement* e = (XmlElement*)i->get();
00192             std::string name = e->getName();
00193             std::transform( name.begin(), name.end(), name.begin(), tolower );
00194             if ( name == name_lower )
00195                 results.push_back( e );
00196         }
00197     }
00198 
00199     return results;
00200 }
00201 
00202 void
00203 XmlElement::addSubElement(const std::string& tag, const std::string& text)
00204 {
00205     XmlElement* ele = new XmlElement(tag);
00206     ele->getChildren().push_back(new XmlText(text));
00207     children.push_back(ele);
00208 }
00209 
00210 void
00211 XmlElement::addSubElement(const std::string& tag, const Properties& attrs, const std::string& text)
00212 {
00213     XmlElement* ele = new XmlElement(tag);
00214     for( Properties::const_iterator i = attrs.begin(); i != attrs.end(); i++ )
00215         ele->attrs[i->first] = i->second;
00216     ele->getChildren().push_back(new XmlText(text));
00217     children.push_back(ele);
00218 }
00219 
00220 Config
00221 XmlElement::getConfig() const
00222 {
00223     Config conf( name );
00224     for( XmlAttributes::const_iterator a = attrs.begin(); a != attrs.end(); a++ )
00225         conf.attr( a->first ) = a->second;
00226     for( XmlNodeList::const_iterator c = children.begin(); c != children.end(); c++ )
00227     {
00228         XmlNode* n = c->get();
00229         if ( n->isElement() )
00230             conf.add( static_cast<const XmlElement*>(n)->getConfig() );
00231     }
00232 
00233     conf.value() = getText();
00234         //else 
00235         //    conf.value() = trim( static_cast<const XmlText*>(n)->getValue() );
00236     return conf;
00237 }
00238 
00239 XmlText::XmlText( const std::string& _value )
00240 {
00241     value = _value;
00242 }
00243 
00244 const std::string&
00245 XmlText::getValue() const
00246 {
00247     return value;
00248 }
00249 
00250 
00251 #if 0
00252 XmlDocument::XmlDocument( const std::string& _source_uri ) :
00253 XmlElement( "Document" ),
00254 source_uri( _source_uri )
00255 {
00256     //NOP
00257 }
00258 #endif
00259 
00260 XmlDocument::XmlDocument() :
00261 XmlElement( "Document" )
00262 {
00263     //nop
00264 }
00265 
00266 XmlDocument::XmlDocument( const Config& conf ) :
00267 XmlElement( conf )
00268 {
00269     //NOP
00270 }
00271 
00272 XmlDocument::~XmlDocument()
00273 {
00274     //NOP
00275 }
00276 
00277 namespace
00278 {
00279     XmlAttributes
00280     getAttributes( const char** attrs )
00281     {
00282         XmlAttributes map;
00283         const char** ptr = attrs;
00284         while( *ptr != NULL )
00285         {
00286             std::string name = *ptr++;
00287             std::string value = *ptr++;
00288             std::transform( name.begin(), name.end(), name.begin(), tolower );
00289             map[name] = value;
00290         }
00291         return map;
00292     }
00293 
00294     void processNode(XmlElement* parent, TiXmlNode* node)
00295     {
00296         XmlElement* new_element = 0;
00297         switch (node->Type())
00298         {
00299         case TiXmlNode::TINYXML_ELEMENT:
00300             {
00301                 TiXmlElement* element = node->ToElement();
00302                 std::string tag = element->Value();
00303                 std::transform( tag.begin(), tag.end(), tag.begin(), tolower);
00304 
00305                 //Get all the attributes
00306                 XmlAttributes attrs;
00307                 TiXmlAttribute* attr = element->FirstAttribute();
00308                 while (attr)
00309                 {
00310                     std::string name  = attr->Name();
00311                     std::string value = attr->Value();
00312                     std::transform( name.begin(), name.end(), name.begin(), tolower);
00313                     attrs[name] = value;
00314                     attr = attr->Next();
00315                 }
00316 
00317                 //All the element to the stack
00318                 new_element = new XmlElement( tag, attrs );
00319                 parent->getChildren().push_back( new_element );
00320             }
00321             break;
00322         case TiXmlNode::TINYXML_TEXT:
00323             {
00324                 TiXmlText* text = node->ToText();
00325                 std::string data( text->Value());
00326                 parent->getChildren().push_back( new XmlText( data ) );
00327             }
00328             break;
00329         }    
00330 
00331         XmlElement* new_parent = new_element ? new_element : parent;
00332         TiXmlNode* child;
00333         for (child = node->FirstChild(); child != 0; child = child->NextSibling())
00334         {    
00335             processNode( new_parent, child );
00336         }
00337     }
00338 
00339     void
00340     removeDocType(std::string &xmlStr)
00341     {
00342         //TinyXML has an issue with parsing DTDs.  See http://www.grinninglizard.com/tinyxmldocs/index.html
00343         //We need to remove any !DOCTYPE block that appears in the XML before parsing to avoid errors.
00344         std::string::size_type startIndex = xmlStr.find("<!DOCTYPE");
00345         if (startIndex == xmlStr.npos) return;
00346 
00347         std::string::size_type endIndex = startIndex;
00348         int numChildElements = 0;
00349         //We've found the first index of the <!DOCTYPE, now find the index of the closing >
00350         while (endIndex < xmlStr.size())
00351         {
00352             endIndex+=1;
00353             if (xmlStr[endIndex] == '<')
00354             {
00355                 numChildElements++;
00356             }
00357             else if (xmlStr[endIndex] == '>')
00358             {
00359                 if (numChildElements == 0)
00360                 {
00361                     break;
00362                 }
00363                 else
00364                 {
00365                     numChildElements--;
00366                 }
00367             }
00368         }
00369 
00370         //Now, replace the <!DOCTYPE> element with whitespace
00371         xmlStr.erase(startIndex, endIndex - startIndex + 1);
00372     }
00373 }
00374 
00375 
00376 XmlDocument*
00377 XmlDocument::load( const std::string& location )
00378 {
00379     return load( URI(location) );
00380 }
00381 
00382 XmlDocument*
00383 XmlDocument::load( const URI& uri )
00384 {
00385     std::string buffer;
00386     if ( HTTPClient::readString( uri.full(), buffer ) != HTTPClient::RESULT_OK )
00387     {
00388         return 0L;
00389     }
00390 
00391     std::stringstream buf(buffer);
00392     XmlDocument* result = load(buf);
00393 
00394     if ( result )
00395         result->_sourceURI = uri;
00396 
00397     return result;
00398 }
00399 
00400 
00401 XmlDocument*
00402 XmlDocument::load( std::istream& in, const URIContext& uriContext )
00403 {
00404     TiXmlDocument xmlDoc;
00405 
00406     //Read the entire document into a string
00407     std::stringstream buffer;
00408     buffer << in.rdbuf();
00409     std::string xmlStr(buffer.str()); 
00410 
00411     removeDocType( xmlStr );
00412     //OE_NOTICE << xmlStr;
00413 
00414     XmlDocument* doc = NULL;
00415     xmlDoc.Parse(xmlStr.c_str());    
00416 
00417     if ( xmlDoc.Error() )
00418     {
00419         std::stringstream buf;
00420         buf << xmlDoc.ErrorDesc() << " (row " << xmlDoc.ErrorRow() << ", col " << xmlDoc.ErrorCol() << ")";
00421         std::string str = buf.str();
00422         OE_WARN << "Error in XML document: " << str << std::endl;
00423         if ( !uriContext.referrer().empty() )
00424             OE_WARN << uriContext.referrer() << std::endl;
00425     }
00426 
00427     if ( !xmlDoc.Error() && xmlDoc.RootElement() )
00428     {
00429         doc = new XmlDocument();
00430         processNode( doc,  xmlDoc.RootElement() );
00431         doc->_sourceURI = URI("", uriContext);
00432     }
00433     return doc;    
00434 }
00435 
00436 Config
00437 XmlDocument::getConfig() const
00438 {
00439     Config conf = XmlElement::getConfig();
00440     conf.setURIContext( _sourceURI.full() );
00441     return conf;
00442 }
00443 
00444 #define INDENT 4
00445 
00446 static void
00447 storeNode( const XmlNode* node, int depth, std::ostream& out )
00448 {
00449     out << std::fixed; // always use fixed notation
00450 
00451     for( int k=0; k<depth*INDENT; k++ ) out << " ";
00452 
00453     if ( node->isElement() )
00454     {
00455         XmlElement* e = (XmlElement*)node;
00456         out << "<" << e->getName();
00457         for( XmlAttributes::iterator a = e->getAttrs().begin(); a != e->getAttrs().end(); a++ )
00458         {
00459             out << " " << a->first << "=" << "\"" << a->second << "\"";
00460         }
00461         out << ">" << std::endl;
00462         for( XmlNodeList::iterator i = e->getChildren().begin(); i != e->getChildren().end(); i++ )
00463         {
00464             storeNode( i->get(), depth+1, out );
00465         }
00466         for( int k=0; k<depth*INDENT; k++ ) out << " ";
00467         out << "</" << e->getName() << ">" << std::endl;
00468     }
00469     else if ( node->isText() )
00470     {
00471         XmlText* t = (XmlText*)node;
00472         const std::string& text = t->getValue();
00473         if ( text.find_first_of( "<&" ) != std::string::npos )
00474             out << "<![CDATA[" << text << "]]>" << std::endl;
00475         else
00476             out << text << std::endl;
00477     }
00478 }
00479 
00480 void
00481 XmlDocument::store( std::ostream& out ) const
00482 {
00483     out << "<?xml version=\"1.0\"?>" << std::endl;
00484     storeNode( this, 0, out);
00485     /*for( XmlNodeList::const_iterator i = getChildren().begin(); i != getChildren().end(); i++ )
00486     {
00487         storeNode( i->get(), 0, out );
00488     }*/
00489 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines