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