mirror of https://github.com/axmolengine/axmol.git
issue #4693:enumerateChildren() support regular expression
This commit is contained in:
parent
6845a69655
commit
53963a2441
|
@ -30,6 +30,7 @@ THE SOFTWARE.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include "base/CCDirector.h"
|
#include "base/CCDirector.h"
|
||||||
#include "base/CCScheduler.h"
|
#include "base/CCScheduler.h"
|
||||||
|
@ -692,7 +693,7 @@ GLProgram * Node::getGLProgram() const
|
||||||
return _glProgramState ? _glProgramState->getGLProgram() : nullptr;
|
return _glProgramState ? _glProgramState->getGLProgram() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene* Node::getScene()
|
Scene* Node::getScene() const
|
||||||
{
|
{
|
||||||
if(!_parent)
|
if(!_parent)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -767,7 +768,7 @@ Node* Node::getChildByTag(int tag) const
|
||||||
|
|
||||||
Node* Node::getChildByName(const std::string& name) const
|
Node* Node::getChildByName(const std::string& name) const
|
||||||
{
|
{
|
||||||
CCASSERT(name.size() != 0, "Invalid name");
|
CCASSERT(name.length() != 0, "Invalid name");
|
||||||
|
|
||||||
std::hash<std::string> h;
|
std::hash<std::string> h;
|
||||||
size_t hash = h(name);
|
size_t hash = h(name);
|
||||||
|
@ -781,25 +782,143 @@ Node* Node::getChildByName(const std::string& name) const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::enumerateChildren(const std::string &name, std::function<bool (const Node *)> callback) const
|
void Node::enumerateChildren(const std::string &name, std::function<bool (Node *)> callback) const
|
||||||
{
|
{
|
||||||
CCASSERT(name.size() != 0, "Invalid name");
|
CCASSERT(name.length() != 0, "Invalid name");
|
||||||
|
CCASSERT(callback != nullptr, "Invalid callback function");
|
||||||
|
|
||||||
std::hash<std::string> h;
|
size_t length = name.length();
|
||||||
size_t hash = h(name);
|
|
||||||
|
|
||||||
for (const auto& child : _children)
|
size_t subStrStartPos = 0; // sub string start index
|
||||||
|
size_t subStrlength = length; // sub string length
|
||||||
|
|
||||||
|
// Starts with '/' or '//'?
|
||||||
|
bool searchFromRoot = false;
|
||||||
|
bool searchFromRootRecursive = false;
|
||||||
|
if (name[0] == '/')
|
||||||
{
|
{
|
||||||
// Different strings may have the same hash code, but can use it to compare first for speed
|
if (length > 2 && name[1] == '/')
|
||||||
if(child->_hashOfName == hash && child->_name.compare(name) == 0)
|
|
||||||
{
|
{
|
||||||
// terminate enumeration if callback return true
|
searchFromRootRecursive = true;
|
||||||
if (callback(child))
|
subStrStartPos = 2;
|
||||||
|
subStrlength -= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchFromRoot = true;
|
||||||
|
subStrStartPos = 1;
|
||||||
|
subStrlength -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End with '/..'?
|
||||||
|
bool searchFromParent = false;
|
||||||
|
if (length > 3 &&
|
||||||
|
name[length-3] == '/' &&
|
||||||
|
name[length-2] == '.' &&
|
||||||
|
name[length-1] == '.')
|
||||||
|
{
|
||||||
|
searchFromParent = true;
|
||||||
|
subStrlength -= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove '/', '//' and '/..' if exist
|
||||||
|
std::string newName = name.substr(subStrStartPos, subStrlength);
|
||||||
|
// If search from parent, then add * at first to make it match its children, which will do make
|
||||||
|
if (searchFromParent)
|
||||||
|
{
|
||||||
|
newName.insert(0, "[[:alnum:]]+/");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchFromRoot)
|
||||||
|
{
|
||||||
|
// name is '/xxx'
|
||||||
|
auto root = getScene();
|
||||||
|
if (root)
|
||||||
|
{
|
||||||
|
root->doEnumerate(newName, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (searchFromRootRecursive)
|
||||||
|
{
|
||||||
|
// name is '//xxx'
|
||||||
|
auto root = getScene();
|
||||||
|
if (root)
|
||||||
|
{
|
||||||
|
doEnumerateRecursive(root, newName, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// name is xxx
|
||||||
|
doEnumerate(newName, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Node::doEnumerateRecursive(const Node* node, const std::string &name, std::function<bool (Node *)> callback) const
|
||||||
|
{
|
||||||
|
bool ret =false;
|
||||||
|
|
||||||
|
if (node->doEnumerate(name, callback))
|
||||||
|
{
|
||||||
|
// search itself
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// search its children
|
||||||
|
for (const auto& child : node->getChildren())
|
||||||
|
{
|
||||||
|
if (doEnumerateRecursive(child, name, callback))
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Node::doEnumerate(std::string name, std::function<bool (Node *)> callback) const
|
||||||
|
{
|
||||||
|
// name may be xxx/yyy, should find its parent
|
||||||
|
size_t pos = name.find('/');
|
||||||
|
std::string searchName = name;
|
||||||
|
bool needRecursive = false;
|
||||||
|
if (pos != name.npos)
|
||||||
|
{
|
||||||
|
searchName = name.substr(0, pos);
|
||||||
|
name.erase(0, pos+1);
|
||||||
|
needRecursive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
for (const auto& child : _children)
|
||||||
|
{
|
||||||
|
if(std::regex_match(child->_name, std::regex(searchName)))
|
||||||
|
{
|
||||||
|
if (!needRecursive)
|
||||||
|
{
|
||||||
|
// terminate enumeration if callback return true
|
||||||
|
if (callback(child))
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = child->doEnumerate(name, callback);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* "add" logic MUST only be on this method
|
/* "add" logic MUST only be on this method
|
||||||
* If a class want's to extend the 'addChild' behavior it only needs
|
* If a class want's to extend the 'addChild' behavior it only needs
|
||||||
* to override this method
|
* to override this method
|
||||||
|
|
|
@ -679,7 +679,28 @@ public:
|
||||||
virtual Node* getChildByName(const std::string& name) const;
|
virtual Node* getChildByName(const std::string& name) const;
|
||||||
/** Search the children of the receiving node to perform processing for nodes which share a name.
|
/** Search the children of the receiving node to perform processing for nodes which share a name.
|
||||||
*
|
*
|
||||||
* @param name The name to search for
|
* @param name The name to search for, support c++11 regular expression
|
||||||
|
* Search syntax options:
|
||||||
|
* `/` : When placed at the start of the search string, this indicates that the search should be performed on the tree's node.
|
||||||
|
* `//`: Can only be placed at the begin of the search string. This indicates that the search should be performed on the tree's node
|
||||||
|
* and be performed recursively across the entire node tree.
|
||||||
|
* `..`: The search should move up to the node's parent. Can only be placed at the end of string
|
||||||
|
* `/` : When placed anywhere but the start of the search string, this indicates that the search should move to the node's children
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* enumerateChildren("/MyName", ...): This searches the root's children and matches any node with the name `MyName`.
|
||||||
|
* enumerateChildren("//MyName", ...): This searches the root's children recursively and matches any node with the name `MyName`.
|
||||||
|
* enumerateChildren("[[:alnum:]]+", ...): This search string matches every node of its children.
|
||||||
|
* enumerateChildren("/MyName", ...): This searches the node tree and matches the parent node of every node named `MyName`.
|
||||||
|
* enumerateChildren("A([:digit:])", ...): This searches the node's children and returns any child named `A0`, `A1`, ..., `A9`
|
||||||
|
* enumerateChildren("Abby/Normal", ...): This searches the node's grandchildren and returns any node whose name is `Normal`
|
||||||
|
* and whose parent is named `Abby`.
|
||||||
|
* enumerateChildren("//Abby/Normal", ...): This searches the node tree and returns any node whose name is `Normal` and whose
|
||||||
|
* parent is named `Abby`.
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @warning Only support alpha or number for name, and not support unicode
|
||||||
|
*
|
||||||
* @param callback A callback function to execute on nodes that match the `name` parameter. The function takes the following arguments:
|
* @param callback A callback function to execute on nodes that match the `name` parameter. The function takes the following arguments:
|
||||||
* `node`
|
* `node`
|
||||||
* A node that matches the name
|
* A node that matches the name
|
||||||
|
@ -687,7 +708,7 @@ public:
|
||||||
*
|
*
|
||||||
* @since v3.2
|
* @since v3.2
|
||||||
*/
|
*/
|
||||||
virtual void enumerateChildren(const std::string &name, std::function<bool(const Node* node)> callback) const;
|
virtual void enumerateChildren(const std::string &name, std::function<bool(Node* node)> callback) const;
|
||||||
/**
|
/**
|
||||||
* Returns the array of the node's children
|
* Returns the array of the node's children
|
||||||
*
|
*
|
||||||
|
@ -994,7 +1015,7 @@ public:
|
||||||
It returns `nullptr` if the node doesn't belong to any Scene.
|
It returns `nullptr` if the node doesn't belong to any Scene.
|
||||||
This function recursively calls parent->getScene() until parent is a Scene object. The results are not cached. It is that the user caches the results in case this functions is being used inside a loop.
|
This function recursively calls parent->getScene() until parent is a Scene object. The results are not cached. It is that the user caches the results in case this functions is being used inside a loop.
|
||||||
*/
|
*/
|
||||||
virtual Scene* getScene();
|
virtual Scene* getScene() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an AABB (axis-aligned bounding-box) in its parent's coordinate system.
|
* Returns an AABB (axis-aligned bounding-box) in its parent's coordinate system.
|
||||||
|
@ -1427,6 +1448,9 @@ protected:
|
||||||
virtual void disableCascadeColor();
|
virtual void disableCascadeColor();
|
||||||
virtual void updateColor() {}
|
virtual void updateColor() {}
|
||||||
|
|
||||||
|
bool doEnumerate(std::string name, std::function<bool (Node *)> callback) const;
|
||||||
|
bool doEnumerateRecursive(const Node* node, const std::string &name, std::function<bool (Node *)> callback) const;
|
||||||
|
|
||||||
#if CC_USE_PHYSICS
|
#if CC_USE_PHYSICS
|
||||||
virtual void updatePhysicsBodyPosition(Scene* layer);
|
virtual void updatePhysicsBodyPosition(Scene* layer);
|
||||||
virtual void updatePhysicsBodyRotation(Scene* layer);
|
virtual void updatePhysicsBodyRotation(Scene* layer);
|
||||||
|
|
|
@ -98,9 +98,10 @@ std::string Scene::getDescription() const
|
||||||
return StringUtils::format("<Scene | tag = %d>", _tag);
|
return StringUtils::format("<Scene | tag = %d>", _tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene* Scene::getScene()
|
Scene* Scene::getScene() const
|
||||||
{
|
{
|
||||||
return this;
|
// FIX ME: should use const_case<> to fix compiling error
|
||||||
|
return const_cast<Scene*>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CC_USE_PHYSICS
|
#if CC_USE_PHYSICS
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
static Scene *createWithSize(const Size& size);
|
static Scene *createWithSize(const Size& size);
|
||||||
|
|
||||||
// Overrides
|
// Overrides
|
||||||
virtual Scene *getScene() override;
|
virtual Scene *getScene() const override;
|
||||||
|
|
||||||
using Node::addChild;
|
using Node::addChild;
|
||||||
virtual std::string getDescription() const override;
|
virtual std::string getDescription() const override;
|
||||||
|
|
|
@ -1247,6 +1247,8 @@ void NodeNameTest::onEnter()
|
||||||
{
|
{
|
||||||
TestCocosNodeDemo::BaseTest::onEnter();
|
TestCocosNodeDemo::BaseTest::onEnter();
|
||||||
|
|
||||||
|
auto parent = Node::create();
|
||||||
|
|
||||||
// setName(), getName() and getChildByName()
|
// setName(), getName() and getChildByName()
|
||||||
char name[20];
|
char name[20];
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
|
@ -1254,42 +1256,172 @@ void NodeNameTest::onEnter()
|
||||||
sprintf(name, "node%d", i);
|
sprintf(name, "node%d", i);
|
||||||
auto node = Node::create();
|
auto node = Node::create();
|
||||||
node->setName(name);
|
node->setName(name);
|
||||||
addChild(node);
|
parent->addChild(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < 10; ++j)
|
for (int i = 0; i < 10; ++i)
|
||||||
{
|
{
|
||||||
sprintf(name, "node%d", j);
|
sprintf(name, "node%d", i);
|
||||||
auto node = getChildByName(name);
|
auto node = parent->getChildByName(name);
|
||||||
log("find child: %s", node->getName().c_str());
|
log("find child: %s", node->getName().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// node with same name
|
|
||||||
auto node = Node::create();
|
|
||||||
log("node with name test: %p", node);
|
|
||||||
node->setName("test");
|
|
||||||
addChild(node);
|
|
||||||
|
|
||||||
node = Node::create();
|
|
||||||
log("node with name test: %p", node);
|
|
||||||
node->setName("test");
|
|
||||||
addChild(node);
|
|
||||||
|
|
||||||
node = getChildByName("test");
|
|
||||||
log("find node with name: %p", node);
|
|
||||||
|
|
||||||
// enumerateChildren()
|
// enumerateChildren()
|
||||||
log("will terminate when find node with name 'test'");
|
int i = 0;
|
||||||
enumerateChildren("test", [](const Node* node) -> bool {
|
parent->enumerateChildren("test", [&i](Node* node) -> bool {
|
||||||
log("find node with name 'test'");
|
++i;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
log("will find all nodes with name 'test' twice");
|
i = 0;
|
||||||
enumerateChildren("test", [](const Node* node) -> bool {
|
parent->enumerateChildren("test", [&i](Node* node) -> bool {
|
||||||
log("find node with name 'test'");
|
++i;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
CCAssert(i == 2, "");
|
||||||
|
|
||||||
|
// enumerateChildren()
|
||||||
|
// name = regular expression
|
||||||
|
parent = Node::create();
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
auto node = Node::create();
|
||||||
|
sprintf(name, "node%d", i);
|
||||||
|
node->setName(name);
|
||||||
|
parent->addChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node[[:digit:]]+", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 100, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node[[:digit:]]+", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
|
|
||||||
|
// enumerateChildren
|
||||||
|
// name = node[[digit]]+/node
|
||||||
|
|
||||||
|
parent = Node::create();
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
auto node = Node::create();
|
||||||
|
sprintf(name, "node%d", i);
|
||||||
|
node->setName(name);
|
||||||
|
parent->addChild(node);
|
||||||
|
|
||||||
|
for (int j = 0; j < 100; ++j)
|
||||||
|
{
|
||||||
|
auto child = Node::create();
|
||||||
|
child->setName("node");
|
||||||
|
node->addChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node1/node", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 100, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node1/node", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node[[:digit:]]+/node", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 10000, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node[[:digit:]]+/node", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
|
// search from parent
|
||||||
|
// name is xxx/..
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node/..", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
parent->enumerateChildren("node/..", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 10000, "");
|
||||||
|
|
||||||
|
// name = /xxx : search from root
|
||||||
|
parent = getScene();
|
||||||
|
for (int j = 0; j < 100; j++)
|
||||||
|
{
|
||||||
|
auto node = Node::create();
|
||||||
|
sprintf(name, "node%d", j);
|
||||||
|
node->setName(name);
|
||||||
|
parent->addChild(node);
|
||||||
|
|
||||||
|
for (int k = 0; k < 100; ++k)
|
||||||
|
{
|
||||||
|
auto child = Node::create();
|
||||||
|
sprintf(name, "node%d", k);
|
||||||
|
child->setName(name);
|
||||||
|
node->addChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
enumerateChildren("/node[[:digit:]]+", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 100, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
enumerateChildren("/node[[:digit:]]+", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
enumerateChildren("//node[[:digit:]]+", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 10100, ""); // 10000(children) + 100(parent)
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
enumerateChildren("//node[[:digit:]]+", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
CCAssert(i == 1, "");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
enumerateChildren("//node[[:digit:]]+/..", [&i](Node* node) -> bool {
|
||||||
|
++i;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
CCAssert(i == 10000, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in New Issue