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/list.cpp
|
||||||
text_placements/simple.cpp
|
text_placements/simple.cpp
|
||||||
text_properties.cpp
|
text_properties.cpp
|
||||||
|
xml_tree.cpp
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,26 +22,27 @@
|
||||||
|
|
||||||
#ifdef HAVE_LIBXML2
|
#ifdef HAVE_LIBXML2
|
||||||
|
|
||||||
|
// mapnik
|
||||||
#include <mapnik/libxml2_loader.hpp>
|
#include <mapnik/libxml2_loader.hpp>
|
||||||
|
#include <mapnik/xml_tree.hpp>
|
||||||
#include <mapnik/config_error.hpp>
|
#include <mapnik/config_error.hpp>
|
||||||
|
|
||||||
|
// boost
|
||||||
#include <boost/utility.hpp>
|
#include <boost/utility.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
|
|
||||||
|
// libxml
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
#include <libxml/parserInternals.h>
|
#include <libxml/parserInternals.h>
|
||||||
#include <libxml/xinclude.h>
|
#include <libxml/xinclude.h>
|
||||||
|
|
||||||
|
// stl
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using boost::property_tree::ptree;
|
|
||||||
using namespace std;
|
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)
|
#define DEFAULT_OPTIONS (XML_PARSE_NOERROR | XML_PARSE_NOENT | XML_PARSE_NOBLANKS | XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA)
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
|
@ -50,14 +51,14 @@ class libxml2_loader : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
libxml2_loader(const char *encoding = NULL, int options = DEFAULT_OPTIONS, const char *url = NULL) :
|
libxml2_loader(const char *encoding = NULL, int options = DEFAULT_OPTIONS, const char *url = NULL) :
|
||||||
ctx_( 0 ),
|
ctx_(0),
|
||||||
encoding_( encoding ),
|
encoding_(encoding),
|
||||||
options_( options ),
|
options_(options),
|
||||||
url_( url )
|
url_(url)
|
||||||
{
|
{
|
||||||
LIBXML_TEST_VERSION;
|
LIBXML_TEST_VERSION;
|
||||||
ctx_ = xmlNewParserCtxt();
|
ctx_ = xmlNewParserCtxt();
|
||||||
if ( ! ctx_ )
|
if (!ctx_)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to create parser context.");
|
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);
|
boost::filesystem::path path(filename);
|
||||||
if ( !boost::filesystem::exists( path ) ) {
|
if (!boost::filesystem::exists(path))
|
||||||
|
{
|
||||||
throw config_error(string("Could not load map file '") +
|
throw config_error(string("Could not load map file '") +
|
||||||
filename + "': File does not exist");
|
filename + "': File does not exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlDocPtr doc = xmlCtxtReadFile(ctx_, filename.c_str(), encoding_, options_);
|
xmlDocPtr doc = xmlCtxtReadFile(ctx_, filename.c_str(), encoding_, options_);
|
||||||
|
|
||||||
if ( !doc )
|
if (!doc)
|
||||||
{
|
{
|
||||||
xmlError * error = xmlCtxtGetLastError( ctx_ );
|
xmlError * error = xmlCtxtGetLastError(ctx_);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
@ -91,13 +93,13 @@ public:
|
||||||
os << ": " << std::endl << error->message;
|
os << ": " << std::endl << error->message;
|
||||||
// remove CR
|
// remove CR
|
||||||
std::string msg = os.str().substr(0, os.str().size() - 1);
|
std::string msg = os.str().substr(0, os.str().size() - 1);
|
||||||
config_error ex( msg );
|
config_error ex(msg);
|
||||||
|
|
||||||
os.str("");
|
os.str("");
|
||||||
os << "(encountered in file '" << error->file << "' at line "
|
os << "(encountered in file '" << error->file << "' at line "
|
||||||
<< error->line << ")";
|
<< error->line << ")";
|
||||||
|
|
||||||
ex.append_context( os.str() );
|
ex.append_context(os.str());
|
||||||
|
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
@ -110,21 +112,21 @@ public:
|
||||||
<< std::endl;
|
<< 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_);
|
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())
|
if (!base_path.empty())
|
||||||
{
|
{
|
||||||
boost::filesystem::path path(base_path);
|
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 '") +
|
throw config_error(string("Could not locate base_path '") +
|
||||||
base_path + "': file or directory does not exist");
|
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_);
|
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_ );
|
xmlError * error = xmlCtxtGetLastError( ctx_ );
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
@ -149,7 +151,7 @@ public:
|
||||||
throw config_error(os.str());
|
throw config_error(os.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
int iXIncludeReturn = xmlXIncludeProcessFlags( doc, options_ );
|
int iXIncludeReturn = xmlXIncludeProcessFlags(doc, options_);
|
||||||
|
|
||||||
if (iXIncludeReturn < 0)
|
if (iXIncludeReturn < 0)
|
||||||
{
|
{
|
||||||
|
@ -157,63 +159,47 @@ public:
|
||||||
throw config_error("XML XInclude error. One or more files failed to load.");
|
throw config_error("XML XInclude error. One or more files failed to load.");
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlNode * root = xmlDocGetRootElement( doc );
|
xmlNode * root = xmlDocGetRootElement(doc);
|
||||||
if ( ! root ) {
|
if (!root) {
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
throw config_error("XML document is empty.");
|
throw config_error("XML document is empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
populate_tree( root, pt );
|
populate_tree(root, node);
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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() ));
|
node.add_attribute((char *)attributes->name, (char *)attributes->children->content);
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 )
|
for (; cur_node; cur_node = cur_node->next )
|
||||||
{
|
{
|
||||||
switch (cur_node->type)
|
switch (cur_node->type)
|
||||||
{
|
{
|
||||||
case XML_ELEMENT_NODE:
|
case XML_ELEMENT_NODE:
|
||||||
{
|
{
|
||||||
ptree::iterator it = pt.push_back( ptree::value_type(
|
|
||||||
(char*)cur_node->name, ptree() ));
|
xml_node &new_node = node.add_child((char *)cur_node->name, cur_node->line, false);
|
||||||
append_attributes( cur_node->properties, it->second);
|
append_attributes(cur_node->properties, new_node);
|
||||||
populate_tree( cur_node->children, it->second );
|
populate_tree(cur_node->children, new_node);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XML_TEXT_NODE:
|
case XML_TEXT_NODE:
|
||||||
{
|
{
|
||||||
std::string trimmed = boost::algorithm::trim_copy(std::string((char*)cur_node->content));
|
std::string trimmed = boost::algorithm::trim_copy(std::string((char*)cur_node->content));
|
||||||
if (trimmed.empty()) break;
|
if (trimmed.empty()) break; //Don't add empty text nodes
|
||||||
ptree::iterator it = pt.push_back(ptree::value_type("<xmltext>", ptree()));
|
node.add_child(trimmed, cur_node->line, true);
|
||||||
it->second.put_value(trimmed);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XML_COMMENT_NODE:
|
case XML_COMMENT_NODE:
|
||||||
{
|
|
||||||
ptree::iterator it = pt.push_back(
|
|
||||||
ptree::value_type( "<xmlcomment>", ptree() ));
|
|
||||||
it->second.put_value( (char*) cur_node->content );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -228,15 +214,15 @@ private:
|
||||||
const char *url_;
|
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;
|
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;
|
libxml2_loader loader;
|
||||||
loader.load_string( str, pt, base_path );
|
loader.load_string(str, node, base_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end of namespace mapnik
|
} // 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