23 #ifndef _util_misc_xmlwriter_h
24 #define _util_misc_xmlwriter_h
26 #define BOOST_PARAMETER_MAX_ARITY 15
33 #include <boost/archive/iterators/base64_from_binary.hpp>
34 #include <boost/archive/iterators/insert_linebreaks.hpp>
35 #include <boost/archive/iterators/transform_width.hpp>
36 #include <boost/archive/iterators/ostream_iterator.hpp>
39 #include <boost/property_tree/ptree.hpp>
40 #include <boost/property_tree/xml_parser.hpp>
43 #include <boost/type_traits.hpp>
44 #include <boost/mpl/not.hpp>
45 #include <boost/mpl/or.hpp>
46 #include <boost/mpl/and.hpp>
49 #include <boost/parameter/name.hpp>
50 #include <boost/parameter/preprocessor.hpp>
51 #include <boost/version.hpp>
54 #include <Eigen/Dense>
56 #include <util/misc/runnable.h>
57 #include <util/misc/exenv.h>
58 #include <util/misc/consumableresources.h>
59 #include <util/keyval/keyval.h>
60 #include <util/misc/scexception.h>
61 #include <util/misc/xml.h>
63 #ifndef NO_USE_BOOST_ENDIAN
64 # include <boost/detail/endian.hpp>
65 # if defined(BOOST_LITTLE_ENDIAN)
66 # define IS_BIG_ENDIAN false
67 # elif defined(BOOST_BIG_ENDIAN)
68 # define IS_BIG_ENDIAN true
70 # include <arpa/inet.h>
71 # define IS_BIG_ENDIAN htonl(47) == 47
74 # include <arpa/inet.h>
75 # define IS_BIG_ENDIAN htonl(47) == 47
81 using boost::property_tree::ptree;
82 #if BOOST_VERSION >= 105600
83 typedef boost::property_tree::xml_writer_settings<std::string> xml_writer_settings;
85 typedef boost::property_tree::xml_writer_settings<char> xml_writer_settings;
97 BOOST_PARAMETER_NAME(
object)
98 BOOST_PARAMETER_NAME(outfile)
99 BOOST_PARAMETER_NAME(out)
100 BOOST_PARAMETER_NAME(compress_data)
101 BOOST_PARAMETER_NAME(pretty_print)
102 BOOST_PARAMETER_NAME(indent_spaces)
103 BOOST_PARAMETER_NAME(use_tabs)
104 BOOST_PARAMETER_NAME(human_readable)
105 BOOST_PARAMETER_NAME(fold_in_class_names)
106 BOOST_PARAMETER_NAME(root_name)
107 BOOST_PARAMETER_NAME(name)
108 BOOST_PARAMETER_NAME(attributes)
122 template<
typename T,
bool do_it>
123 struct destroy_data {
124 void operator()(T* data)
const { };
128 struct destroy_data<T, true> {
129 void operator()(T* data)
const {
136 template<typename T, bool deallocate_when_destroyed=not std::is_const<T>::value>
141 bool human_readable_;
148 bool human_readable=
false,
149 bool pretty_print=
false
153 human_readable_(human_readable),
154 pretty_print_(pretty_print)
158 destroy_data<T, deallocate_when_destroyed>()(data_);
161 unsigned long n()
const {
return n_; }
162 T* data()
const {
return data_; }
163 bool human_readable()
const {
return human_readable_; }
164 bool pretty_print()
const {
return pretty_print_; }
175 template <
typename T>
176 typename boost::enable_if<
177 boost::is_base_of<XMLWritable, typename boost::decay<T>::type>,
186 template <
typename T>
190 typename boost::disable_if_c<
191 boost::is_base_of<
XMLWritable,
typename boost::decay<T>::type>::value
192 or not boost::is_base_of<
RefCount,
typename boost::decay<T>::type>::value,
194 >::type
const& parent,
203 ptree& write_xml(
const Eigen::MatrixXd&, ptree&,
const XMLWriter&);
204 ptree& write_xml(
const Eigen::VectorXd&, ptree&,
const XMLWriter&);
205 ptree& write_xml(
const std::vector<double>&, ptree&,
const XMLWriter&);
207 template<
typename Derived>
208 ptree& write_xml(
const Eigen::MatrixBase<Derived>&, ptree&,
const XMLWriter&);
221 ptree* current_root_;
222 std::stack<ptree*> pt_stack_;
225 bool delete_pt_ =
false;
228 bool human_readable_;
229 bool fold_in_class_name_;
230 bool writing_done_ =
false;
232 int pretty_print_spaces_;
233 char pretty_print_space_char_;
234 std::vector< Ref<XMLWritable> > data_;
235 xml_writer_settings write_settings_;
239 void init_filename(
const std::string& filename);
241 std::string filename_;
259 bool fold_in_class_name()
const {
return fold_in_class_name_; }
260 const std::string& filename()
const {
return filename_; }
261 bool writing_done()
const {
return writing_done_; }
262 bool has_active_context()
const {
return pt_stack_.size() > 1; }
270 bool preserve_data_pointer =
false
273 assert(!compress_data_);
274 if(!human_readable_){
275 pt.put(
"<xmlattr>.ndata", ndata);
276 pt.put(
"<xmlattr>.datum_size",
sizeof(T));
277 pt.put(
"<xmlattr>.big_endian", IS_BIG_ENDIAN);
280 pt.put(
"<xmlattr>.ndata", ndata);
281 pt.put(
"<xmlattr>.human_readable",
true);
284 if(preserve_data_pointer) {
300 template <
typename T>
306 return this->write_to_xml(obj, parent);
309 template <
typename T>
313 std::string wrapper_name
317 ptree& child_tree = parent.add_child(wrapper_name, ptree());
318 this->write_to_xml(obj, child_tree);
322 template <
typename T,
typename MapType>
326 std::string wrapper_name,
331 ptree& child_tree = parent.add_child(wrapper_name, ptree());
332 this->write_to_xml(obj, child_tree);
333 for(
auto it : attrs) {
334 child_tree.put(
"<xmlattr>." + it.first, it.second);
339 template <
typename T,
typename... Args>
340 ptree& insert_child_default(
345 assert(not writing_done_);
346 return insert_child(*current_root_, obj, args...);
355 template <
typename T>
356 ptree& write_to_xml(T&& obj, ptree& parent)
const
359 return write_xml(std::forward<T>(obj), parent, *
this);
363 inline typename boost::enable_if<boost::is_base_of<RefCount, T>, ptree&>::type
364 write_to_xml(
const Ref<T>& obj, ptree& parent)
const {
365 return write_to_xml_impl(obj, parent, boost::is_base_of<XMLWritable, T>());
369 ptree& write_to_xml(T&& obj)
const {
370 assert(not writing_done_);
371 return write_to_xml(std::forward<T>(obj), *current_root_);
377 void begin_writing_context(
const std::string& root_name);
379 void end_writing_context();
382 static std::stack<Ref<XMLWriter>> writer_stack;
383 static std::string current_context_name;
384 static std::stack<std::string> context_name_stack;
389 inline ptree& write_to_xml_impl(
const Ref<T>& obj, ptree& parent,
const boost::true_type is_writable)
const {
391 return write_to_xml(obj_write, parent);
395 inline ptree& write_to_xml_impl(
const Ref<T>& obj, ptree& parent,
const boost::false_type is_writable)
const {
396 return write_to_xml(*obj, parent);
404 template <
typename T>
405 typename boost::enable_if<
406 boost::is_base_of<XMLWritable, typename boost::decay<T>::type>,
415 return obj->write_xml(parent, writer);
418 template <
typename T>
422 typename boost::disable_if_c<
423 boost::is_base_of<XMLWritable,
typename boost::decay<T>::type>::value
424 or not boost::is_base_of<RefCount,
typename boost::decay<T>::type>::value,
426 >::type
const& parent,
427 const XMLWriter& writer
430 return write_xml(*obj, parent, writer);
433 template<
typename Derived>
434 ptree& write_xml(
const Eigen::MatrixBase<Derived>& obj, ptree& parent,
const XMLWriter& writer)
436 typedef Eigen::MatrixBase<Derived> MatrixType;
437 typedef typename Eigen::internal::traits<Derived>::Scalar Scalar;
439 ptree& child = parent.add_child(
"EigenDerived", ptree());
440 const int ninner = obj.innerSize();
441 const int nouter = obj.outerSize();
442 child.put(
"<xmlattr>.ninner", ninner);
443 child.put(
"<xmlattr>.nouter", nouter);
444 child.put(
"<xmlattr>.row_major",
int(MatrixType::IsRowMajor));
445 child.put(
"<xmlattr>.is_vector",
int(MatrixType::IsVectorAtCompileTime));
446 child.put(
"<xmlattr>.signed",
447 int(std::is_signed<Scalar>::value)
451 Scalar* data = allocate<Scalar>(nouter*ninner);
452 for(
int i = 0; i < nouter; ++i){
453 for(
int j = 0; j < ninner; ++j){
454 if(MatrixType::IsRowMajor) {
455 data[i*ninner + j] = obj(i, j);
458 data[i*ninner + j] = obj(j, i);
465 writer.put_binary_data(child.add_child(
"data", ptree()), data, nouter*ninner);
471 template<
typename Derived,
unsigned int ViewMode>
474 template<
typename Derived>
477 const Eigen::TriangularView<Derived, Eigen::Lower>& obj,
482 const int nrows = obj.rows();
483 const int ncols = obj.cols();
485 ptree& my_tree = pt.add_child(
"EigenDerived", ptree());
486 my_tree.put(
"<xmlattr>.n", nrows);
487 my_tree.put(
"<xmlattr>.lower_triangle",
true);
488 ptree& data_tree = my_tree.add_child(
"data", ptree());
489 long ndata = nrows * (nrows+1) / 2;
490 double* data = allocate<double>(ndata);
491 double* data_spot = data;
492 for(
int irow = 0; irow < nrows; ++irow) {
493 for(
int icol = 0; icol <= irow; ++icol) {
494 (*data_spot) = obj.coeff(irow, icol);
500 writer.put_binary_data<
double>(data_tree, data, ndata);
504 typedef Derived MatrixType;
507 ptree& child = pt.add_child(
"EigenDerived", ptree());
508 child.put(
"<xmlattr>.ninner", ncols);
509 child.put(
"<xmlattr>.nouter", nrows);
510 child.put(
"<xmlattr>.row_major",
true);
511 child.put(
"<xmlattr>.is_vector",
int(MatrixType::IsVectorAtCompileTime));
512 ptree& data_tree = child.add_child(
"data", ptree());
513 double* data = allocate<double>(nrows*ncols);
514 double* data_spot = data;
515 for(
int irow = 0; irow < nrows; ++irow) {
516 for(
int icol = 0; icol < ncols; ++icol) {
518 (*data_spot) = obj.coeff(irow, icol);
520 else if(icol < nrows) {
521 (*data_spot) = obj.coeff(icol, irow);
531 writer.put_binary_data<
double>(data_tree, data, nrows*ncols);
540 template<
typename Derived,
unsigned int ViewMode>
541 ptree& write_xml(
const Eigen::TriangularView<Derived, ViewMode>& obj, ptree& parent,
const XMLWriter& writer) {
546 template<
template<
typename...>
class Container,
template<
typename...>
class TupleType,
typename... NumTypes>
547 typename boost::enable_if_c<
548 boost::is_convertible<
549 Container<TupleType<NumTypes...>>,
550 std::vector<TupleType<NumTypes...>>
555 boost::is_integral<NumTypes>,
556 boost::is_floating_point<NumTypes>
562 const Container<TupleType<NumTypes...>>& obj,
564 const XMLWriter& writer
567 typedef Container<TupleType<NumTypes...>> input_type;
568 typedef std::vector<TupleType<NumTypes...>> vect_type;
572 const auto& the_function = [](
573 const vect_type& obj,
575 const XMLWriter& writer
579 if(writer.fold_in_class_name()) {
580 parent.put(
"<xmlattr>.type",
"std::vector<double>");
581 child_ptr = &(parent);
584 ptree& tmp = parent.add_child(
"data", ptree());
587 ptree& child = *child_ptr;
588 writer.put_binary_data(child, obj.data(), obj.size(),
true);
589 child.put(
"<xmlattr>.nperdatum",
sizeof...(NumTypes));
593 return the_function(obj, parent, writer);
600 using boost::is_convertible;
601 namespace mpl = boost::mpl;
603 #define XMLWRITER_FILENAME_NOT_GIVEN "__FILENAME_NOT_GIVEN__"
606 #define XMLWRITER_KV_OPTIONAL_PARAMS \
607 (outfile, *(is_convertible<mpl::_, std::string>), XMLWRITER_FILENAME_NOT_GIVEN) \
608 (compress_data, (bool), false) \
609 (pretty_print, (bool), true) \
610 (indent_spaces, (int), 2) \
611 (use_tabs, (bool), false) \
612 (human_readable, (bool), false) \
613 (fold_in_class_names, (bool), true)
615 #define XMLWRITER_CREATE_FROM_PARAMS(VARNAME) \
616 std::string __fname = outfile; \
617 Ref<AssignedKeyVal> akv = new AssignedKeyVal; \
618 akv->assign("filename", \
619 __fname == XMLWRITER_FILENAME_NOT_GIVEN ? "-" : __fname \
621 akv->assignboolean("compress_data", (bool)compress_data); \
622 akv->assignboolean("pretty_print", (bool)pretty_print); \
623 akv->assignboolean("indent_spaces", (bool)indent_spaces); \
624 akv->assignboolean("use_tabs", (bool)use_tabs); \
625 akv->assignboolean("human_readable", (bool)human_readable); \
626 akv->assignboolean("fold_in_class_names", (bool)fold_in_class_names); \
627 Ref<XMLWriter> VARNAME(new XMLWriter(akv));
629 BOOST_PARAMETER_FUNCTION(
639 XMLWRITER_KV_OPTIONAL_PARAMS
641 *(is_convertible<mpl::_, std::string>), std::string(
"mpqc")
647 XMLWRITER_CREATE_FROM_PARAMS(writer)
649 writer->begin_writing_context(std::string(root_name));
650 writer->write_to_xml(
object);
651 writer->end_writing_context();
656 BOOST_PARAMETER_FUNCTION(
661 (name, *(is_convertible<mpl::_, std::string>))
664 XMLWRITER_KV_OPTIONAL_PARAMS
668 std::string fname = outfile;
669 std::string tag_name = name;
670 bool fname_not_given = fname == XMLWRITER_FILENAME_NOT_GIVEN;
671 if(!XMLWriter::current_writer.
null() and not XMLWriter::current_writer->writing_done()){
672 if(fname_not_given || XMLWriter::current_writer->filename() == fname){
673 XMLWriter::current_writer->begin_writing_context(tag_name);
677 XMLWriter::writer_stack.push(XMLWriter::current_writer);
679 XMLWRITER_CREATE_FROM_PARAMS(writer)
681 XMLWriter::current_writer = writer;
682 writer->begin_writing_context(tag_name);
687 XMLWRITER_CREATE_FROM_PARAMS(writer)
689 XMLWriter::current_writer = writer;
690 writer->begin_writing_context(tag_name);
693 XMLWriter::context_name_stack.push(XMLWriter::current_context_name);
694 XMLWriter::current_context_name = tag_name;
698 BOOST_PARAMETER_FUNCTION(
703 (name, *(is_convertible<mpl::_, std::string>), XMLWRITER_FILENAME_NOT_GIVEN)
707 std::string tag_name = name;
709 (tag_name == XMLWRITER_FILENAME_NOT_GIVEN or tag_name == XMLWriter::current_context_name)
710 and !XMLWriter::current_writer.
null()
711 and not XMLWriter::context_name_stack.empty()
713 XMLWriter::current_writer->end_writing_context();
714 if(not XMLWriter::current_writer->has_active_context()){
715 if(XMLWriter::writer_stack.size() > 0) {
716 XMLWriter::current_writer = XMLWriter::writer_stack.top();
717 XMLWriter::writer_stack.pop();
720 XMLWriter::current_writer = 0;
725 throw ProgrammingError(
"Mismatched transparent XML contexts", __FILE__, __LINE__);
728 XMLWriter::current_context_name = XMLWriter::context_name_stack.top();
729 XMLWriter::context_name_stack.pop();
732 template <
typename T,
typename MapType>
733 inline typename boost::enable_if<is_convertible<MapType, bool>,
void>::type
734 _write_as_xml_impl(T&&
object,
const std::string& tag_name,
const MapType& attrs)
736 XMLWriter::current_writer->insert_child_default(
object, tag_name);
739 template <
typename T,
typename MapType>
740 inline typename boost::enable_if<boost::mpl::not_<is_convertible<MapType, bool>>,
void>::type
741 _write_as_xml_impl(T&&
object,
const std::string& tag_name,
const MapType& attrs)
743 XMLWriter::current_writer->insert_child_default(
object, tag_name, attrs);
746 BOOST_PARAMETER_FUNCTION(
755 (attributes, *,
false)
759 if(XMLWriter::current_writer.
null()) {
760 throw ProgrammingError(
"No current XML context", __FILE__, __LINE__);
763 std::string tag_name = name;
764 _write_as_xml_impl(
object, tag_name, attributes);
768 template<
typename Value=std::
string>
769 using attrs = std::map<std::string, Value>;
775 get_human_readable_data(
784 get_human_readable_data(
794 get_human_readable_data(
802 throw FeatureNotImplemented(
"get_human_readable_data", __FILE__, __LINE__);
811 typedef std::string internal_type;
814 boost::optional<external_type> get_value(
const internal_type& str)
818 assert(str.length()*
sizeof(char) %
sizeof(T) == 0);
821 unsigned long ndata = str.length() *
sizeof(char) /
sizeof(T);
822 T* rv_data =
new T[ndata];
823 ::memcpy(rv_data, &(str.c_str()[0]), ndata *
sizeof(T));
824 return boost::optional<external_type>(
external_type(rv_data, ndata));
827 return boost::optional<external_type>(boost::none);
832 boost::optional<internal_type> put_value(
const external_type& xds)
834 if(xds.human_readable()){
835 return boost::optional<internal_type>(
836 detail::get_human_readable_data(
844 using namespace boost::archive::iterators;
848 transform_width<const char*, 6, 8>
851 typedef insert_linebreaks<base64_text, 72> base64_text_linebreaks;
853 std::stringstream sstr;
854 if(xds.pretty_print()){
856 base64_text_linebreaks(xds.data()),
857 base64_text_linebreaks(xds.data() + xds.n()),
858 boost::archive::iterators::ostream_iterator<char>(sstr)
860 if(xds.n() * 8 *
sizeof(T) % 6 == 2)
861 return boost::optional<internal_type>(internal_type(
"\n" + sstr.str() +
"==\n"));
862 else if(xds.n() * 8 *
sizeof(T) % 6 == 4)
863 return boost::optional<internal_type>(internal_type(
"\n" + sstr.str() +
"=\n"));
865 return boost::optional<internal_type>(internal_type(
"\n" + sstr.str() +
"\n"));
869 base64_text(xds.data()),
870 base64_text(xds.data() + xds.n()),
871 boost::archive::iterators::ostream_iterator<char>(sstr)
873 if(xds.n() * 8 *
sizeof(T) % 6 == 2)
874 return boost::optional<internal_type>(internal_type(sstr.str() +
"=="));
875 else if(xds.n() * 8 *
sizeof(T) % 6 == 4)
876 return boost::optional<internal_type>(internal_type(sstr.str() +
"="));
878 return boost::optional<internal_type>(internal_type(sstr.str()));
890 namespace property_tree {
892 template<
typename Ch,
typename Traits,
typename Alloc,
typename T,
bool val>