ソースを参照

Fixed XPath query result integration with existing XMLElement class (XMLElement constructed from XPath query result should be abled to traverse up/down document tree as if it is a 'normal' XMLElement). Added feature to auto-add variable into XPath variables set based on pre-formatted variable string. Modified 3rd-party PugiXML library to have a proper xpath_node copy-constructor. Fixed UI.h to build under GCC.

Wei Tjong Yao 12 年 前
コミット
832ef7ddfd

+ 4 - 2
Docs/ScriptAPI.dox

@@ -1231,7 +1231,9 @@ Methods:<br>
 - bool RemoveChild(const String&)
 - bool RemoveChild(const String&)
 - bool RemoveChildren(const String& arg0 = String ( ))
 - bool RemoveChildren(const String& arg0 = String ( ))
 - XMLElement SelectSingle(const String&)
 - XMLElement SelectSingle(const String&)
+- XMLElement SelectSinglePrepared(const XPathQuery&)
 - XPathResultSet Select(const String&)
 - XPathResultSet Select(const String&)
+- XPathResultSet SelectPrepared(const XPathQuery&)
 - bool SetAttribute(const String&, const String&)
 - bool SetAttribute(const String&, const String&)
 - bool SetAttribute(const String&)
 - bool SetAttribute(const String&)
 - bool SetBool(const String&, bool)
 - bool SetBool(const String&, bool)
@@ -1299,8 +1301,8 @@ Methods:<br>
 - bool SetVariable(const String&, float)
 - bool SetVariable(const String&, float)
 - bool SetVariable(const String&, const String&)
 - bool SetVariable(const String&, const String&)
 - bool SetVariable(const String&, const XPathResultSet&)
 - bool SetVariable(const String&, const XPathResultSet&)
-- void RemoveVariables()
-- void SetQuery(const String&, bool arg1 = false)
+- bool SetQuery(const String&, const String& arg1 = String ( ), bool arg2 = true)
+- void Clear()
 - bool EvaluateToBool(XMLElement)
 - bool EvaluateToBool(XMLElement)
 - float EvaluateToFloat(XMLElement)
 - float EvaluateToFloat(XMLElement)
 - String EvaluateToString(XMLElement)
 - String EvaluateToString(XMLElement)

+ 7 - 5
Engine/Engine/ResourceAPI.cpp

@@ -201,9 +201,9 @@ static void ConstructXPathQuery(XPathQuery* ptr)
     new(ptr) XPathQuery();
     new(ptr) XPathQuery();
 }
 }
 
 
-static void ConstructXPathQueryWithString(const String& queryString, XPathQuery* ptr)
+static void ConstructXPathQueryWithString(const String& queryString, const String& variableString, XPathQuery* ptr)
 {
 {
-    new(ptr) XPathQuery(queryString);
+    new(ptr) XPathQuery(queryString, variableString);
 }
 }
 
 
 static void DestructXPathQuery(XPathQuery* ptr)
 static void DestructXPathQuery(XPathQuery* ptr)
