osgEarth 2.1.1
|
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 }