/* * Copyright (C) 2019 YY Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __X_XML_WRITER_H #define __X_XML_WRITER_H #include #include #include #include #include //#include #include #include "util.h" #include "xtypes.h" #include "traits.h" #define X2STRUCT_BUFFER_SIZE 1024 #define X2STRUCT_TYPE_OBJECT 0 #define X2STRUCT_TYPE_ARRAY 1 namespace x2struct { class XmlWriter { private: class XmlKey{ public: XmlKey(const char*key, XmlWriter*writer, bool base) { _key = key; if (_key[0]=='\0') { return; } _writer = writer; _base = base; _writer->indent(); _writer->append("<", 1); _writer->append(_key, -1); _writer->append(">"); _lines = _writer->_lines; } ~XmlKey() { if (_key[0]=='\0') { return; } if (!_base || _writer->_lines>_lines) { _writer->indent(); } _writer->append("append(_key, -1); _writer->append(">"); } private: const char*_key; XmlWriter* _writer; bool _base; int _lines; }; friend class XmlKey; public: XmlWriter(int indentCount=0, char indentChar=' '):_indentCount(indentCount),_indentChar(indentChar) { if (_indentCount > 0) { if (_indentChar!=' ' && _indentChar!='\t') { throw std::runtime_error("indentChar must be space or tab"); } } _buffer.resize(1); _buffer.reserve(X2STRUCT_BUFFER_SIZE); _cur = &_buffer[0]; _lines = 0; } ~XmlWriter() { } public: std::string toStr() { size_t total_len = 0; for (size_t i=0; i<_buffer.size(); ++i) { total_len += _buffer[i].length(); } std::string buf; buf.reserve(total_len); for (size_t i=0; i<_buffer.size(); ++i) { buf.append(_buffer[i]); } return buf; } void array_begin() { _state.push_back(X2STRUCT_TYPE_ARRAY); } void array_end() { _state.pop_back(); } void object_begin() { _state.push_back(X2STRUCT_TYPE_OBJECT); } void object_end() { _state.pop_back(); } const std::string&type() { static std::string t("xml"); return t; } XmlWriter& convert(const char*key, const std::string &val) { XmlKey xkey(key, this, true); for (size_t i=0; i': append(">", 4); break; case '&': append("&", 5); break; case '\'': append("'", 6); break; case '\"': append(""", 6); break; default: if(c >= ' ') { append(val[i]); } else { char tmp[16]; int l = sprintf(tmp, "\\x%02X", c); append(tmp, l); } } } return *this; } #ifdef XTOSTRUCT_SUPPORT_CHAR_ARRAY XmlWriter& convert(const char*key, const char val[]) { std::string str(val); return this->convert(key, str); } #endif XmlWriter& convert(const char*key, bool val) { XmlKey xkey(key, this, true); if (val) { append("true", 4); } else { append("false", 5); } return *this; } XmlWriter& convert(const char*key, int16_t val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, uint16_t val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, int32_t val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, uint32_t val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, int64_t val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, uint64_t val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, double val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } XmlWriter& convert(const char*key, float val) { XmlKey xkey(key, this, true); append(Util::tostr(val)); return *this; } template XmlWriter& convert(const char*key, const std::vector&data) { key=key[0]=='\0'?"x":key; this->array_begin(); for (size_t i=0; iconvert("", data[i]); } this->array_end(); return *this; } template XmlWriter& convert(const char*key, const std::list&data) { key=key[0]=='\0'?"x":key; this->array_begin(); for (typename std::list::const_iterator it=data.begin(); it!=data.end(); ++it) { XmlKey xkey(key, this, true); this->convert("", *it); } this->array_end(); return *this; } template XmlWriter& convert(const char*key, const std::set&data) { key=key[0]=='\0'?"x":key; this->array_begin(); for (typename std::set::const_iterator it=data.begin(); it!=data.end(); ++it) { XmlKey xkey(key, this, true); this->convert("", *it); } this->array_end(); return *this; } template void convert(const char*key, const std::map &data) { XmlKey xkey(key, this, false); this->object_begin(); for (typename std::map::const_iterator iter=data.begin(); iter!=data.end(); ++iter) { this->convert(iter->first.c_str(), iter->second); } this->object_end(); } template void convert(const char*key, const std::map &data) { XmlKey xkey(key, this, false); this->object_begin(); for (typename std::map::const_iterator iter=data.begin(); iter!=data.end(); ++iter) { std::string _k("x"); _k.append(Util::tostr(iter->first)); this->convert(_k.c_str(), iter->second); } this->object_end(); } #ifdef X_SUPPORT_C0X // unordered_map template void convert(const char*key, const std::unordered_map &data) { XmlKey xkey(key, this, false); this->object_begin(); for (typename std::unordered_map::const_iterator iter=data.begin(); iter!=data.end(); ++iter) { this->convert(iter->first.c_str(), iter->second); } this->object_end(); } // shared_ptr template void convert(const char*key, const std::shared_ptr& val) { if (NULL == val.get()) { return; } this->convert(key, *val); } // class/struct that not defined macro XTOSTRUCT template ::value, int>::type = 0> void convert(const char*key, const T& data, x_for_class(T, int) *unused=0) { (void)unused; XmlKey xkey(key, this, false); this->object_begin(); x_struct_to_str(*this, key, data); this->object_end(); } // class/struct that defined macro XTOSTRUCT template ::value, int>::type = 0> void convert(const char*key, const T& data) { #else template void convert(const char*key, const T& data, x_for_class(T, int) *p=0) { (void)p; #endif XmlKey xkey(key, this, false); this->object_begin(); data.__struct_to_str(*this, key); this->object_end(); } template void convert(const char*key, const XType& data) { data.__struct_to_str(*this, key); } private: void append(const char* str, int len) { if (len < 0) { len = strlen(str); } if (len+_cur->length() <= X2STRUCT_BUFFER_SIZE) { _cur->append(str, len); } else { _buffer.push_back(std::string()); _cur = &_buffer[_buffer.size()-1]; _cur->reserve(X2STRUCT_BUFFER_SIZE); _cur->append(str, len); } } void append(const std::string&str) { append(str.c_str(), str.length()); } void append(char ch) { char buf[1] = {ch}; append(buf, 1); } void indent() { if (_indentCount >= 0) { append("\n", 1); ++_lines; } size_t depth=_state.size(); if (in_array()) { --depth; } if (_indentCount>0 && depth>0) { append(std::string(_indentCount*depth, _indentChar)); } } bool in_array() { return _state.size()>0&&_state[_state.size()-1]==X2STRUCT_TYPE_ARRAY; } int _indentCount; char _indentChar; int _lines; std::vector _buffer; std::string *_cur; std::vector _state; }; } #undef X2STRUCT_BUFFER_SIZE #undef X2STRUCT_TYPE_OBJECT #undef X2STRUCT_TYPE_ARRAY #endif