@@ -226,7 +226,9 @@ static void RegisterXMLElement(asIScriptEngine* engine)
     engine->RegisterObjectMethod("XMLElement", "bool RemoveChild(const String&in)", asMETHODPR(XMLElement, RemoveChild, (const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool RemoveChild(const String&in)", asMETHODPR(XMLElement, RemoveChild, (const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool RemoveChildren(const String&in name = String())", asMETHODPR(XMLElement, RemoveChildren, (const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool RemoveChildren(const String&in name = String())", asMETHODPR(XMLElement, RemoveChildren, (const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "XMLElement SelectSingle(const String&in)", asMETHOD(XMLElement, SelectSingle), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "XMLElement SelectSingle(const String&in)", asMETHOD(XMLElement, SelectSingle), asCALL_THISCALL);
+    engine->RegisterObjectMethod("XMLElement", "XMLElement SelectSinglePrepared(const XPathQuery&in)", asMETHOD(XMLElement, SelectSinglePrepared), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "XPathResultSet Select(const String&in)", asMETHOD(XMLElement, Select), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "XPathResultSet Select(const String&in)", asMETHOD(XMLElement, Select), asCALL_THISCALL);
+    engine->RegisterObjectMethod("XMLElement", "XPathResultSet SelectPrepared(const XPathQuery&in)", asMETHOD(XMLElement, SelectPrepared), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool SetAttribute(const String&in, const String&in)", asMETHODPR(XMLElement, SetAttribute, (const String&, const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool SetAttribute(const String&in, const String&in)", asMETHODPR(XMLElement, SetAttribute, (const String&, const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool SetAttribute(const String&in)", asMETHODPR(XMLElement, SetAttribute, (const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool SetAttribute(const String&in)", asMETHODPR(XMLElement, SetAttribute, (const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool SetBool(const String&in, bool)", asMETHOD(XMLElement, SetBool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XMLElement", "bool SetBool(const String&in, bool)", asMETHOD(XMLElement, SetBool), asCALL_THISCALL);
@@ -285,15 +287,15 @@ static void RegisterXMLElement(asIScriptEngine* engine)
     engine->RegisterObjectMethod("XPathResultSet", "bool get_empty()", asMETHOD(XPathResultSet, Empty), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathResultSet", "bool get_empty()", asMETHOD(XPathResultSet, Empty), asCALL_THISCALL);
 
 
     engine->RegisterObjectBehaviour("XPathQuery", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructXPathQuery), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("XPathQuery", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructXPathQuery), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectBehaviour("XPathQuery", asBEHAVE_CONSTRUCT, "void f(const String&in)", asFUNCTION(ConstructXPathQueryWithString), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("XPathQuery", asBEHAVE_CONSTRUCT, "void f(const String&in, const String& arg1 = String())", asFUNCTION(ConstructXPathQueryWithString), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("XPathQuery", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructXPathQuery), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("XPathQuery", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructXPathQuery), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("XPathQuery", "void Bind()", asMETHOD(XPathQuery, Bind), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "void Bind()", asMETHOD(XPathQuery, Bind), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, bool)", asMETHODPR(XPathQuery, SetVariable, (const String&, bool), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, bool)", asMETHODPR(XPathQuery, SetVariable, (const String&, bool), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, float)", asMETHODPR(XPathQuery, SetVariable, (const String&, float), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, float)", asMETHODPR(XPathQuery, SetVariable, (const String&, float), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, const String&in)", asMETHODPR(XPathQuery, SetVariable, (const String&, const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, const String&in)", asMETHODPR(XPathQuery, SetVariable, (const String&, const String&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, const XPathResultSet&in)", asMETHODPR(XPathQuery, SetVariable, (const String&, const XPathResultSet&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool SetVariable(const String&in, const XPathResultSet&in)", asMETHODPR(XPathQuery, SetVariable, (const String&, const XPathResultSet&), bool), asCALL_THISCALL);
-    engine->RegisterObjectMethod("XPathQuery", "void RemoveVariables()", asMETHOD(XPathQuery, RemoveVariables), asCALL_THISCALL);
-    engine->RegisterObjectMethod("XPathQuery", "void SetQuery(const String&, bool arg1 = false)", asMETHOD(XPathQuery, SetQuery), asCALL_THISCALL);
+    engine->RegisterObjectMethod("XPathQuery", "bool SetQuery(const String&, const String& arg1 = String(), bool arg2 = true)", asMETHOD(XPathQuery, SetQuery), asCALL_THISCALL);
+    engine->RegisterObjectMethod("XPathQuery", "void Clear()", asMETHOD(XPathQuery, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool EvaluateToBool(XMLElement)", asMETHOD(XPathQuery, EvaluateToBool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "bool EvaluateToBool(XMLElement)", asMETHOD(XPathQuery, EvaluateToBool), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "float EvaluateToFloat(XMLElement)", asMETHOD(XPathQuery, EvaluateToFloat), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "float EvaluateToFloat(XMLElement)", asMETHOD(XPathQuery, EvaluateToFloat), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "String EvaluateToString(XMLElement)", asMETHOD(XPathQuery, EvaluateToString), asCALL_THISCALL);
     engine->RegisterObjectMethod("XPathQuery", "String EvaluateToString(XMLElement)", asMETHOD(XPathQuery, EvaluateToString), asCALL_THISCALL);

+ 111 - 35
Engine/Resource/XMLElement.cpp

@@ -52,10 +52,11 @@ XMLElement::XMLElement(XMLFile* file, pugi::xml_node_struct* node) :
 {
 {
 }
 }
 
 
-XMLElement::XMLElement(const XPathResultSet* resultSet, const pugi::xpath_node* xpathNode, unsigned xpathResultIndex) :
+XMLElement::XMLElement(XMLFile* file, const XPathResultSet* resultSet, const pugi::xpath_node* xpathNode, unsigned xpathResultIndex) :
+    file_(file),
     node_(0),
     node_(0),
     xpathResultSet_(resultSet),
     xpathResultSet_(resultSet),
-    xpathNode_(resultSet ? xpathNode : (xpathNode ? new pugi::xpath_node(xpathNode->attribute(), xpathNode->node()) : 0)),
+    xpathNode_(resultSet ? xpathNode : (xpathNode ? new pugi::xpath_node(*xpathNode) : 0)),
     xpathResultIndex_(xpathResultIndex)
     xpathResultIndex_(xpathResultIndex)
 {
 {
 }
 }
@@ -64,7 +65,7 @@ XMLElement::XMLElement(const XMLElement& rhs) :
     file_(rhs.file_),
     file_(rhs.file_),
     node_(rhs.node_),
     node_(rhs.node_),
     xpathResultSet_(rhs.xpathResultSet_),
     xpathResultSet_(rhs.xpathResultSet_),
-    xpathNode_(rhs.xpathResultSet_ ? rhs.xpathNode_ : (rhs.xpathNode_ ? new pugi::xpath_node(rhs.xpathNode_->attribute(), rhs.xpathNode_->node()) : 0)),
+    xpathNode_(rhs.xpathResultSet_ ? rhs.xpathNode_ : (rhs.xpathNode_ ? new pugi::xpath_node(*rhs.xpathNode_) : 0)),
     xpathResultIndex_(rhs.xpathResultIndex_)
     xpathResultIndex_(rhs.xpathResultIndex_)
 {
 {
 }
 }
@@ -84,7 +85,7 @@ XMLElement& XMLElement::operator = (const XMLElement& rhs)
     file_ = rhs.file_;
     file_ = rhs.file_;
     node_ = rhs.node_;
     node_ = rhs.node_;
     xpathResultSet_ = rhs.xpathResultSet_;
     xpathResultSet_ = rhs.xpathResultSet_;
-    xpathNode_ = rhs.xpathResultSet_ ? rhs.xpathNode_ : (rhs.xpathNode_ ? new pugi::xpath_node(rhs.xpathNode_->attribute(), rhs.xpathNode_->node()) : 0);
+    xpathNode_ = rhs.xpathResultSet_ ? rhs.xpathNode_ : (rhs.xpathNode_ ? new pugi::xpath_node(*rhs.xpathNode_) : 0);
     xpathResultIndex_ = rhs.xpathResultIndex_;
     xpathResultIndex_ = rhs.xpathResultIndex_;
     return *this;
     return *this;
 }
 }
@@ -96,7 +97,7 @@ XMLElement XMLElement::CreateChild(const String& name)
 
 
 XMLElement XMLElement::CreateChild(const char* name)
 XMLElement XMLElement::CreateChild(const char* name)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return XMLElement();
         return XMLElement();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
@@ -106,7 +107,7 @@ XMLElement XMLElement::CreateChild(const char* name)
 
 
 bool XMLElement::RemoveChild(const XMLElement& element)
 bool XMLElement::RemoveChild(const XMLElement& element)
 {
 {
-    if ((!element.node_ && !element.xpathNode_) || (!node_ && !xpathNode_))
+    if (!element.file_ || (!element.node_ && !element.xpathNode_) || !file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
@@ -121,7 +122,7 @@ bool XMLElement::RemoveChild(const String& name)
 
 
 bool XMLElement::RemoveChild(const char* name)
 bool XMLElement::RemoveChild(const char* name)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
@@ -165,22 +166,42 @@ bool XMLElement::RemoveChildren(const char* name)
 
 
 XMLElement XMLElement::SelectSingle(const String& query, pugi::xpath_variable_set* variables)
 XMLElement XMLElement::SelectSingle(const String& query, pugi::xpath_variable_set* variables)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return XMLElement();
         return XMLElement();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     pugi::xpath_node result = node.select_single_node(query.CString(), variables);
     pugi::xpath_node result = node.select_single_node(query.CString(), variables);
-    return XMLElement(0, &result, 0);
+    return XMLElement(file_, 0, &result, 0);
+}
+
+XMLElement XMLElement::SelectSinglePrepared(const XPathQuery& query)
+{
+    if (!file_ || (!node_ && !xpathNode_ && !query.GetXPathQuery()))
+        return XMLElement();
+
+    const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
+    pugi::xpath_node result = node.select_single_node(*query.GetXPathQuery());
+    return XMLElement(file_, 0, &result, 0);
 }
 }
 
 
 XPathResultSet XMLElement::Select(const String& query, pugi::xpath_variable_set* variables)
 XPathResultSet XMLElement::Select(const String& query, pugi::xpath_variable_set* variables)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return XPathResultSet();
         return XPathResultSet();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
     pugi::xpath_node_set result = node.select_nodes(query.CString(), variables);
     pugi::xpath_node_set result = node.select_nodes(query.CString(), variables);
-    return XPathResultSet(&result);
+    return XPathResultSet(file_, &result);
+}
+
+XPathResultSet XMLElement::SelectPrepared(const XPathQuery& query)
+{
+    if (!file_ || (!node_ && !xpathNode_ && query.GetXPathQuery()))
+        return XPathResultSet();
+
+    const pugi::xml_node& node = xpathNode_ ? xpathNode_->node(): pugi::xml_node(node_);
+    pugi::xpath_node_set result = node.select_nodes(*query.GetXPathQuery());
+    return XPathResultSet(file_, &result);
 }
 }
 
 
 bool XMLElement::SetAttribute(const String& name, const String& value)
 bool XMLElement::SetAttribute(const String& name, const String& value)
@@ -190,7 +211,7 @@ bool XMLElement::SetAttribute(const String& name, const String& value)
 
 
 bool XMLElement::SetAttribute(const char* name, const char* value)
 bool XMLElement::SetAttribute(const char* name, const char* value)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     // If xpath_node contains just attribute, set its value regardless of the specified name
     // If xpath_node contains just attribute, set its value regardless of the specified name
@@ -322,7 +343,7 @@ bool XMLElement::SetVariantValue(const Variant& value)
 
 
 bool XMLElement::SetResourceRef(const ResourceRef& value)
 bool XMLElement::SetResourceRef(const ResourceRef& value)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     // Need the context & resource cache to query for reverse hash mappings
     // Need the context & resource cache to query for reverse hash mappings
@@ -334,7 +355,7 @@ bool XMLElement::SetResourceRef(const ResourceRef& value)
 
 
 bool XMLElement::SetResourceRefList(const ResourceRefList& value)
 bool XMLElement::SetResourceRefList(const ResourceRefList& value)
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     // Need the context & resource cache to query for reverse hash mappings
     // Need the context & resource cache to query for reverse hash mappings
@@ -400,6 +421,21 @@ bool XMLElement::SetVector4(const String& name, const Vector4& value)
     return SetAttribute(name, value.ToString());
     return SetAttribute(name, value.ToString());
 }
 }
 
 
+bool XMLElement::IsNull() const
+{
+    return !NotNull();
+}
+
+bool XMLElement::NotNull() const
+{
+    return node_ || (xpathNode_ && !xpathNode_->operator !());
+}
+
+XMLElement::operator bool () const
+{
+    return NotNull();
+}
+
 String XMLElement::GetName() const
 String XMLElement::GetName() const
 {
 {
     if ((!file_ || !node_) && !xpathNode_)
     if ((!file_ || !node_) && !xpathNode_)
@@ -420,7 +456,7 @@ bool XMLElement::HasChild(const String& name) const
 
 
 bool XMLElement::HasChild(const char* name) const
 bool XMLElement::HasChild(const char* name) const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
@@ -434,7 +470,7 @@ XMLElement XMLElement::GetChild(const String& name) const
 
 
 XMLElement XMLElement::GetChild(const char* name) const
 XMLElement XMLElement::GetChild(const char* name) const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return XMLElement();
         return XMLElement();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
@@ -451,7 +487,7 @@ XMLElement XMLElement::GetNext(const String& name) const
 
 
 XMLElement XMLElement::GetNext(const char* name) const
 XMLElement XMLElement::GetNext(const char* name) const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return XMLElement();
         return XMLElement();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
@@ -463,7 +499,7 @@ XMLElement XMLElement::GetNext(const char* name) const
 
 
 XMLElement XMLElement::GetParent() const
 XMLElement XMLElement::GetParent() const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return XMLElement();
         return XMLElement();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
@@ -472,7 +508,7 @@ XMLElement XMLElement::GetParent() const
 
 
 unsigned XMLElement::GetNumAttributes() const
 unsigned XMLElement::GetNumAttributes() const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return 0;
         return 0;
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
@@ -495,7 +531,7 @@ bool XMLElement::HasAttribute(const String& name) const
 
 
 bool XMLElement::HasAttribute(const char* name) const
 bool XMLElement::HasAttribute(const char* name) const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return false;
         return false;
 
 
     // If xpath_node contains just attribute, check against it
     // If xpath_node contains just attribute, check against it
@@ -513,7 +549,7 @@ String XMLElement::GetAttribute(const String& name) const
 
 
 const char* XMLElement::GetAttribute(const char* name) const
 const char* XMLElement::GetAttribute(const char* name) const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return 0;
         return 0;
 
 
     // If xpath_node contains just attribute, return it regardless of the specified name
     // If xpath_node contains just attribute, return it regardless of the specified name
@@ -546,7 +582,7 @@ String XMLElement::GetAttributeUpper(const char* name) const
 
 
 Vector<String> XMLElement::GetAttributeNames() const
 Vector<String> XMLElement::GetAttributeNames() const
 {
 {
-    if ((!file_ || !node_) && !xpathNode_)
+    if (!file_ || (!node_ && !xpathNode_))
         return Vector<String>();
         return Vector<String>();
 
 
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
     const pugi::xml_node& node = xpathNode_ ? xpathNode_->node() : pugi::xml_node(node_);
@@ -775,7 +811,8 @@ XPathResultSet::XPathResultSet() :
 {
 {
 }
 }
 
 
-XPathResultSet::XPathResultSet(pugi::xpath_node_set* resultSet) :
+XPathResultSet::XPathResultSet(XMLFile* file, pugi::xpath_node_set* resultSet) :
+    file_(file),
     resultSet_(resultSet ? new pugi::xpath_node_set(resultSet->begin(), resultSet->end()) : 0)
     resultSet_(resultSet ? new pugi::xpath_node_set(resultSet->begin(), resultSet->end()) : 0)
 {
 {
     // Sort the node set in forward document order
     // Sort the node set in forward document order
@@ -784,6 +821,7 @@ XPathResultSet::XPathResultSet(pugi::xpath_node_set* resultSet) :
 }
 }
 
 
 XPathResultSet::XPathResultSet(const XPathResultSet& rhs) :
 XPathResultSet::XPathResultSet(const XPathResultSet& rhs) :
+    file_(rhs.file_),
     resultSet_(rhs.resultSet_ ? new pugi::xpath_node_set(rhs.resultSet_->begin(), rhs.resultSet_->end()) : 0)
     resultSet_(rhs.resultSet_ ? new pugi::xpath_node_set(rhs.resultSet_->begin(), rhs.resultSet_->end()) : 0)
 {
 {
 }
 }
@@ -796,18 +834,19 @@ XPathResultSet::~XPathResultSet()
 
 
 XPathResultSet& XPathResultSet::operator = (const XPathResultSet& rhs)
 XPathResultSet& XPathResultSet::operator = (const XPathResultSet& rhs)
 {
 {
+    file_ = rhs.file_;
     resultSet_ = rhs.resultSet_ ? new pugi::xpath_node_set(rhs.resultSet_->begin(), rhs.resultSet_->end()) : 0;
     resultSet_ = rhs.resultSet_ ? new pugi::xpath_node_set(rhs.resultSet_->begin(), rhs.resultSet_->end()) : 0;
     return *this;
     return *this;
 }
 }
 
 
 XMLElement XPathResultSet::operator[](unsigned index) const
 XMLElement XPathResultSet::operator[](unsigned index) const
 {
 {
-    return resultSet_ && index >= Size() ? XMLElement() : XMLElement(this, &resultSet_->operator [](index), index);
+    return resultSet_ && index >= Size() ? XMLElement() : XMLElement(file_, this, &resultSet_->operator [](index), index);
 }
 }
 
 
 XMLElement XPathResultSet::FirstResult()
 XMLElement XPathResultSet::FirstResult()
 {
 {
-    return Empty() ? XMLElement() : XMLElement(this, resultSet_->begin(), 0);
+    return Empty() ? XMLElement() : XMLElement(file_, this, resultSet_->begin(), 0);
 }
 }
 
 
 unsigned XPathResultSet::Size() const
 unsigned XPathResultSet::Size() const
@@ -826,11 +865,11 @@ XPathQuery::XPathQuery() :
 {
 {
 }
 }
 
 
-XPathQuery::XPathQuery(const String& queryString) :
-    queryString_(queryString),
+XPathQuery::XPathQuery(const String& queryString, const String& variableString) :
+    query_(0),
     variables_(0)
     variables_(0)
 {
 {
-    query_ = new pugi::xpath_query(queryString_.CString(), variables_);
+    SetQuery(queryString, variableString);
 }
 }
 
 
 XPathQuery::~XPathQuery()
 XPathQuery::~XPathQuery()
@@ -876,17 +915,54 @@ bool XPathQuery::SetVariable(const String& name, const XPathResultSet& value)
     return variables_->set(name.CString(), value.GetXPathNodeSet());
     return variables_->set(name.CString(), value.GetXPathNodeSet());
 }
 }
 
 
-void XPathQuery::RemoveVariables()
+bool XPathQuery::SetQuery(const String& queryString, const String& variableString, bool bind)
 {
 {
-    delete variables_;
-    variables_ = 0;
-}
+    if (!variableString.Empty())
+    {
+        Clear();
+        variables_ = new pugi::xpath_variable_set();
+
+        // Parse the variable string having format "name1:type1,name2:type2,..." where type is one of "Bool", "Float", "String", "ResultSet"
+        Vector<String> vars = variableString.Split(',');
+        for (Vector<String>::ConstIterator i = vars.Begin(); i != vars.End(); ++i)
+        {
+            Vector<String> tokens = i->Trimmed().Split(':');
+            if (tokens.Size() != 2)
+                continue;
+
+            pugi::xpath_value_type type;
+            if (tokens[1] == "Bool")
+                type = pugi::xpath_type_boolean;
+            else if (tokens[1] == "Float")
+                type = pugi::xpath_type_number;
+            else if (tokens[1] == "String")
+                type = pugi::xpath_type_string;
+            else if (tokens[1] == "ResultSet")
+                type = pugi::xpath_type_node_set;
+            else
+                return false;
+
+            if (!variables_->add(tokens[0].CString(), type))
+                return false;
+        }
+    }
 
 
-void XPathQuery::SetQuery(const String& queryString, bool bind)
-{
     queryString_ = queryString;
     queryString_ = queryString;
+
     if (bind)
     if (bind)
         Bind();
         Bind();
+
+    return true;
+}
+
+void XPathQuery::Clear()
+{
+    queryString_.Clear();
+
+    delete variables_;
+    variables_ = 0;
+    delete query_;
+    query_ = 0;
 }
 }
 
 
 bool XPathQuery::EvaluateToBool(XMLElement element) const
 bool XPathQuery::EvaluateToBool(XMLElement element) const
@@ -926,7 +1002,7 @@ XPathResultSet XPathQuery::Evaluate(XMLElement element) const
 
 
     const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node(): pugi::xml_node(element.GetNode());
     const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node(): pugi::xml_node(element.GetNode());
     pugi::xpath_node_set result = query_->evaluate_node_set(node);
     pugi::xpath_node_set result = query_->evaluate_node_set(node);
-    return XPathResultSet(&result);
+    return XPathResultSet(element.GetFile(), &result);
 }
 }
 
 
 }
 }

+ 18 - 11
Engine/Resource/XMLElement.h

@@ -40,6 +40,7 @@ namespace Urho3D
 {
 {
 
 
 class XMLFile;
 class XMLFile;
+class XPathQuery;
 class XPathResultSet;
 class XPathResultSet;
 
 
 /// Element in an XML file.
 /// Element in an XML file.
@@ -51,7 +52,7 @@ public:
     /// Construct with document and node pointers.
     /// Construct with document and node pointers.
     XMLElement(XMLFile* file, pugi::xml_node_struct* node);
     XMLElement(XMLFile* file, pugi::xml_node_struct* node);
     /// Construct from xpath query result set.
     /// Construct from xpath query result set.
-    XMLElement(const XPathResultSet* resultSet, const pugi::xpath_node* xpathNode, unsigned xpathResultIndex);
+    XMLElement(XMLFile* file, const XPathResultSet* resultSet, const pugi::xpath_node* xpathNode, unsigned xpathResultIndex);
     /// Copy-construct from another element.
     /// Copy-construct from another element.
     XMLElement(const XMLElement& rhs);
     XMLElement(const XMLElement& rhs);
     /// Destruct.
     /// Destruct.
@@ -76,8 +77,12 @@ public:
 
 
     /// Select an element/attribute using XPath query.
     /// Select an element/attribute using XPath query.
     XMLElement SelectSingle(const String& query, pugi::xpath_variable_set* variables = 0);
     XMLElement SelectSingle(const String& query, pugi::xpath_variable_set* variables = 0);
+    /// Select an element/attribute using XPath query.
+    XMLElement SelectSinglePrepared(const XPathQuery& query);
     /// Select elements/attributes using XPath query.
     /// Select elements/attributes using XPath query.
     XPathResultSet Select(const String& query, pugi::xpath_variable_set* variables = 0);
     XPathResultSet Select(const String& query, pugi::xpath_variable_set* variables = 0);
+    /// Select elements/attributes using XPath query.
+    XPathResultSet SelectPrepared(const XPathQuery& query);
 
 
     /// Set an attribute.
     /// Set an attribute.
     bool SetAttribute(const String& name, const String& value);
     bool SetAttribute(const String& name, const String& value);
@@ -133,11 +138,11 @@ public:
     bool SetVector4(const String& name, const Vector4& value);
     bool SetVector4(const String& name, const Vector4& value);
 
 
     /// Return whether does not refer to an element or an XPath node.
     /// Return whether does not refer to an element or an XPath node.
-    bool IsNull() const { return !node_ && !xpathNode_; }
+    bool IsNull() const;
     /// Return whether refers to an element or an XPath node.
     /// Return whether refers to an element or an XPath node.
-    bool NotNull() const { return node_ || xpathNode_; }
+    bool NotNull() const;
     /// Return true if refers to an element or an XPath node.
     /// Return true if refers to an element or an XPath node.
-    operator bool () const { return node_ || xpathNode_; }
+    operator bool () const;
     /// Return element name (or attribute name if it is an attribute only XPath query result).
     /// Return element name (or attribute name if it is an attribute only XPath query result).
     String GetName() const;
     String GetName() const;
     /// Return whether has a child element.
     /// Return whether has a child element.
@@ -253,7 +258,7 @@ public:
     /// Construct empty result set.
     /// Construct empty result set.
     XPathResultSet();
     XPathResultSet();
     /// Construct with result set from XPath query.
     /// Construct with result set from XPath query.
-    XPathResultSet(pugi::xpath_node_set* resultSet);
+    XPathResultSet(XMLFile* file, pugi::xpath_node_set* resultSet);
     // Copy-construct.
     // Copy-construct.
     XPathResultSet(const XPathResultSet& rhs);
     XPathResultSet(const XPathResultSet& rhs);
     /// Destruct.
     /// Destruct.
@@ -272,6 +277,8 @@ public:
     pugi::xpath_node_set* GetXPathNodeSet() const { return resultSet_; }
     pugi::xpath_node_set* GetXPathNodeSet() const { return resultSet_; }
 
 
 private:
 private:
+    /// XML file.
+    WeakPtr<XMLFile> file_;
     /// Pugixml xpath_node_set.
     /// Pugixml xpath_node_set.
     pugi::xpath_node_set* resultSet_;
     pugi::xpath_node_set* resultSet_;
 };
 };
@@ -282,8 +289,8 @@ class XPathQuery
 public:
 public:
     /// Construct empty.
     /// Construct empty.
     XPathQuery();
     XPathQuery();
-    /// Construct XPath query object.
-    XPathQuery(const String& queryString);
+    /// Construct XPath query object with query string and variable string. The variable string format is "name1:type1,name2:type2,..." where type is one of "Bool", "Float", "String", "ResultSet".
+    XPathQuery(const String& queryString, const String& variableString = String::EMPTY);
     /// Destruct.
     /// Destruct.
     ~XPathQuery();
     ~XPathQuery();
     /// Bind query object with variable set.
     /// Bind query object with variable set.
@@ -296,10 +303,10 @@ public:
     bool SetVariable(const String& name, const String& value);
     bool SetVariable(const String& name, const String& value);
     /// Add/Set an XPath query result set variable. Return true if successful.
     /// Add/Set an XPath query result set variable. Return true if successful.
     bool SetVariable(const String& name, const XPathResultSet& value);
     bool SetVariable(const String& name, const XPathResultSet& value);
-    /// Remove all variables.
-    void RemoveVariables();
-    /// Set XPath query string.
-    void SetQuery(const String& queryString, bool bind = false);
+    /// Set XPath query string and variable string. The variable string format is "name1:type1,name2:type2,..." where type is one of "Bool", "Float", "String", "ResultSet".
+    bool SetQuery(const String& queryString, const String& variableString = String::EMPTY, bool bind = true);
+    /// Clear by removing all variables and XPath query object.
+    void Clear();
     /// Evaluate XPath query and expecting a boolean return value.
     /// Evaluate XPath query and expecting a boolean return value.
     bool EvaluateToBool(XMLElement element) const;
     bool EvaluateToBool(XMLElement element) const;
     /// Evaluate XPath query and expecting a float return value.
     /// Evaluate XPath query and expecting a float return value.

+ 1 - 2
Engine/UI/UI.h

@@ -23,6 +23,7 @@
 #pragma once
 #pragma once
 
 
 #include "Object.h"
 #include "Object.h"
+#include "Cursor.h"
 #include "UIBatch.h"
 #include "UIBatch.h"
 
 
 namespace Urho3D
 namespace Urho3D
@@ -37,8 +38,6 @@ class VertexBuffer;
 class XMLElement;
 class XMLElement;
 class XMLFile;
 class XMLFile;
 
 
-enum CursorShape;
-
 /// %UI subsystem. Manages the graphical user interface.
 /// %UI subsystem. Manages the graphical user interface.
 class UI : public Object
 class UI : public Object
 {
 {

+ 6 - 12
Engine/UI/UIElement.cpp

@@ -106,6 +106,8 @@ template<> LayoutMode Variant::Get<LayoutMode>() const
 
 
 OBJECTTYPESTATIC(UIElement);
 OBJECTTYPESTATIC(UIElement);
 
 
+XPathQuery UIElement::styleXPathQuery_("/elements/element[@type=$typeName]", "typeName:String");
+
 UIElement::UIElement(Context* context) :
 UIElement::UIElement(Context* context) :
     Serializable(context),
     Serializable(context),
     parent_(0),
     parent_(0),
@@ -763,20 +765,12 @@ void UIElement::SetStyle(XMLFile* file, const String& typeName)
     if (!file)
     if (!file)
         return;
         return;
 
 
-    XMLElement rootElem = file->GetRoot();
-    XMLElement childElem = rootElem.GetChild("element");
-    while (childElem)
-    {
-        if (typeName == childElem.GetAttribute("type"))
-        {
-            SetStyle(childElem);
-            return;
-        }
-        childElem = childElem.GetNext("element");
-    }
+    styleXPathQuery_.SetVariable("typeName", typeName);
+    XMLElement styleElem = file->GetRoot().SelectSinglePrepared(styleXPathQuery_);
+    if (styleElem)
+        SetStyle(styleElem);
 }
 }
 
 
-
 void UIElement::SetStyle(const XMLElement& element)
 void UIElement::SetStyle(const XMLElement& element)
 {
 {
     LoadXML(element);
     LoadXML(element);

+ 2 - 0
Engine/UI/UIElement.h

@@ -559,6 +559,8 @@ private:
     TraversalMode traversalMode_;
     TraversalMode traversalMode_;
     /// Element creation/deletion event sender flag.
     /// Element creation/deletion event sender flag.
     bool elementEventSender_;
     bool elementEventSender_;
+    /// XPath query for selecting UI-style.
+    static XPathQuery styleXPathQuery_;
 };
 };
 
 
 template <class T> T* UIElement::CreateChild(const String& name, unsigned index) { return static_cast<T*>(CreateChild(T::GetTypeStatic(), name, index)); }
 template <class T> T* UIElement::CreateChild(const String& name, unsigned index) { return static_cast<T*>(CreateChild(T::GetTypeStatic(), name, index)); }

+ 5 - 0
ThirdParty/PugiXml/src/pugixml.cpp

@@ -9014,6 +9014,11 @@ namespace pugi
 	{
 	{
 	}
 	}
 		
 		
+	// Modified by YaoWT for Urho3D
+	xpath_node::xpath_node(const xpath_node& rhs): _node(rhs._node), _attribute(rhs._attribute)
+	{
+	}
+
 	xpath_node::xpath_node(const xml_node& node): _node(node)
 	xpath_node::xpath_node(const xml_node& node): _node(node)
 	{
 	{
 	}
 	}

+ 4 - 0
ThirdParty/PugiXml/src/pugixml.hpp

@@ -970,6 +970,10 @@ namespace pugi
 	public:
 	public:
 		// Default constructor; constructs empty XPath node
 		// Default constructor; constructs empty XPath node
 		xpath_node();
 		xpath_node();
+
+		// Modified by YaoWT for Urho3D
+		// Copy constructor from another xpath_node
+		xpath_node(const xpath_node& rhs);
 		
 		
 		// Construct XPath node from XML node/attribute
 		// Construct XPath node from XML node/attribute
 		xpath_node(const xml_node& node);
 		xpath_node(const xml_node& node);