ComponentKit/Core/ComponentTree/CKRootTreeNode.mm (83 lines of code) (raw):
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import "CKRootTreeNode.h"
#import <RenderCore/RCAssert.h>
#import "CKRenderHelpers.h"
#import "CKTreeNode.h"
CKRootTreeNode::CKRootTreeNode(): _node([CKTreeNode rootNode]) {};
#if CK_ASSERTIONS_ENABLED
static auto _parentIdentifiers(const std::unordered_map<CKTreeNodeIdentifier,
CKTreeNode *>& nodesToParentNodes,
CKTreeNode *node) -> NSString * {
const auto parents = [NSMutableArray new];
while (node.component != nil) {
[parents addObject:node.component.className];
const auto it = nodesToParentNodes.find(node.nodeIdentifier);
node = (it != nodesToParentNodes.cend()) ? it->second : nil;
}
return [[[parents reverseObjectEnumerator] allObjects] componentsJoinedByString:@"-"];
}
static auto _existingAndNewParentIdentifiers(
const std::unordered_map<CKTreeNodeIdentifier,
CKTreeNode *>& nodesToParentNodes,
CKTreeNode *node,
CKTreeNode *parent) -> NSString * {
return [NSString stringWithFormat:@"Previous Parents:%@\nNew Parents:%@",
_parentIdentifiers(nodesToParentNodes, nodesToParentNodes.find(node.nodeIdentifier)->second),
_parentIdentifiers(nodesToParentNodes, parent)];
}
#endif
void CKRootTreeNode::registerNode(CKTreeNode *node, CKTreeNode *parent) noexcept {
RCCAssert(parent != nil, @"Cannot register a nil parent node");
if (node) {
#if CK_ASSERTIONS_ENABLED
const auto registeredParent = _nodesToParentNodes.find(node.nodeIdentifier);
if (registeredParent != _nodesToParentNodes.cend()) {
const auto parentComponentTreeDescription =
_existingAndNewParentIdentifiers(_nodesToParentNodes, node, parent);
if (registeredParent->second.nodeIdentifier == parent.nodeIdentifier) {
// Suggests non optimal tree build/reuse logic.
RCCFailAssertWithCategory(node.component.className,
@"Duplicate parent registration.\n%@",
parentComponentTreeDescription);
} else {
// Suggests same component instance is used in two subtrees or reuse error.
RCCFailAssertWithCategory(node.component.className,
@"Distinct parent registration (current: %ld - new: %ld).\n%@",
(long)registeredParent->second.nodeIdentifier,
(long)parent.nodeIdentifier,
parentComponentTreeDescription);
}
}
#endif
_nodesToParentNodes[node.nodeIdentifier] = parent;
}
}
CKTreeNode *CKRootTreeNode::parentForNodeIdentifier(CKTreeNodeIdentifier nodeIdentifier) const {
RCCAssert(nodeIdentifier != 0, @"Cannot retrieve parent for an empty node");
auto const it = _nodesToParentNodes.find(nodeIdentifier);
if (it != _nodesToParentNodes.end()) {
return it->second;
}
return nil;
}
bool CKRootTreeNode::isEmpty() const {
return _node.childrenSize == 0;
}
CKTreeNode *CKRootTreeNode::node() const {
return _node;
}
const CKTreeNodeDirtyIds& CKRootTreeNode::dirtyNodeIdsForPropsUpdates() const {
return _dirtyNodeIdsForPropsUpdates;
}
void CKRootTreeNode::markTopRenderComponentAsDirtyForPropsUpdates() noexcept {
while (!_stack.empty()) {
auto nodeIdentifier = _stack.top();
_dirtyNodeIdsForPropsUpdates.insert(nodeIdentifier);
_stack.pop();
}
}
void CKRootTreeNode::willBuildComponentTree(CKTreeNode *node) noexcept {
_stack.push(node.nodeIdentifier);
}
void CKRootTreeNode::didBuildComponentTree() noexcept {
if (!_stack.empty()) {
_stack.pop();
}
}