Created
April 22, 2009 13:27
-
-
Save moriyoshi/99792 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
#include <typeinfo> | |
#include <utility> | |
#include <boost/shared_ptr.hpp> | |
#include <boost/variant.hpp> | |
#include <boost/foreach.hpp> | |
#include <boost/assert.hpp> | |
namespace xml_builder { | |
namespace detail { | |
namespace tokens { | |
struct start_tag; | |
struct end_tag; | |
struct cdata; | |
} // namespace tokens | |
typedef boost::variant< | |
tokens::start_tag, | |
tokens::end_tag, | |
tokens::cdata> token; | |
typedef std::vector<token> token_vector; | |
typedef boost::shared_ptr<token_vector> token_vector_handle; | |
} // namespace detail | |
struct xml_string | |
{ | |
xml_string(char const* _data): data(_data) {} | |
char const *data; | |
}; | |
struct xml | |
{ | |
xml(detail::token_vector_handle hdl): hdl_(hdl) {} | |
template<typename T1_, typename T2_> | |
void dump(std::basic_ostream<T1_, T2_>&) const; | |
private: | |
detail::token_vector_handle hdl_; | |
}; | |
namespace detail { | |
namespace tokens { | |
struct start_tag | |
{ | |
typedef std::vector<std::pair<char const*, char const*> > attr_list_type; | |
start_tag(): name_(0), attr_list_() {} | |
start_tag(char const* name): name_(name) {} | |
char const* name() const | |
{ | |
return name_; | |
} | |
attr_list_type& attributes() | |
{ | |
if (!attr_list_) | |
attr_list_.reset(new attr_list_type()); | |
return *attr_list_; | |
} | |
attr_list_type const& attributes() const | |
{ | |
return const_cast<start_tag*>(this)->attributes(); | |
} | |
private: | |
char const* name_; | |
boost::shared_ptr<attr_list_type> attr_list_; | |
}; | |
struct end_tag | |
{ | |
end_tag(): name_(0) {} | |
end_tag(char const* name): name_(name) {} | |
char const* name() const | |
{ | |
return name_; | |
} | |
private: | |
char const* name_; | |
}; | |
struct cdata | |
{ | |
cdata(): data_(0) {} | |
cdata(char const *data): data_(data) {} | |
char const* data() const | |
{ | |
return data_; | |
} | |
private: | |
char const *data_; | |
}; | |
} // namespace tokens | |
struct expect_attr_name_or_close; | |
struct empty {}; | |
struct expect_open | |
{ | |
expect_open(token_vector_handle const& hdl): hdl_(hdl) {} | |
token_vector_handle handle() const | |
{ | |
return hdl_; | |
} | |
operator xml() { return xml(hdl_); } | |
private: | |
token_vector_handle hdl_; | |
}; | |
struct expect_close | |
{ | |
expect_close(token_vector_handle const& hdl): hdl_(hdl) {} | |
token_vector_handle handle() const | |
{ | |
return hdl_; | |
} | |
private: | |
const token_vector_handle hdl_; | |
}; | |
struct expect_attr_name_or_close | |
{ | |
expect_attr_name_or_close(token_vector_handle const& hdl): hdl_(hdl) {} | |
token_vector_handle handle() const | |
{ | |
return hdl_; | |
} | |
private: | |
const token_vector_handle hdl_; | |
}; | |
struct expect_attr_value | |
{ | |
expect_attr_value(token_vector_handle const& hdl): hdl_(hdl) {} | |
token_vector_handle handle() const | |
{ | |
return hdl_; | |
} | |
private: | |
const token_vector_handle hdl_; | |
}; | |
struct attribute_pair | |
{ | |
attribute_pair(char const* name): name_(name), value_(0) {} | |
attribute_pair& operator=(char const* v) | |
{ | |
value_ = v; | |
return *this; | |
} | |
char const* name() const | |
{ | |
return name_; | |
} | |
char const* value() const | |
{ | |
return value_; | |
} | |
private: | |
char const* name_; | |
char const* value_; | |
}; | |
template<typename Tstrm_> | |
struct token_printer: public boost::static_visitor<> | |
{ | |
token_printer(Tstrm_& out): out_(out) {} | |
void operator()(tokens::start_tag const& tag) const | |
{ | |
out_ << "<" << tag.name(); | |
BOOST_FOREACH (typename tokens::start_tag::attr_list_type::value_type i, | |
tag.attributes() ) | |
{ | |
out_ << " " << i.first << "=\"" << i.second << "\""; | |
} | |
out_ << ">" << std::endl; | |
} | |
void operator()(tokens::end_tag const& tag) const | |
{ | |
out_ << "</" << tag.name() << ">" << std::endl; | |
} | |
void operator()(tokens::cdata const& cdata) const | |
{ | |
out_ << cdata.data() << std::endl; | |
} | |
private: | |
Tstrm_& out_; | |
}; | |
template<typename T1_, typename T2_> | |
inline token_printer<std::basic_ostream<T1_, T2_> > | |
make_token_printer(std::basic_ostream<T1_, T2_>& out) | |
{ | |
return token_printer<std::basic_ostream<T1_, T2_> >(out); | |
} | |
} // namespace detail | |
inline detail::expect_open operator>( | |
detail::expect_attr_name_or_close const&lhs, xml_string const& text) | |
{ | |
detail::token_vector_handle hdl = lhs.handle(); | |
hdl->push_back(detail::tokens::cdata(text.data)); | |
return detail::expect_open(hdl); | |
} | |
inline detail::expect_open operator>=( | |
detail::expect_attr_name_or_close const& lhs, xml_string const &text) | |
{ | |
detail::token_vector_handle hdl = lhs.handle(); | |
hdl->push_back(detail::tokens::cdata(text.data)); | |
return detail::expect_open(hdl); | |
} | |
inline detail::expect_open operator>( | |
detail::expect_attr_name_or_close const& lhs, detail::empty const&) | |
{ | |
return detail::expect_open(lhs.handle()); | |
} | |
inline detail::expect_open operator>=( | |
detail::expect_attr_name_or_close const& lhs, detail::empty const&) | |
{ | |
return detail::expect_open(lhs.handle()); | |
} | |
inline detail::expect_open operator>( | |
detail::expect_close const& lhs, xml_string const& text) | |
{ | |
detail::token_vector_handle hdl = lhs.handle(); | |
hdl->push_back(detail::tokens::cdata(text.data)); | |
return detail::expect_open(hdl); | |
} | |
inline detail::expect_open operator>( | |
detail::expect_close const& lhs, detail::empty const&) | |
{ | |
return detail::expect_open(lhs.handle()); | |
} | |
inline detail::expect_close operator<=( | |
detail::expect_open const& lhs, xml_string const& tagname) | |
{ | |
detail::token_vector_handle hdl = lhs.handle(); | |
hdl->push_back(detail::tokens::end_tag(tagname.data)); | |
return detail::expect_close(hdl); | |
} | |
inline detail::expect_attr_name_or_close operator<( | |
detail::empty const&, xml_string const& tagname) | |
{ | |
detail::token_vector_handle hdl(new detail::token_vector()); | |
hdl->push_back(detail::tokens::start_tag(tagname.data)); | |
return detail::expect_attr_name_or_close(hdl); | |
} | |
inline detail::expect_attr_name_or_close operator<( | |
detail::expect_open const& lhs, xml_string const& tagname) | |
{ | |
detail::token_vector_handle hdl = lhs.handle(); | |
hdl->push_back(detail::tokens::start_tag(tagname.data)); | |
return detail::expect_attr_name_or_close(hdl); | |
} | |
inline detail::expect_attr_name_or_close operator>=(detail::expect_attr_name_or_close const& lhs, detail::attribute_pair const& pair) | |
{ | |
detail::token_vector_handle hdl = lhs.handle(); | |
BOOST_ASSERT(hdl->back().which() == 0); | |
boost::get<detail::tokens::start_tag&>(hdl->back()).attributes().push_back( | |
std::make_pair(pair.name(), pair.value())); | |
return detail::expect_attr_name_or_close(hdl); | |
} | |
template<typename T1_, typename T2_> | |
inline void xml::dump(std::basic_ostream<T1_, T2_>& out) const | |
{ | |
for (typename detail::token_vector::const_iterator i(hdl_->begin()), e(hdl_->end()); | |
i != e; ++i) { | |
boost::apply_visitor(detail::make_token_printer(out), *i); | |
} | |
} | |
static const detail::empty $ = detail::empty(); | |
} | |
#define $$(kv) >=((detail::attribute_pair)kv) | |
int main(int, char **) | |
{ | |
using namespace xml_builder; | |
xml($ | |
<"html">$ | |
<"head">$ | |
<"meta" $$("http-equiv"="Content-Type") | |
$$("content"="text/html") >=$ | |
<="head">$ | |
<"body">$ | |
<"h1">"Hello, World!"<="h1"> | |
"test" | |
<="body">$ | |
<="html">$ | |
).dump(std::cout); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment