1 | |
---|
2 | #pragma once |
---|
3 | |
---|
4 | #include <stack> |
---|
5 | |
---|
6 | #include <boost/mpl/assert.hpp> |
---|
7 | #include <boost/format.hpp> |
---|
8 | #include <boost/lexical_cast.hpp> |
---|
9 | #include <boost/filesystem/path.hpp> |
---|
10 | #include <boost/filesystem/operations.hpp> |
---|
11 | |
---|
12 | #include <boost/archive/xml_iarchive.hpp> |
---|
13 | |
---|
14 | #include <boost/serialization/version.hpp> |
---|
15 | #include <boost/serialization/vector.hpp> |
---|
16 | #include <boost/serialization/map.hpp> |
---|
17 | #include <boost/serialization/split_free.hpp> |
---|
18 | |
---|
19 | #include <boost/archive/impl/basic_text_iprimitive.ipp> |
---|
20 | #include <boost/archive/impl/xml_iarchive_impl.ipp> |
---|
21 | #include <boost/archive/impl/basic_xml_iarchive.ipp> |
---|
22 | #include <boost/archive/impl/archive_pointer_iserializer.ipp> |
---|
23 | #include <boost/archive/shared_ptr_helper.hpp> |
---|
24 | |
---|
25 | #include "global/string_conv.hpp" |
---|
26 | #include "txml.hpp" |
---|
27 | |
---|
28 | #define foreach BOOST_FOREACH |
---|
29 | |
---|
30 | #ifndef TXML_ARCHIVE_LOGGING |
---|
31 | # define TXML_LOG(s) |
---|
32 | #else |
---|
33 | # include "../halEvent.hpp" |
---|
34 | # define TXML_LOG(msg) \ |
---|
35 | hal::event_log.post(boost::shared_ptr<hal::EventDetail>( \ |
---|
36 | new hal::EventMsg(msg, hal::event_logger::xml_dev))) |
---|
37 | #endif |
---|
38 | |
---|
39 | namespace |
---|
40 | { |
---|
41 | |
---|
42 | struct i_stringstream_holder |
---|
43 | { |
---|
44 | std::stringstream stream_; |
---|
45 | }; |
---|
46 | |
---|
47 | } |
---|
48 | |
---|
49 | namespace hal { namespace xml |
---|
50 | { |
---|
51 | |
---|
52 | namespace serial = boost::serialization; |
---|
53 | namespace arc = boost::archive; |
---|
54 | |
---|
55 | class txml_iarchive : |
---|
56 | private i_stringstream_holder, |
---|
57 | public arc::basic_text_iprimitive<std::istream>, |
---|
58 | public arc::detail::common_iarchive<txml_iarchive>, |
---|
59 | public boost::archive::detail::shared_ptr_helper |
---|
60 | { |
---|
61 | typedef arc::detail::common_iarchive<txml_iarchive> detail_common_iarchive; |
---|
62 | |
---|
63 | public: |
---|
64 | txml_iarchive(std::istream& is, unsigned int flags = 0) : |
---|
65 | basic_text_iprimitive<std::istream>(stream_, 0 != (flags & arc::no_codecvt)), |
---|
66 | detail_common_iarchive(flags), |
---|
67 | is_(is), |
---|
68 | previous_child_node_(0) |
---|
69 | { |
---|
70 | is_ >> xml_; |
---|
71 | |
---|
72 | current_node_ = xml_.root_element(); |
---|
73 | init(); |
---|
74 | } |
---|
75 | |
---|
76 | ~txml_iarchive() |
---|
77 | {} |
---|
78 | |
---|
79 | template<class T> |
---|
80 | void load(T& t) |
---|
81 | { |
---|
82 | std::string tstring = current_node_->first_child()->value_str(); |
---|
83 | |
---|
84 | TXML_LOG(boost::wformat(L" << basic_text_iprimitive: %1%") % from_utf8(tstring)); |
---|
85 | |
---|
86 | stream_ << tstring; |
---|
87 | basic_text_iprimitive<std::istream>::load(t); |
---|
88 | |
---|
89 | stream_.clear(); |
---|
90 | stream_ << std::noboolalpha; |
---|
91 | } |
---|
92 | |
---|
93 | /* void load(char* s) |
---|
94 | { |
---|
95 | std::string tstring = current_node_->first_child()->value_str(); |
---|
96 | |
---|
97 | std::memcpy(s, tstring.data(), tstring.size()); |
---|
98 | s[tstring.size()] = 0; |
---|
99 | TXML_LOG(boost::wformat(L" << load char*: %1%") % from_utf8(tstring)); |
---|
100 | } |
---|
101 | |
---|
102 | void load(wchar_t* t) |
---|
103 | { |
---|
104 | TXML_LOG(boost::wformat(L" << load wchar_t*: %1%") % from_utf8(current_node_->first_child()->value_str()); |
---|
105 | } |
---|
106 | */ |
---|
107 | void load(std::string& s) |
---|
108 | { |
---|
109 | if (xml::node* child = current_node_->first_child()) |
---|
110 | s = child->value_str(); |
---|
111 | else |
---|
112 | s = ""; |
---|
113 | |
---|
114 | TXML_LOG(boost::wformat(L" << load string: %1%") % from_utf8(s)); |
---|
115 | } |
---|
116 | |
---|
117 | void load(std::wstring& ws) |
---|
118 | { |
---|
119 | if (xml::node* child = current_node_->first_child()) |
---|
120 | ws = from_utf8(child->value_str()); |
---|
121 | else |
---|
122 | ws = L""; |
---|
123 | |
---|
124 | TXML_LOG(boost::wformat(L" << load wstring: %1%") % ws); |
---|
125 | } |
---|
126 | |
---|
127 | void load_override(arc::class_name_type& t, int) |
---|
128 | { |
---|
129 | std::string tstring = current_node_->first_child()->value_str(); |
---|
130 | |
---|
131 | TXML_LOG(boost::wformat(L" << load class_name_type: %1%") % from_utf8(tstring)); |
---|
132 | |
---|
133 | char * tptr = t; |
---|
134 | std::memcpy(tptr, tstring.data(), tstring.size()); |
---|
135 | tptr[tstring.size()] = '\0'; |
---|
136 | } |
---|
137 | |
---|
138 | void init() |
---|
139 | { |
---|
140 | if (current_node_) |
---|
141 | { |
---|
142 | std::string signature = read_attribute<std::string>("signature"); |
---|
143 | int version = read_attribute<int>("version"); |
---|
144 | |
---|
145 | TXML_LOG(boost::wformat(L" << siganture: %1%, version: %2%") % from_utf8(signature) % version); |
---|
146 | } |
---|
147 | } |
---|
148 | |
---|
149 | template<typename T> |
---|
150 | T read_attribute(const char* attribute_name, const char* fallback_name=0) |
---|
151 | { |
---|
152 | T type = T(); |
---|
153 | |
---|
154 | TXML_LOG(boost::wformat(L" << attribute_name: %1%") % from_utf8(attribute_name)); |
---|
155 | |
---|
156 | xml::element* e = current_node_->to_element(); |
---|
157 | if (!e) return type; |
---|
158 | |
---|
159 | int result = e->query_value_attribute(attribute_name, &type); |
---|
160 | |
---|
161 | if (result == xml::TIXML_NO_ATTRIBUTE && fallback_name != 0) |
---|
162 | { |
---|
163 | TXML_LOG(boost::wformat(L" << -- fallback_name: %1%") % from_utf8(fallback_name)); |
---|
164 | |
---|
165 | result = e->query_value_attribute(fallback_name, &type); |
---|
166 | } |
---|
167 | |
---|
168 | assert(result == xml::TIXML_SUCCESS); |
---|
169 | TXML_LOG(boost::wformat(L" << -- value: %2%") % from_utf8(attribute_name) % type); |
---|
170 | |
---|
171 | return type; |
---|
172 | } |
---|
173 | |
---|
174 | template<> |
---|
175 | std::string read_attribute(const char* attribute_name, const char* fallback_name) |
---|
176 | { |
---|
177 | std::string type; |
---|
178 | |
---|
179 | TXML_LOG(boost::wformat(L" << attribute_name: %1%") % from_utf8(attribute_name)); |
---|
180 | |
---|
181 | xml::element* e = current_node_->to_element(); |
---|
182 | if (!e) return type; |
---|
183 | |
---|
184 | int result = e->query_value_attribute(attribute_name, &type); |
---|
185 | |
---|
186 | if (result == xml::TIXML_NO_ATTRIBUTE && fallback_name != 0) |
---|
187 | { |
---|
188 | TXML_LOG(boost::wformat(L" << -- fallback_name: %1%") % from_utf8(fallback_name)); |
---|
189 | |
---|
190 | result = e->query_value_attribute(fallback_name, &type); |
---|
191 | } |
---|
192 | |
---|
193 | assert(result == xml::TIXML_SUCCESS); |
---|
194 | |
---|
195 | TXML_LOG(boost::wformat(L" << -- value: %2%") % from_utf8(attribute_name) % from_utf8(type)); |
---|
196 | |
---|
197 | return type; |
---|
198 | } |
---|
199 | |
---|
200 | bool load_start(const char* name) |
---|
201 | { |
---|
202 | if (name) |
---|
203 | { |
---|
204 | xml::node* failsafe_current = 0; |
---|
205 | |
---|
206 | TXML_LOG(boost::wformat(L" << load_start: %1%") % from_utf8(name)); |
---|
207 | |
---|
208 | node_stack_.push(current_node_); |
---|
209 | failsafe_current = current_node_; |
---|
210 | |
---|
211 | boost::filesystem::path location(name); |
---|
212 | |
---|
213 | if (previous_child_node_ && |
---|
214 | previous_child_branch_ == location.parent_path()) |
---|
215 | { |
---|
216 | // TXML_LOG(boost::wformat(L" << previous_child: %1%") % previous_child_node_->to_element()->get_text()); |
---|
217 | failsafe_current = previous_child_node_->next_sibling(location.filename()); |
---|
218 | previous_child_node_ = 0; |
---|
219 | |
---|
220 | if (!failsafe_current) |
---|
221 | failsafe_current = current_node_->first_child(location.filename());; |
---|
222 | } |
---|
223 | else |
---|
224 | { |
---|
225 | foreach(std::string elem, location) |
---|
226 | { |
---|
227 | TXML_LOG(boost::wformat(L" >> >> %1%") % from_utf8(elem)); |
---|
228 | |
---|
229 | if (elem == ".") |
---|
230 | {} |
---|
231 | else if (elem == "..") |
---|
232 | { |
---|
233 | failsafe_current = failsafe_current->parent(); |
---|
234 | } |
---|
235 | else |
---|
236 | { |
---|
237 | failsafe_current = failsafe_current->first_child(elem); |
---|
238 | |
---|
239 | if (!failsafe_current) return false; |
---|
240 | } |
---|
241 | } |
---|
242 | } |
---|
243 | |
---|
244 | // xml::node* n = new xml::element(leaf); |
---|
245 | |
---|
246 | if (!failsafe_current) |
---|
247 | return false; |
---|
248 | else |
---|
249 | { |
---|
250 | current_node_ = failsafe_current; |
---|
251 | |
---|
252 | previous_child_branch_ = location.parent_path(); |
---|
253 | } |
---|
254 | } |
---|
255 | return true; |
---|
256 | } |
---|
257 | |
---|
258 | void load_end(const char *name) |
---|
259 | { |
---|
260 | if (name) |
---|
261 | { |
---|
262 | TXML_LOG(boost::wformat(L" << load_end: %1%") % from_utf8(name)); |
---|
263 | |
---|
264 | previous_child_node_ = current_node_; |
---|
265 | |
---|
266 | current_node_ = node_stack_.top(); |
---|
267 | node_stack_.pop(); |
---|
268 | } |
---|
269 | } |
---|
270 | |
---|
271 | // Anything not an attribute and not a name-value pair is an |
---|
272 | // error and should be trapped here. |
---|
273 | template<class T> |
---|
274 | void load_override(T & t, BOOST_PFTO int) |
---|
275 | { |
---|
276 | BOOST_MPL_ASSERT((boost::serialization::is_wrapper<T>)); |
---|
277 | |
---|
278 | this->detail_common_iarchive::load_override(t, 0); |
---|
279 | // If your program fails to compile here, its most likely due to |
---|
280 | // not specifying an nvp wrapper around the variable to |
---|
281 | // be serialized. |
---|
282 | } |
---|
283 | |
---|
284 | // special treatment for name-value pairs. |
---|
285 | template<class T> |
---|
286 | void load_override(const ::boost::serialization::nvp<T>& t, int) |
---|
287 | { |
---|
288 | if (t.name()) TXML_LOG(boost::wformat(L" << loading: %1%") % t.name()); |
---|
289 | |
---|
290 | if (current_node_ && load_start(t.name())) |
---|
291 | { |
---|
292 | this->detail_common_iarchive::load_override(t.value(), 0); |
---|
293 | load_end(t.name()); |
---|
294 | } |
---|
295 | else |
---|
296 | TXML_LOG(boost::wformat(L" << load_aborted: %1%") % t.name()); |
---|
297 | } |
---|
298 | |
---|
299 | // specific overrides for attributes - not name value pairs so we |
---|
300 | // want to trap them before the above "fall through" |
---|
301 | void load_override(arc::class_id_optional_type&, int) |
---|
302 | {} |
---|
303 | |
---|
304 | void load_override(arc::object_id_type& t, int) |
---|
305 | { |
---|
306 | t = read_attribute<arc::object_id_type>(arc::OBJECT_ID(), arc::OBJECT_REFERENCE()); |
---|
307 | } |
---|
308 | |
---|
309 | void load_override(arc::version_type& t, int) |
---|
310 | { |
---|
311 | t = read_attribute<arc::version_type>(arc::VERSION()); |
---|
312 | } |
---|
313 | |
---|
314 | void load_override(arc::class_id_type& t, int) |
---|
315 | { |
---|
316 | t = read_attribute<arc::class_id_type>(arc::CLASS_ID(), arc::CLASS_ID_REFERENCE()); |
---|
317 | } |
---|
318 | |
---|
319 | void load_override(arc::tracking_type& t, int) |
---|
320 | { |
---|
321 | t = read_attribute<arc::tracking_type>(arc::TRACKING()); |
---|
322 | } |
---|
323 | |
---|
324 | xml::document xml_; |
---|
325 | |
---|
326 | xml::node* current_node_; |
---|
327 | xml::node* previous_child_node_; |
---|
328 | boost::filesystem::path previous_child_branch_; |
---|
329 | |
---|
330 | std::stack<xml::node*> node_stack_; |
---|
331 | |
---|
332 | std::istream& is_; |
---|
333 | }; |
---|
334 | |
---|
335 | } } |
---|
336 | |
---|
337 | //BOOST_SERIALIZATION_REGISTER_ARCHIVE(xml::txml_iarchive) |
---|