Add new XML data structure and modify XML parser to work with this structure.
This commit is contained in:
parent
ad86e9aebc
commit
481271cb76
4 changed files with 209 additions and 57 deletions
78
include/mapnik/xml_tree.hpp
Normal file
78
include/mapnik/xml_tree.hpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2012 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MAPNIK_XML_TREE_H
|
||||
#define MAPNIK_XML_TREE_H
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
class xml_tree;
|
||||
|
||||
class xml_attribute
|
||||
{
|
||||
public:
|
||||
std::string value;
|
||||
bool processed;
|
||||
};
|
||||
|
||||
class xml_node
|
||||
{
|
||||
public:
|
||||
xml_node(xml_tree &tree, std::string name, unsigned line=0, bool text_node = false);
|
||||
|
||||
std::string name() const;
|
||||
std::string text() const;
|
||||
|
||||
xml_node &add_child(std::string name, unsigned line=0, bool text_node = false);
|
||||
void add_attribute(std::string name, std::string value);
|
||||
void set_processed(bool processed);
|
||||
private:
|
||||
xml_tree &tree_;
|
||||
std::string name_;
|
||||
std::list<xml_node> children_;
|
||||
std::map<std::string, xml_attribute> attributes_;
|
||||
bool text_node_;
|
||||
unsigned line_;
|
||||
bool processed_;
|
||||
|
||||
};
|
||||
|
||||
class xml_tree
|
||||
{
|
||||
public:
|
||||
xml_tree();
|
||||
void set_filename(std::string fn);
|
||||
std::string filename() const;
|
||||
xml_node &node();
|
||||
private:
|
||||
xml_node node_;
|
||||
std::string file_;
|
||||
//TODO: Grammars
|
||||
};
|
||||
|
||||
} //ns mapnik
|
||||
|
||||
#endif // MAPNIK_XML_TREE_H
|
|
@ -178,6 +178,7 @@ source = Split(
|
|||
text_placements/list.cpp
|
||||
text_placements/simple.cpp
|
||||
text_properties.cpp
|
||||
xml_tree.cpp
|
||||
"""
|
||||
)
|
||||
|
||||
|
|
|
@ -22,26 +22,27 @@
|
|||
|
||||
#ifdef HAVE_LIBXML2
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/libxml2_loader.hpp>
|
||||
|
||||
#include <mapnik/xml_tree.hpp>
|
||||
#include <mapnik/config_error.hpp>
|
||||
|
||||
// boost
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
|
||||
// libxml
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/parserInternals.h>
|
||||
#include <libxml/xinclude.h>
|
||||
|
||||
// stl
|
||||
#include <iostream>
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
using namespace std;
|
||||
|
||||
//#define DEFAULT_OPTIONS (XML_PARSE_NOENT | XML_PARSE_NOBLANKS | XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA)
|
||||
#define DEFAULT_OPTIONS (XML_PARSE_NOERROR | XML_PARSE_NOENT | XML_PARSE_NOBLANKS | XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA)
|
||||
|
||||
namespace mapnik
|
||||
|
@ -50,14 +51,14 @@ class libxml2_loader : boost::noncopyable
|
|||
{
|
||||
public:
|
||||
libxml2_loader(const char *encoding = NULL, int options = DEFAULT_OPTIONS, const char *url = NULL) :
|
||||
ctx_( 0 ),
|
||||
encoding_( encoding ),
|
||||
options_( options ),
|
||||
url_( url )
|
||||
ctx_(0),
|
||||
encoding_(encoding),
|
||||
options_(options),
|
||||
url_(url)
|
||||
{
|
||||
LIBXML_TEST_VERSION;
|
||||
ctx_ = xmlNewParserCtxt();
|
||||
if ( ! ctx_ )
|
||||
if (!ctx_)
|
||||
{
|
||||
throw std::runtime_error("Failed to create parser context.");
|
||||
}
|
||||
|
@ -71,19 +72,20 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void load( const std::string & filename, ptree & pt )
|
||||
void load(const std::string & filename, xml_node &node)
|
||||
{
|
||||
boost::filesystem::path path(filename);
|
||||
if ( !boost::filesystem::exists( path ) ) {
|
||||
if (!boost::filesystem::exists(path))
|
||||
{
|
||||
throw config_error(string("Could not load map file '") +
|
||||
filename + "': File does not exist");
|
||||
}
|
||||
|
||||
xmlDocPtr doc = xmlCtxtReadFile(ctx_, filename.c_str(), encoding_, options_);
|
||||
|
||||
if ( !doc )
|
||||
if (!doc)
|
||||
{
|
||||
xmlError * error = xmlCtxtGetLastError( ctx_ );
|
||||
xmlError * error = xmlCtxtGetLastError(ctx_);
|
||||
if (error)
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
@ -91,13 +93,13 @@ public:
|
|||
os << ": " << std::endl << error->message;
|
||||
// remove CR
|
||||
std::string msg = os.str().substr(0, os.str().size() - 1);
|
||||
config_error ex( msg );
|
||||
config_error ex(msg);
|
||||
|
||||
os.str("");
|
||||
os << "(encountered in file '" << error->file << "' at line "
|
||||
<< error->line << ")";
|
||||
|
||||
ex.append_context( os.str() );
|
||||
ex.append_context(os.str());
|
||||
|
||||
throw ex;
|
||||
}
|
||||
|
@ -110,21 +112,21 @@ public:
|
|||
<< std::endl;
|
||||
}
|
||||
*/
|
||||
load(doc, pt);
|
||||
load(doc, node);
|
||||
}
|
||||
|
||||
void load( const int fd, ptree & pt )
|
||||
void load(const int fd, xml_node &node)
|
||||
{
|
||||
xmlDocPtr doc = xmlCtxtReadFd(ctx_, fd, url_, encoding_, options_);
|
||||
load(doc, pt);
|
||||
load(doc, node);
|
||||
}
|
||||
|
||||
void load_string( const std::string & buffer, ptree & pt, std::string const & base_path )
|
||||
void load_string(const std::string & buffer, xml_node &node, std::string const & base_path )
|
||||
{
|
||||
if (!base_path.empty())
|
||||
{
|
||||
boost::filesystem::path path(base_path);
|
||||
if ( ! boost::filesystem::exists( path ) ) {
|
||||
if (!boost::filesystem::exists(path)) {
|
||||
throw config_error(string("Could not locate base_path '") +
|
||||
base_path + "': file or directory does not exist");
|
||||
}
|
||||
|
@ -132,12 +134,12 @@ public:
|
|||
|
||||
xmlDocPtr doc = xmlCtxtReadMemory(ctx_, buffer.data(), buffer.length(), base_path.c_str(), encoding_, options_);
|
||||
|
||||
load(doc, pt);
|
||||
load(doc, node);
|
||||
}
|
||||
|
||||
void load( const xmlDocPtr doc, ptree & pt )
|
||||
void load(const xmlDocPtr doc, xml_node &node)
|
||||
{
|
||||
if ( !doc )
|
||||
if (!doc)
|
||||
{
|
||||
xmlError * error = xmlCtxtGetLastError( ctx_ );
|
||||
std::ostringstream os;
|
||||
|
@ -149,7 +151,7 @@ public:
|
|||
throw config_error(os.str());
|
||||
}
|
||||
|
||||
int iXIncludeReturn = xmlXIncludeProcessFlags( doc, options_ );
|
||||
int iXIncludeReturn = xmlXIncludeProcessFlags(doc, options_);
|
||||
|
||||
if (iXIncludeReturn < 0)
|
||||
{
|
||||
|
@ -157,63 +159,47 @@ public:
|
|||
throw config_error("XML XInclude error. One or more files failed to load.");
|
||||
}
|
||||
|
||||
xmlNode * root = xmlDocGetRootElement( doc );
|
||||
if ( ! root ) {
|
||||
xmlNode * root = xmlDocGetRootElement(doc);
|
||||
if (!root) {
|
||||
xmlFreeDoc(doc);
|
||||
throw config_error("XML document is empty.");
|
||||
}
|
||||
|
||||
populate_tree( root, pt );
|
||||
populate_tree(root, node);
|
||||
xmlFreeDoc(doc);
|
||||
}
|
||||
|
||||
private:
|
||||
void append_attributes( xmlAttr * attributes, ptree & pt)
|
||||
void append_attributes(xmlAttr *attributes, xml_node &node)
|
||||
{
|
||||
if (attributes)
|
||||
for (; attributes; attributes = attributes->next )
|
||||
{
|
||||
ptree::iterator it = pt.push_back( ptree::value_type( "<xmlattr>", ptree() ));
|
||||
ptree & attr_list = it->second;
|
||||
xmlAttr * cur_attr = attributes;
|
||||
for (; cur_attr; cur_attr = cur_attr->next )
|
||||
{
|
||||
ptree::iterator it = attr_list.push_back(
|
||||
ptree::value_type( (char*)cur_attr->name, ptree() ));
|
||||
it->second.put_value( (char*) cur_attr->children->content );
|
||||
}
|
||||
node.add_attribute((char *)attributes->name, (char *)attributes->children->content);
|
||||
}
|
||||
}
|
||||
|
||||
void populate_tree( xmlNode * node, ptree & pt )
|
||||
void populate_tree(xmlNode *cur_node, xml_node &node)
|
||||
{
|
||||
xmlNode * cur_node = node;
|
||||
|
||||
for (; cur_node; cur_node = cur_node->next )
|
||||
{
|
||||
switch (cur_node->type)
|
||||
{
|
||||
case XML_ELEMENT_NODE:
|
||||
{
|
||||
ptree::iterator it = pt.push_back( ptree::value_type(
|
||||
(char*)cur_node->name, ptree() ));
|
||||
append_attributes( cur_node->properties, it->second);
|
||||
populate_tree( cur_node->children, it->second );
|
||||
|
||||
xml_node &new_node = node.add_child((char *)cur_node->name, cur_node->line, false);
|
||||
append_attributes(cur_node->properties, new_node);
|
||||
populate_tree(cur_node->children, new_node);
|
||||
}
|
||||
break;
|
||||
case XML_TEXT_NODE:
|
||||
{
|
||||
std::string trimmed = boost::algorithm::trim_copy(std::string((char*)cur_node->content));
|
||||
if (trimmed.empty()) break;
|
||||
ptree::iterator it = pt.push_back(ptree::value_type("<xmltext>", ptree()));
|
||||
it->second.put_value(trimmed);
|
||||
if (trimmed.empty()) break; //Don't add empty text nodes
|
||||
node.add_child(trimmed, cur_node->line, true);
|
||||
}
|
||||
break;
|
||||
case XML_COMMENT_NODE:
|
||||
{
|
||||
ptree::iterator it = pt.push_back(
|
||||
ptree::value_type( "<xmlcomment>", ptree() ));
|
||||
it->second.put_value( (char*) cur_node->content );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -228,15 +214,15 @@ private:
|
|||
const char *url_;
|
||||
};
|
||||
|
||||
void read_xml2( std::string const & filename, boost::property_tree::ptree & pt)
|
||||
void read_xml2(std::string const & filename, xml_node &node)
|
||||
{
|
||||
libxml2_loader loader;
|
||||
loader.load( filename, pt );
|
||||
loader.load(filename, node);
|
||||
}
|
||||
void read_xml2_string( std::string const & str, boost::property_tree::ptree & pt, std::string const & base_path)
|
||||
void read_xml2_string(std::string const & str, xml_node &node, std::string const & base_path)
|
||||
{
|
||||
libxml2_loader loader;
|
||||
loader.load_string( str, pt, base_path );
|
||||
loader.load_string(str, node, base_path);
|
||||
}
|
||||
|
||||
} // end of namespace mapnik
|
||||
|
|
87
src/xml_tree.cpp
Normal file
87
src/xml_tree.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2012 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
#include <mapnik/xml_tree.hpp>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
xml_tree::xml_tree()
|
||||
: node_(*this, "<root>")
|
||||
{
|
||||
}
|
||||
|
||||
void xml_tree::set_filename(std::string fn)
|
||||
{
|
||||
file_ = fn;
|
||||
}
|
||||
|
||||
std::string xml_tree::filename() const
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
xml_node &xml_tree::node()
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
xml_node::xml_node(xml_tree &tree, std::string name, unsigned line, bool text_node)
|
||||
: tree_(tree),
|
||||
name_(name),
|
||||
text_node_(text_node),
|
||||
line_(line),
|
||||
processed_(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string xml_node::name() const
|
||||
{
|
||||
if (!text_node_)
|
||||
return name_;
|
||||
else
|
||||
return "<xmltext>";
|
||||
}
|
||||
|
||||
std::string xml_node::text() const
|
||||
{
|
||||
if (text_node_)
|
||||
return name_;
|
||||
else
|
||||
return "NOT A TEXT NODE";
|
||||
}
|
||||
|
||||
void xml_node::set_processed(bool processed)
|
||||
{
|
||||
processed_ = processed;
|
||||
}
|
||||
|
||||
xml_node &xml_node::add_child(std::string name, unsigned line, bool text_node)
|
||||
{
|
||||
children_.push_back(xml_node(tree_, name, line, text_node));
|
||||
return children_.back();
|
||||
}
|
||||
|
||||
|
||||
} //ns mapnik
|
Loading…
Reference in a new issue