// ================================================================================ // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås == // == See tb_core.h for more information. == // ================================================================================ #include "tb_node_ref_tree.h" #include "tb_language.h" #include "tb_system.h" namespace tb { // == TBNodeRefTree ============================================================== //static TBLinkListOf TBNodeRefTree::s_ref_trees; TBNodeRefTree::TBNodeRefTree(const char *name) : m_name(name), m_name_id(name) { s_ref_trees.AddLast(this); } TBNodeRefTree::~TBNodeRefTree() { s_ref_trees.Remove(this); } TBValue &TBNodeRefTree::GetValue(const char *request) { if (TBNode *node = m_node.GetNodeFollowRef(request)) return node->GetValue(); TBDebugPrint("TBNodeRefTree::GetValue - Request not found: %s\n", request); static TBValue nullval; return nullval; } //static TBValue &TBNodeRefTree::GetValueFromTree(const char *request) { assert(*request == '@'); TBNode tmp; tmp.GetValue().SetString(request, TBValue::SET_AS_STATIC); TBNode *node = TBNodeRefTree::FollowNodeRef(&tmp); if (node != &tmp) return node->GetValue(); static TBValue nullval; return nullval; } void TBNodeRefTree::SetValue(const char *request, const TBValue &value) { if (TBNode *node = m_node.GetNode(request, TBNode::GET_MISS_POLICY_CREATE)) { // FIX: Only invoke the listener if it really changed. node->GetValue().Copy(value); InvokeChangeListenersInternal(request); } } void TBNodeRefTree::InvokeChangeListenersInternal(const char *request) { TBLinkListOf::Iterator iter = m_listeners.IterateForward(); while (TBNodeRefTreeListener *listener = iter.GetAndStep()) listener->OnDataChanged(this, request); } //static TBNodeRefTree *TBNodeRefTree::GetRefTree(const char *name, int name_len) { for (TBNodeRefTree *rt = s_ref_trees.GetFirst(); rt; rt = rt->GetNext()) if (strncmp(rt->GetName(), name, name_len) == 0) return rt; return nullptr; } //static TBNode *TBNodeRefTree::FollowNodeRef(TBNode *node) { // Detect circular loops by letting this call get a unique id. // Update the id on each visited node and if it's already set, // there's a loop. This cost the storage of id in each TBNode, // and assumes the look up doesn't cause other lookups // recursively. // FIX: Switch to hare and teleporting tortouise? static uint32 s_cycle_id = 0; uint32 cycle_id = ++s_cycle_id; TBNode *start_node = node; while (node->GetValue().IsString()) { // If not a reference at all, we're done. const char *node_str = node->GetValue().GetString(); if (*node_str != '@') break; // If there's no tree name and request, we're done. It's probably a language string. const char *name_start = node_str + 1; const char *name_end = TBNode::GetNextNodeSeparator(name_start); if (*name_end == 0) break; // We have a "@treename>noderequest" string. Go ahead and look it up. if (TBNodeRefTree *rt = TBNodeRefTree::GetRefTree(name_start, name_end - name_start)) { TBNode *next_node = rt->m_node.GetNode(name_end + 1, TBNode::GET_MISS_POLICY_NULL); if (!next_node) { TBDebugPrint("TBNodeRefTree::ResolveNode - Node not found on request \"%s\"\n", node_str); break; } node = next_node; // Detect circular reference loop. if (node->m_cycle_id != cycle_id) node->m_cycle_id = cycle_id; else { TBDebugPrint("TBNodeRefTree::ResolveNode - Reference loop detected on request \"%s\" from node \"%s\"\n", node_str, node->GetValue().GetString()); return start_node; } } else { TBDebugPrint("TBNodeRefTree::ResolveNode - No tree found for request \"%s\" from node \"%s\"\n", node_str, node->GetValue().GetString()); break; } } return node; } //static void TBNodeRefTree::ResolveConditions(TBNode *parent_node) { bool condition_ret = false; TBNode *node = parent_node->GetFirstChild(); while (node) { bool delete_node = false; bool move_children = false; if (strcmp(node->GetName(), "@if") == 0) { condition_ret = node->GetValueFollowRef().GetInt() ? true : false; if (condition_ret) move_children = true; delete_node = true; } else if (strcmp(node->GetName(), "@else") == 0) { condition_ret = !condition_ret; if (condition_ret) move_children = true; delete_node = true; } // Make sure we'll skip any nodes added from a conditional branch. TBNode *node_next = node->GetNext(); if (move_children) { // Resolve the branch first, since we'll skip it. ResolveConditions(node); while (TBNode *content = node->GetLastChild()) { node->Remove(content); parent_node->AddAfter(content, node); } } if (delete_node) parent_node->Delete(node); else ResolveConditions(node); node = node_next; } } }; // namespace tb