1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/Context.h"
26 #include "../IO/Log.h"
27 #include "../Resource/XMLFile.h"
28
29 #include <PugiXml/pugixml.hpp>
30
31 #include "../DebugNew.h"
32
33 namespace Urho3D
34 {
35
36 const XMLElement XMLElement::EMPTY;
37
XMLElement()38 XMLElement::XMLElement() :
39 node_(0),
40 xpathResultSet_(0),
41 xpathNode_(0),
42 xpathResultIndex_(0)
43 {
44 }
45
XMLElement(XMLFile * file,pugi::xml_node_struct * node)46 XMLElement::XMLElement(XMLFile* file, pugi::xml_node_struct* node) :
47 file_(file),
48 node_(node),
49 xpathResultSet_(0),
50 xpathNode_(0),
51 xpathResultIndex_(0)
52 {
53 }
54
XMLElement(XMLFile * file,const XPathResultSet * resultSet,const pugi::xpath_node * xpathNode,unsigned xpathResultIndex)55 XMLElement::XMLElement(XMLFile* file, const XPathResultSet* resultSet, const pugi::xpath_node* xpathNode,
56 unsigned xpathResultIndex) :
57 file_(file),
58 node_(0),
59 xpathResultSet_(resultSet),
60 xpathNode_(resultSet ? xpathNode : (xpathNode ? new pugi::xpath_node(*xpathNode) : 0)),
61 xpathResultIndex_(xpathResultIndex)
62 {
63 }
64
XMLElement(const XMLElement & rhs)65 XMLElement::XMLElement(const XMLElement& rhs) :
66 file_(rhs.file_),
67 node_(rhs.node_),
68 xpathResultSet_(rhs.xpathResultSet_),
69 xpathNode_(rhs.xpathResultSet_ ? rhs.xpathNode_ : (rhs.xpathNode_ ? new pugi::xpath_node(*rhs.xpathNode_) : 0)),
70 xpathResultIndex_(rhs.xpathResultIndex_)
71 {
72 }
73
~XMLElement()74 XMLElement::~XMLElement()
75 {
76 // XMLElement class takes the ownership of a single xpath_node object, so destruct it now
77 if (!xpathResultSet_ && xpathNode_)
78 {
79 delete xpathNode_;
80 xpathNode_ = 0;
81 }
82 }
83
operator =(const XMLElement & rhs)84 XMLElement& XMLElement::operator =(const XMLElement& rhs)
85 {
86 file_ = rhs.file_;
87 node_ = rhs.node_;
88 xpathResultSet_ = rhs.xpathResultSet_;
89 xpathNode_ = rhs.xpathResultSet_ ? rhs.xpathNode_ : (rhs.xpathNode_ ? new pugi::xpath_node(*rhs.xpathNode_) : 0);
90 xpathResultIndex_ = rhs.xpathResultIndex_;
91 return *this;
92 }
93
CreateChild(const String & name)94 XMLElement XMLElement::CreateChild(const String& name)
95 {
96 return CreateChild(name.CString());
97 }
98
CreateChild(const char * name)99 XMLElement XMLElement::CreateChild(const char* name)
100 {
101 if (!file_ || (!node_ && !xpathNode_))
102 return XMLElement();
103
104 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
105 pugi::xml_node child = const_cast<pugi::xml_node&>(node).append_child(name);
106 return XMLElement(file_, child.internal_object());
107 }
108
GetOrCreateChild(const String & name)109 XMLElement XMLElement::GetOrCreateChild(const String& name)
110 {
111 XMLElement child = GetChild(name);
112 if (child.NotNull())
113 return child;
114 else
115 return CreateChild(name);
116 }
117
GetOrCreateChild(const char * name)118 XMLElement XMLElement::GetOrCreateChild(const char* name)
119 {
120 XMLElement child = GetChild(name);
121 if (child.NotNull())
122 return child;
123 else
124 return CreateChild(name);
125 }
126
RemoveChild(const XMLElement & element)127 bool XMLElement::RemoveChild(const XMLElement& element)
128 {
129 if (!element.file_ || (!element.node_ && !element.xpathNode_) || !file_ || (!node_ && !xpathNode_))
130 return false;
131
132 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
133 const pugi::xml_node& child = element.xpathNode_ ? element.xpathNode_->node() : pugi::xml_node(element.node_);
134 return const_cast<pugi::xml_node&>(node).remove_child(child);
135 }
136
RemoveChild(const String & name)137 bool XMLElement::RemoveChild(const String& name)
138 {
139 return RemoveChild(name.CString());
140 }
141
RemoveChild(const char * name)142 bool XMLElement::RemoveChild(const char* name)
143 {
144 if (!file_ || (!node_ && !xpathNode_))
145 return false;
146
147 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
148 return const_cast<pugi::xml_node&>(node).remove_child(name);
149 }
150
RemoveChildren(const String & name)151 bool XMLElement::RemoveChildren(const String& name)
152 {
153 return RemoveChildren(name.CString());
154 }
155
RemoveChildren(const char * name)156 bool XMLElement::RemoveChildren(const char* name)
157 {
158 if ((!file_ || !node_) && !xpathNode_)
159 return false;
160
161 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
162 if (!String::CStringLength(name))
163 {
164 for (;;)
165 {
166 pugi::xml_node child = node.last_child();
167 if (child.empty())
168 break;
169 const_cast<pugi::xml_node&>(node).remove_child(child);
170 }
171 }
172 else
173 {
174 for (;;)
175 {
176 pugi::xml_node child = node.child(name);
177 if (child.empty())
178 break;
179 const_cast<pugi::xml_node&>(node).remove_child(child);
180 }
181 }
182
183 return true;
184 }
185
RemoveAttribute(const String & name)186 bool XMLElement::RemoveAttribute(const String& name)
187 {
188 return RemoveAttribute(name.CString());
189 }
190
RemoveAttribute(const char * name)191 bool XMLElement::RemoveAttribute(const char* name)
192 {
193 if (!file_ || (!node_ && !xpathNode_))
194 return false;
195
196 // If xpath_node contains just attribute, remove it regardless of the specified name
197 if (xpathNode_ && xpathNode_->attribute())
198 return xpathNode_->parent().remove_attribute(
199 xpathNode_->attribute()); // In attribute context, xpath_node's parent is the parent node of the attribute itself
200
201 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
202 return const_cast<pugi::xml_node&>(node).remove_attribute(node.attribute(name));
203 }
204
SelectSingle(const String & query,pugi::xpath_variable_set * variables) const205 XMLElement XMLElement::SelectSingle(const String& query, pugi::xpath_variable_set* variables) const
206 {
207 if (!file_ || (!node_ && !xpathNode_))
208 return XMLElement();
209
210 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
211 pugi::xpath_node result = node.select_single_node(query.CString(), variables);
212 return XMLElement(file_, 0, &result, 0);
213 }
214
SelectSinglePrepared(const XPathQuery & query) const215 XMLElement XMLElement::SelectSinglePrepared(const XPathQuery& query) const
216 {
217 if (!file_ || (!node_ && !xpathNode_ && !query.GetXPathQuery()))
218 return XMLElement();
219
220 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
221 pugi::xpath_node result = node.select_single_node(*query.GetXPathQuery());
222 return XMLElement(file_, 0, &result, 0);
223 }
224
Select(const String & query,pugi::xpath_variable_set * variables) const225 XPathResultSet XMLElement::Select(const String& query, pugi::xpath_variable_set* variables) const
226 {
227 if (!file_ || (!node_ && !xpathNode_))
228 return XPathResultSet();
229
230 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
231 pugi::xpath_node_set result = node.select_nodes(query.CString(), variables);
232 return XPathResultSet(file_, &result);
233 }
234
SelectPrepared(const XPathQuery & query) const235 XPathResultSet XMLElement::SelectPrepared(const XPathQuery& query) const
236 {
237 if (!file_ || (!node_ && !xpathNode_ && query.GetXPathQuery()))
238 return XPathResultSet();
239
240 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
241 pugi::xpath_node_set result = node.select_nodes(*query.GetXPathQuery());
242 return XPathResultSet(file_, &result);
243 }
244
SetValue(const String & value)245 bool XMLElement::SetValue(const String& value)
246 {
247 return SetValue(value.CString());
248 }
249
SetValue(const char * value)250 bool XMLElement::SetValue(const char* value)
251 {
252 if (!file_ || (!node_ && !xpathNode_))
253 return false;
254
255 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
256
257 // Search for existing value first
258 for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling())
259 {
260 if (child.type() == pugi::node_pcdata)
261 return const_cast<pugi::xml_node&>(child).set_value(value);
262 }
263
264 // If no previous value found, append new
265 return const_cast<pugi::xml_node&>(node).append_child(pugi::node_pcdata).set_value(value);
266 }
267
SetAttribute(const String & name,const String & value)268 bool XMLElement::SetAttribute(const String& name, const String& value)
269 {
270 return SetAttribute(name.CString(), value.CString());
271 }
272
SetAttribute(const char * name,const char * value)273 bool XMLElement::SetAttribute(const char* name, const char* value)
274 {
275 if (!file_ || (!node_ && !xpathNode_))
276 return false;
277
278 // If xpath_node contains just attribute, set its value regardless of the specified name
279 if (xpathNode_ && xpathNode_->attribute())
280 return xpathNode_->attribute().set_value(value);
281
282 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
283 pugi::xml_attribute attr = node.attribute(name);
284 if (attr.empty())
285 attr = const_cast<pugi::xml_node&>(node).append_attribute(name);
286 return attr.set_value(value);
287 }
288
SetAttribute(const String & value)289 bool XMLElement::SetAttribute(const String& value)
290 {
291 return SetAttribute(value.CString());
292 }
293
SetAttribute(const char * value)294 bool XMLElement::SetAttribute(const char* value)
295 {
296 // If xpath_node contains just attribute, set its value
297 return xpathNode_ && xpathNode_->attribute() && xpathNode_->attribute().set_value(value);
298 }
299
SetBool(const String & name,bool value)300 bool XMLElement::SetBool(const String& name, bool value)
301 {
302 return SetAttribute(name, String(value));
303 }
304
SetBoundingBox(const BoundingBox & value)305 bool XMLElement::SetBoundingBox(const BoundingBox& value)
306 {
307 if (!SetVector3("min", value.min_))
308 return false;
309 return SetVector3("max", value.max_);
310 }
311
SetBuffer(const String & name,const void * data,unsigned size)312 bool XMLElement::SetBuffer(const String& name, const void* data, unsigned size)
313 {
314 String dataStr;
315 BufferToString(dataStr, data, size);
316 return SetAttribute(name, dataStr);
317 }
318
SetBuffer(const String & name,const PODVector<unsigned char> & value)319 bool XMLElement::SetBuffer(const String& name, const PODVector<unsigned char>& value)
320 {
321 if (!value.Size())
322 return SetAttribute(name, String::EMPTY);
323 else
324 return SetBuffer(name, &value[0], value.Size());
325 }
326
SetColor(const String & name,const Color & value)327 bool XMLElement::SetColor(const String& name, const Color& value)
328 {
329 return SetAttribute(name, value.ToString());
330 }
331
SetFloat(const String & name,float value)332 bool XMLElement::SetFloat(const String& name, float value)
333 {
334 return SetAttribute(name, String(value));
335 }
336
SetDouble(const String & name,double value)337 bool XMLElement::SetDouble(const String& name, double value)
338 {
339 return SetAttribute(name, String(value));
340 }
341
SetUInt(const String & name,unsigned value)342 bool XMLElement::SetUInt(const String& name, unsigned value)
343 {
344 return SetAttribute(name, String(value));
345 }
346
SetInt(const String & name,int value)347 bool XMLElement::SetInt(const String& name, int value)
348 {
349 return SetAttribute(name, String(value));
350 }
351
SetUInt64(const String & name,unsigned long long value)352 bool XMLElement::SetUInt64(const String& name, unsigned long long value)
353 {
354 return SetAttribute(name, String(value));
355 }
356
SetInt64(const String & name,long long value)357 bool XMLElement::SetInt64(const String& name, long long value)
358 {
359 return SetAttribute(name, String(value));
360 }
361
SetIntRect(const String & name,const IntRect & value)362 bool XMLElement::SetIntRect(const String& name, const IntRect& value)
363 {
364 return SetAttribute(name, value.ToString());
365 }
366
SetIntVector2(const String & name,const IntVector2 & value)367 bool XMLElement::SetIntVector2(const String& name, const IntVector2& value)
368 {
369 return SetAttribute(name, value.ToString());
370 }
371
SetIntVector3(const String & name,const IntVector3 & value)372 bool XMLElement::SetIntVector3(const String& name, const IntVector3& value)
373 {
374 return SetAttribute(name, value.ToString());
375 }
376
SetRect(const String & name,const Rect & value)377 bool XMLElement::SetRect(const String& name, const Rect& value)
378 {
379 return SetAttribute(name, value.ToString());
380 }
381
SetQuaternion(const String & name,const Quaternion & value)382 bool XMLElement::SetQuaternion(const String& name, const Quaternion& value)
383 {
384 return SetAttribute(name, value.ToString());
385 }
386
SetString(const String & name,const String & value)387 bool XMLElement::SetString(const String& name, const String& value)
388 {
389 return SetAttribute(name, value);
390 }
391
SetVariant(const Variant & value)392 bool XMLElement::SetVariant(const Variant& value)
393 {
394 if (!SetAttribute("type", value.GetTypeName()))
395 return false;
396
397 return SetVariantValue(value);
398 }
399
SetVariantValue(const Variant & value)400 bool XMLElement::SetVariantValue(const Variant& value)
401 {
402 switch (value.GetType())
403 {
404 case VAR_RESOURCEREF:
405 return SetResourceRef(value.GetResourceRef());
406
407 case VAR_RESOURCEREFLIST:
408 return SetResourceRefList(value.GetResourceRefList());
409
410 case VAR_VARIANTVECTOR:
411 return SetVariantVector(value.GetVariantVector());
412
413 case VAR_STRINGVECTOR:
414 return SetStringVector(value.GetStringVector());
415
416 case VAR_VARIANTMAP:
417 return SetVariantMap(value.GetVariantMap());
418
419 default:
420 return SetAttribute("value", value.ToString().CString());
421 }
422 }
423
SetResourceRef(const ResourceRef & value)424 bool XMLElement::SetResourceRef(const ResourceRef& value)
425 {
426 if (!file_ || (!node_ && !xpathNode_))
427 return false;
428
429 // Need the context to query for the type
430 Context* context = file_->GetContext();
431
432 return SetAttribute("value", String(context->GetTypeName(value.type_)) + ";" + value.name_);
433 }
434
SetResourceRefList(const ResourceRefList & value)435 bool XMLElement::SetResourceRefList(const ResourceRefList& value)
436 {
437 if (!file_ || (!node_ && !xpathNode_))
438 return false;
439
440 // Need the context to query for the type
441 Context* context = file_->GetContext();
442
443 String str(context->GetTypeName(value.type_));
444 for (unsigned i = 0; i < value.names_.Size(); ++i)
445 {
446 str += ";";
447 str += value.names_[i];
448 }
449
450 return SetAttribute("value", str.CString());
451 }
452
SetVariantVector(const VariantVector & value)453 bool XMLElement::SetVariantVector(const VariantVector& value)
454 {
455 // Must remove all existing variant child elements (if they exist) to not cause confusion
456 if (!RemoveChildren("variant"))
457 return false;
458
459 for (VariantVector::ConstIterator i = value.Begin(); i != value.End(); ++i)
460 {
461 XMLElement variantElem = CreateChild("variant");
462 if (!variantElem)
463 return false;
464 variantElem.SetVariant(*i);
465 }
466
467 return true;
468 }
469
SetStringVector(const StringVector & value)470 bool XMLElement::SetStringVector(const StringVector& value)
471 {
472 if (!RemoveChildren("string"))
473 return false;
474
475 for (StringVector::ConstIterator i = value.Begin(); i != value.End(); ++i)
476 {
477 XMLElement stringElem = CreateChild("string");
478 if (!stringElem)
479 return false;
480 stringElem.SetAttribute("value", *i);
481 }
482
483 return true;
484 }
485
SetVariantMap(const VariantMap & value)486 bool XMLElement::SetVariantMap(const VariantMap& value)
487 {
488 if (!RemoveChildren("variant"))
489 return false;
490
491 for (VariantMap::ConstIterator i = value.Begin(); i != value.End(); ++i)
492 {
493 XMLElement variantElem = CreateChild("variant");
494 if (!variantElem)
495 return false;
496 variantElem.SetUInt("hash", i->first_.Value());
497 variantElem.SetVariant(i->second_);
498 }
499
500 return true;
501 }
502
SetVector2(const String & name,const Vector2 & value)503 bool XMLElement::SetVector2(const String& name, const Vector2& value)
504 {
505 return SetAttribute(name, value.ToString());
506 }
507
SetVector3(const String & name,const Vector3 & value)508 bool XMLElement::SetVector3(const String& name, const Vector3& value)
509 {
510 return SetAttribute(name, value.ToString());
511 }
512
SetVector4(const String & name,const Vector4 & value)513 bool XMLElement::SetVector4(const String& name, const Vector4& value)
514 {
515 return SetAttribute(name, value.ToString());
516 }
517
SetVectorVariant(const String & name,const Variant & value)518 bool XMLElement::SetVectorVariant(const String& name, const Variant& value)
519 {
520 VariantType type = value.GetType();
521 if (type == VAR_FLOAT || type == VAR_VECTOR2 || type == VAR_VECTOR3 || type == VAR_VECTOR4 || type == VAR_MATRIX3 ||
522 type == VAR_MATRIX3X4 || type == VAR_MATRIX4)
523 return SetAttribute(name, value.ToString());
524 else
525 return false;
526 }
527
SetMatrix3(const String & name,const Matrix3 & value)528 bool XMLElement::SetMatrix3(const String& name, const Matrix3& value)
529 {
530 return SetAttribute(name, value.ToString());
531 }
532
SetMatrix3x4(const String & name,const Matrix3x4 & value)533 bool XMLElement::SetMatrix3x4(const String& name, const Matrix3x4& value)
534 {
535 return SetAttribute(name, value.ToString());
536 }
537
SetMatrix4(const String & name,const Matrix4 & value)538 bool XMLElement::SetMatrix4(const String& name, const Matrix4& value)
539 {
540 return SetAttribute(name, value.ToString());
541 }
542
IsNull() const543 bool XMLElement::IsNull() const
544 {
545 return !NotNull();
546 }
547
NotNull() const548 bool XMLElement::NotNull() const
549 {
550 return node_ || (xpathNode_ && !xpathNode_->operator !());
551 }
552
operator bool() const553 XMLElement::operator bool() const
554 {
555 return NotNull();
556 }
557
GetName() const558 String XMLElement::GetName() const
559 {
560 if ((!file_ || !node_) && !xpathNode_)
561 return String();
562
563 // If xpath_node contains just attribute, return its name instead
564 if (xpathNode_ && xpathNode_->attribute())
565 return String(xpathNode_->attribute().name());
566
567 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
568 return String(node.name());
569 }
570
HasChild(const String & name) const571 bool XMLElement::HasChild(const String& name) const
572 {
573 return HasChild(name.CString());
574 }
575
HasChild(const char * name) const576 bool XMLElement::HasChild(const char* name) const
577 {
578 if (!file_ || (!node_ && !xpathNode_))
579 return false;
580
581 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
582 return !node.child(name).empty();
583 }
584
GetChild(const String & name) const585 XMLElement XMLElement::GetChild(const String& name) const
586 {
587 return GetChild(name.CString());
588 }
589
GetChild(const char * name) const590 XMLElement XMLElement::GetChild(const char* name) const
591 {
592 if (!file_ || (!node_ && !xpathNode_))
593 return XMLElement();
594
595 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
596 if (!String::CStringLength(name))
597 return XMLElement(file_, node.first_child().internal_object());
598 else
599 return XMLElement(file_, node.child(name).internal_object());
600 }
601
GetNext(const String & name) const602 XMLElement XMLElement::GetNext(const String& name) const
603 {
604 return GetNext(name.CString());
605 }
606
GetNext(const char * name) const607 XMLElement XMLElement::GetNext(const char* name) const
608 {
609 if (!file_ || (!node_ && !xpathNode_))
610 return XMLElement();
611
612 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
613 if (!String::CStringLength(name))
614 return XMLElement(file_, node.next_sibling().internal_object());
615 else
616 return XMLElement(file_, node.next_sibling(name).internal_object());
617 }
618
GetParent() const619 XMLElement XMLElement::GetParent() const
620 {
621 if (!file_ || (!node_ && !xpathNode_))
622 return XMLElement();
623
624 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
625 return XMLElement(file_, node.parent().internal_object());
626 }
627
GetNumAttributes() const628 unsigned XMLElement::GetNumAttributes() const
629 {
630 if (!file_ || (!node_ && !xpathNode_))
631 return 0;
632
633 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
634 unsigned ret = 0;
635
636 pugi::xml_attribute attr = node.first_attribute();
637 while (!attr.empty())
638 {
639 ++ret;
640 attr = attr.next_attribute();
641 }
642
643 return ret;
644 }
645
HasAttribute(const String & name) const646 bool XMLElement::HasAttribute(const String& name) const
647 {
648 return HasAttribute(name.CString());
649 }
650
HasAttribute(const char * name) const651 bool XMLElement::HasAttribute(const char* name) const
652 {
653 if (!file_ || (!node_ && !xpathNode_))
654 return false;
655
656 // If xpath_node contains just attribute, check against it
657 if (xpathNode_ && xpathNode_->attribute())
658 return String(xpathNode_->attribute().name()) == name;
659
660 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
661 return !node.attribute(name).empty();
662 }
663
GetValue() const664 String XMLElement::GetValue() const
665 {
666 if (!file_ || (!node_ && !xpathNode_))
667 return String::EMPTY;
668
669 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
670 return String(node.child_value());
671 }
672
GetAttribute(const String & name) const673 String XMLElement::GetAttribute(const String& name) const
674 {
675 return String(GetAttributeCString(name.CString()));
676 }
677
GetAttribute(const char * name) const678 String XMLElement::GetAttribute(const char* name) const
679 {
680 return String(GetAttributeCString(name));
681 }
682
GetAttributeCString(const char * name) const683 const char* XMLElement::GetAttributeCString(const char* name) const
684 {
685 if (!file_ || (!node_ && !xpathNode_))
686 return 0;
687
688 // If xpath_node contains just attribute, return it regardless of the specified name
689 if (xpathNode_ && xpathNode_->attribute())
690 return xpathNode_->attribute().value();
691
692 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
693 return node.attribute(name).value();
694 }
695
GetAttributeLower(const String & name) const696 String XMLElement::GetAttributeLower(const String& name) const
697 {
698 return GetAttribute(name).ToLower();
699 }
700
GetAttributeLower(const char * name) const701 String XMLElement::GetAttributeLower(const char* name) const
702 {
703 return String(GetAttribute(name)).ToLower();
704 }
705
GetAttributeUpper(const String & name) const706 String XMLElement::GetAttributeUpper(const String& name) const
707 {
708 return GetAttribute(name).ToUpper();
709 }
710
GetAttributeUpper(const char * name) const711 String XMLElement::GetAttributeUpper(const char* name) const
712 {
713 return String(GetAttribute(name)).ToUpper();
714 }
715
GetAttributeNames() const716 Vector<String> XMLElement::GetAttributeNames() const
717 {
718 if (!file_ || (!node_ && !xpathNode_))
719 return Vector<String>();
720
721 const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
722 Vector<String> ret;
723
724 pugi::xml_attribute attr = node.first_attribute();
725 while (!attr.empty())
726 {
727 ret.Push(String(attr.name()));
728 attr = attr.next_attribute();
729 }
730
731 return ret;
732 }
733
GetBool(const String & name) const734 bool XMLElement::GetBool(const String& name) const
735 {
736 return ToBool(GetAttribute(name));
737 }
738
GetBoundingBox() const739 BoundingBox XMLElement::GetBoundingBox() const
740 {
741 BoundingBox ret;
742
743 ret.min_ = GetVector3("min");
744 ret.max_ = GetVector3("max");
745 return ret;
746 }
747
GetBuffer(const String & name) const748 PODVector<unsigned char> XMLElement::GetBuffer(const String& name) const
749 {
750 PODVector<unsigned char> ret;
751 StringToBuffer(ret, GetAttribute(name));
752 return ret;
753 }
754
GetBuffer(const String & name,void * dest,unsigned size) const755 bool XMLElement::GetBuffer(const String& name, void* dest, unsigned size) const
756 {
757 Vector<String> bytes = GetAttribute(name).Split(' ');
758 if (size < bytes.Size())
759 return false;
760
761 unsigned char* destBytes = (unsigned char*)dest;
762 for (unsigned i = 0; i < bytes.Size(); ++i)
763 destBytes[i] = (unsigned char)ToInt(bytes[i]);
764 return true;
765 }
766
GetColor(const String & name) const767 Color XMLElement::GetColor(const String& name) const
768 {
769 return ToColor(GetAttribute(name));
770 }
771
GetFloat(const String & name) const772 float XMLElement::GetFloat(const String& name) const
773 {
774 return ToFloat(GetAttribute(name));
775 }
776
GetDouble(const String & name) const777 double XMLElement::GetDouble(const String& name) const
778 {
779 return ToDouble(GetAttribute(name));
780 }
781
GetUInt(const String & name) const782 unsigned XMLElement::GetUInt(const String& name) const
783 {
784 return ToUInt(GetAttribute(name));
785 }
786
GetInt(const String & name) const787 int XMLElement::GetInt(const String& name) const
788 {
789 return ToInt(GetAttribute(name));
790 }
791
GetUInt64(const String & name) const792 unsigned long long XMLElement::GetUInt64(const String& name) const
793 {
794 return ToUInt64(GetAttribute(name));
795 }
796
GetInt64(const String & name) const797 long long XMLElement::GetInt64(const String& name) const
798 {
799 return ToInt64(GetAttribute(name));
800 }
801
GetIntRect(const String & name) const802 IntRect XMLElement::GetIntRect(const String& name) const
803 {
804 return ToIntRect(GetAttribute(name));
805 }
806
GetIntVector2(const String & name) const807 IntVector2 XMLElement::GetIntVector2(const String& name) const
808 {
809 return ToIntVector2(GetAttribute(name));
810 }
811
GetIntVector3(const String & name) const812 IntVector3 XMLElement::GetIntVector3(const String& name) const
813 {
814 return ToIntVector3(GetAttribute(name));
815 }
816
GetQuaternion(const String & name) const817 Quaternion XMLElement::GetQuaternion(const String& name) const
818 {
819 return ToQuaternion(GetAttribute(name));
820 }
821
GetRect(const String & name) const822 Rect XMLElement::GetRect(const String& name) const
823 {
824 return ToRect(GetAttribute(name));
825 }
826
GetVariant() const827 Variant XMLElement::GetVariant() const
828 {
829 VariantType type = Variant::GetTypeFromName(GetAttribute("type"));
830 return GetVariantValue(type);
831 }
832
GetVariantValue(VariantType type) const833 Variant XMLElement::GetVariantValue(VariantType type) const
834 {
835 Variant ret;
836
837 if (type == VAR_RESOURCEREF)
838 ret = GetResourceRef();
839 else if (type == VAR_RESOURCEREFLIST)
840 ret = GetResourceRefList();
841 else if (type == VAR_VARIANTVECTOR)
842 ret = GetVariantVector();
843 else if (type == VAR_STRINGVECTOR)
844 ret = GetStringVector();
845 else if (type == VAR_VARIANTMAP)
846 ret = GetVariantMap();
847 else
848 ret.FromString(type, GetAttributeCString("value"));
849
850 return ret;
851 }
852
GetResourceRef() const853 ResourceRef XMLElement::GetResourceRef() const
854 {
855 ResourceRef ret;
856
857 Vector<String> values = GetAttribute("value").Split(';');
858 if (values.Size() == 2)
859 {
860 ret.type_ = values[0];
861 ret.name_ = values[1];
862 }
863
864 return ret;
865 }
866
GetResourceRefList() const867 ResourceRefList XMLElement::GetResourceRefList() const
868 {
869 ResourceRefList ret;
870
871 Vector<String> values = GetAttribute("value").Split(';', true);
872 if (values.Size() >= 1)
873 {
874 ret.type_ = values[0];
875 ret.names_.Resize(values.Size() - 1);
876 for (unsigned i = 1; i < values.Size(); ++i)
877 ret.names_[i - 1] = values[i];
878 }
879
880 return ret;
881 }
882
GetVariantVector() const883 VariantVector XMLElement::GetVariantVector() const
884 {
885 VariantVector ret;
886
887 XMLElement variantElem = GetChild("variant");
888 while (variantElem)
889 {
890 ret.Push(variantElem.GetVariant());
891 variantElem = variantElem.GetNext("variant");
892 }
893
894 return ret;
895 }
896
GetStringVector() const897 StringVector XMLElement::GetStringVector() const
898 {
899 StringVector ret;
900
901 XMLElement stringElem = GetChild("string");
902 while (stringElem)
903 {
904 ret.Push(stringElem.GetAttributeCString("value"));
905 stringElem = stringElem.GetNext("string");
906 }
907
908 return ret;
909 }
910
GetVariantMap() const911 VariantMap XMLElement::GetVariantMap() const
912 {
913 VariantMap ret;
914
915 XMLElement variantElem = GetChild("variant");
916 while (variantElem)
917 {
918 // If this is a manually edited map, user can not be expected to calculate hashes manually. Also accept "name" attribute
919 if (variantElem.HasAttribute("name"))
920 ret[StringHash(variantElem.GetAttribute("name"))] = variantElem.GetVariant();
921 else if (variantElem.HasAttribute("hash"))
922 ret[StringHash(variantElem.GetUInt("hash"))] = variantElem.GetVariant();
923
924 variantElem = variantElem.GetNext("variant");
925 }
926
927 return ret;
928 }
929
GetVector2(const String & name) const930 Vector2 XMLElement::GetVector2(const String& name) const
931 {
932 return ToVector2(GetAttribute(name));
933 }
934
GetVector3(const String & name) const935 Vector3 XMLElement::GetVector3(const String& name) const
936 {
937 return ToVector3(GetAttribute(name));
938 }
939
GetVector4(const String & name) const940 Vector4 XMLElement::GetVector4(const String& name) const
941 {
942 return ToVector4(GetAttribute(name));
943 }
944
GetVector(const String & name) const945 Vector4 XMLElement::GetVector(const String& name) const
946 {
947 return ToVector4(GetAttribute(name), true);
948 }
949
GetVectorVariant(const String & name) const950 Variant XMLElement::GetVectorVariant(const String& name) const
951 {
952 return ToVectorVariant(GetAttribute(name));
953 }
954
GetMatrix3(const String & name) const955 Matrix3 XMLElement::GetMatrix3(const String& name) const
956 {
957 return ToMatrix3(GetAttribute(name));
958 }
959
GetMatrix3x4(const String & name) const960 Matrix3x4 XMLElement::GetMatrix3x4(const String& name) const
961 {
962 return ToMatrix3x4(GetAttribute(name));
963 }
964
GetMatrix4(const String & name) const965 Matrix4 XMLElement::GetMatrix4(const String& name) const
966 {
967 return ToMatrix4(GetAttribute(name));
968 }
969
GetFile() const970 XMLFile* XMLElement::GetFile() const
971 {
972 return file_;
973 }
974
NextResult() const975 XMLElement XMLElement::NextResult() const
976 {
977 if (!xpathResultSet_ || !xpathNode_)
978 return XMLElement();
979
980 return xpathResultSet_->operator [](++xpathResultIndex_);
981 }
982
XPathResultSet()983 XPathResultSet::XPathResultSet() :
984 resultSet_(0)
985 {
986 }
987
XPathResultSet(XMLFile * file,pugi::xpath_node_set * resultSet)988 XPathResultSet::XPathResultSet(XMLFile* file, pugi::xpath_node_set* resultSet) :
989 file_(file),
990 resultSet_(resultSet ? new pugi::xpath_node_set(resultSet->begin(), resultSet->end()) : 0)
991 {
992 // Sort the node set in forward document order
993 if (resultSet_)
994 resultSet_->sort();
995 }
996
XPathResultSet(const XPathResultSet & rhs)997 XPathResultSet::XPathResultSet(const XPathResultSet& rhs) :
998 file_(rhs.file_),
999 resultSet_(rhs.resultSet_ ? new pugi::xpath_node_set(rhs.resultSet_->begin(), rhs.resultSet_->end()) : 0)
1000 {
1001 }
1002
~XPathResultSet()1003 XPathResultSet::~XPathResultSet()
1004 {
1005 delete resultSet_;
1006 resultSet_ = 0;
1007 }
1008
operator =(const XPathResultSet & rhs)1009 XPathResultSet& XPathResultSet::operator =(const XPathResultSet& rhs)
1010 {
1011 file_ = rhs.file_;
1012 resultSet_ = rhs.resultSet_ ? new pugi::xpath_node_set(rhs.resultSet_->begin(), rhs.resultSet_->end()) : 0;
1013 return *this;
1014 }
1015
operator [](unsigned index) const1016 XMLElement XPathResultSet::operator [](unsigned index) const
1017 {
1018 if (!resultSet_)
1019 URHO3D_LOGERRORF(
1020 "Could not return result at index: %u. Most probably this is caused by the XPathResultSet not being stored in a lhs variable.",
1021 index);
1022
1023 return resultSet_ && index < Size() ? XMLElement(file_, this, &resultSet_->operator [](index), index) : XMLElement();
1024 }
1025
FirstResult()1026 XMLElement XPathResultSet::FirstResult()
1027 {
1028 return operator [](0);
1029 }
1030
Size() const1031 unsigned XPathResultSet::Size() const
1032 {
1033 return resultSet_ ? (unsigned)resultSet_->size() : 0;
1034 }
1035
Empty() const1036 bool XPathResultSet::Empty() const
1037 {
1038 return resultSet_ ? resultSet_->empty() : true;
1039 }
1040
XPathQuery()1041 XPathQuery::XPathQuery()
1042 {
1043 }
1044
XPathQuery(const String & queryString,const String & variableString)1045 XPathQuery::XPathQuery(const String& queryString, const String& variableString)
1046 {
1047 SetQuery(queryString, variableString);
1048 }
1049
~XPathQuery()1050 XPathQuery::~XPathQuery()
1051 {
1052 }
1053
Bind()1054 void XPathQuery::Bind()
1055 {
1056 // Delete previous query object and create a new one binding it with variable set
1057 query_ = new pugi::xpath_query(queryString_.CString(), variables_.Get());
1058 }
1059
SetVariable(const String & name,bool value)1060 bool XPathQuery::SetVariable(const String& name, bool value)
1061 {
1062 if (!variables_)
1063 variables_ = new pugi::xpath_variable_set();
1064 return variables_->set(name.CString(), value);
1065 }
1066
SetVariable(const String & name,float value)1067 bool XPathQuery::SetVariable(const String& name, float value)
1068 {
1069 if (!variables_)
1070 variables_ = new pugi::xpath_variable_set();
1071 return variables_->set(name.CString(), value);
1072 }
1073
SetVariable(const String & name,const String & value)1074 bool XPathQuery::SetVariable(const String& name, const String& value)
1075 {
1076 return SetVariable(name.CString(), value.CString());
1077 }
1078
SetVariable(const char * name,const char * value)1079 bool XPathQuery::SetVariable(const char* name, const char* value)
1080 {
1081 if (!variables_)
1082 variables_ = new pugi::xpath_variable_set();
1083 return variables_->set(name, value);
1084 }
1085
SetVariable(const String & name,const XPathResultSet & value)1086 bool XPathQuery::SetVariable(const String& name, const XPathResultSet& value)
1087 {
1088 if (!variables_)
1089 variables_ = new pugi::xpath_variable_set();
1090
1091 pugi::xpath_node_set* nodeSet = value.GetXPathNodeSet();
1092 if (!nodeSet)
1093 return false;
1094
1095 return variables_->set(name.CString(), *nodeSet);
1096 }
1097
SetQuery(const String & queryString,const String & variableString,bool bind)1098 bool XPathQuery::SetQuery(const String& queryString, const String& variableString, bool bind)
1099 {
1100 if (!variableString.Empty())
1101 {
1102 Clear();
1103 variables_ = new pugi::xpath_variable_set();
1104
1105 // Parse the variable string having format "name1:type1,name2:type2,..." where type is one of "Bool", "Float", "String", "ResultSet"
1106 Vector<String> vars = variableString.Split(',');
1107 for (Vector<String>::ConstIterator i = vars.Begin(); i != vars.End(); ++i)
1108 {
1109 Vector<String> tokens = i->Trimmed().Split(':');
1110 if (tokens.Size() != 2)
1111 continue;
1112
1113 pugi::xpath_value_type type;
1114 if (tokens[1] == "Bool")
1115 type = pugi::xpath_type_boolean;
1116 else if (tokens[1] == "Float")
1117 type = pugi::xpath_type_number;
1118 else if (tokens[1] == "String")
1119 type = pugi::xpath_type_string;
1120 else if (tokens[1] == "ResultSet")
1121 type = pugi::xpath_type_node_set;
1122 else
1123 return false;
1124
1125 if (!variables_->add(tokens[0].CString(), type))
1126 return false;
1127 }
1128 }
1129
1130 queryString_ = queryString;
1131
1132 if (bind)
1133 Bind();
1134
1135 return true;
1136 }
1137
Clear()1138 void XPathQuery::Clear()
1139 {
1140 queryString_.Clear();
1141
1142 variables_.Reset();
1143 query_.Reset();
1144 }
1145
EvaluateToBool(XMLElement element) const1146 bool XPathQuery::EvaluateToBool(XMLElement element) const
1147 {
1148 if (!query_ || ((!element.GetFile() || !element.GetNode()) && !element.GetXPathNode()))
1149 return false;
1150
1151 const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node() : pugi::xml_node(element.GetNode());
1152 return query_->evaluate_boolean(node);
1153 }
1154
EvaluateToFloat(XMLElement element) const1155 float XPathQuery::EvaluateToFloat(XMLElement element) const
1156 {
1157 if (!query_ || ((!element.GetFile() || !element.GetNode()) && !element.GetXPathNode()))
1158 return 0.0f;
1159
1160 const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node() : pugi::xml_node(element.GetNode());
1161 return (float)query_->evaluate_number(node);
1162 }
1163
EvaluateToString(XMLElement element) const1164 String XPathQuery::EvaluateToString(XMLElement element) const
1165 {
1166 if (!query_ || ((!element.GetFile() || !element.GetNode()) && !element.GetXPathNode()))
1167 return String::EMPTY;
1168
1169 const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node() : pugi::xml_node(element.GetNode());
1170 String result;
1171 // First call get the size
1172 result.Reserve((unsigned)query_->evaluate_string(0, 0, node));
1173 // Second call get the actual string
1174 query_->evaluate_string(const_cast<pugi::char_t*>(result.CString()), result.Capacity(), node);
1175 return result;
1176 }
1177
Evaluate(XMLElement element) const1178 XPathResultSet XPathQuery::Evaluate(XMLElement element) const
1179 {
1180 if (!query_ || ((!element.GetFile() || !element.GetNode()) && !element.GetXPathNode()))
1181 return XPathResultSet();
1182
1183 const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node() : pugi::xml_node(element.GetNode());
1184 pugi::xpath_node_set result = query_->evaluate_node_set(node);
1185 return XPathResultSet(element.GetFile(), &result);
1186 }
1187
1188 }
1189