tb_node_ref_tree.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // ================================================================================
  2. // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
  3. // == See tb_core.h for more information. ==
  4. // ================================================================================
  5. #include "tb_node_ref_tree.h"
  6. #include "tb_language.h"
  7. #include "tb_system.h"
  8. namespace tb {
  9. // == TBNodeRefTree ==============================================================
  10. //static
  11. TBLinkListOf<TBNodeRefTree> TBNodeRefTree::s_ref_trees;
  12. TBNodeRefTree::TBNodeRefTree(const char *name) : m_name(name), m_name_id(name)
  13. {
  14. s_ref_trees.AddLast(this);
  15. }
  16. TBNodeRefTree::~TBNodeRefTree()
  17. {
  18. s_ref_trees.Remove(this);
  19. }
  20. TBValue &TBNodeRefTree::GetValue(const char *request)
  21. {
  22. if (TBNode *node = m_node.GetNodeFollowRef(request))
  23. return node->GetValue();
  24. TBDebugPrint("TBNodeRefTree::GetValue - Request not found: %s\n", request);
  25. static TBValue nullval;
  26. return nullval;
  27. }
  28. //static
  29. TBValue &TBNodeRefTree::GetValueFromTree(const char *request)
  30. {
  31. assert(*request == '@');
  32. TBNode tmp;
  33. tmp.GetValue().SetString(request, TBValue::SET_AS_STATIC);
  34. TBNode *node = TBNodeRefTree::FollowNodeRef(&tmp);
  35. if (node != &tmp)
  36. return node->GetValue();
  37. static TBValue nullval;
  38. return nullval;
  39. }
  40. void TBNodeRefTree::SetValue(const char *request, const TBValue &value)
  41. {
  42. if (TBNode *node = m_node.GetNode(request, TBNode::GET_MISS_POLICY_CREATE))
  43. {
  44. // FIX: Only invoke the listener if it really changed.
  45. node->GetValue().Copy(value);
  46. InvokeChangeListenersInternal(request);
  47. }
  48. }
  49. void TBNodeRefTree::InvokeChangeListenersInternal(const char *request)
  50. {
  51. TBLinkListOf<TBNodeRefTreeListener>::Iterator iter = m_listeners.IterateForward();
  52. while (TBNodeRefTreeListener *listener = iter.GetAndStep())
  53. listener->OnDataChanged(this, request);
  54. }
  55. //static
  56. TBNodeRefTree *TBNodeRefTree::GetRefTree(const char *name, int name_len)
  57. {
  58. for (TBNodeRefTree *rt = s_ref_trees.GetFirst(); rt; rt = rt->GetNext())
  59. if (strncmp(rt->GetName(), name, name_len) == 0)
  60. return rt;
  61. return nullptr;
  62. }
  63. //static
  64. TBNode *TBNodeRefTree::FollowNodeRef(TBNode *node)
  65. {
  66. // Detect circular loops by letting this call get a unique id.
  67. // Update the id on each visited node and if it's already set,
  68. // there's a loop. This cost the storage of id in each TBNode,
  69. // and assumes the look up doesn't cause other lookups
  70. // recursively.
  71. // FIX: Switch to hare and teleporting tortouise?
  72. static uint32 s_cycle_id = 0;
  73. uint32 cycle_id = ++s_cycle_id;
  74. TBNode *start_node = node;
  75. while (node->GetValue().IsString())
  76. {
  77. // If not a reference at all, we're done.
  78. const char *node_str = node->GetValue().GetString();
  79. if (*node_str != '@')
  80. break;
  81. // If there's no tree name and request, we're done. It's probably a language string.
  82. const char *name_start = node_str + 1;
  83. const char *name_end = TBNode::GetNextNodeSeparator(name_start);
  84. if (*name_end == 0)
  85. break;
  86. // We have a "@treename>noderequest" string. Go ahead and look it up.
  87. if (TBNodeRefTree *rt = TBNodeRefTree::GetRefTree(name_start, name_end - name_start))
  88. {
  89. TBNode *next_node = rt->m_node.GetNode(name_end + 1, TBNode::GET_MISS_POLICY_NULL);
  90. if (!next_node)
  91. {
  92. TBDebugPrint("TBNodeRefTree::ResolveNode - Node not found on request \"%s\"\n", node_str);
  93. break;
  94. }
  95. node = next_node;
  96. // Detect circular reference loop.
  97. if (node->m_cycle_id != cycle_id)
  98. node->m_cycle_id = cycle_id;
  99. else
  100. {
  101. TBDebugPrint("TBNodeRefTree::ResolveNode - Reference loop detected on request \"%s\" from node \"%s\"\n",
  102. node_str, node->GetValue().GetString());
  103. return start_node;
  104. }
  105. }
  106. else
  107. {
  108. TBDebugPrint("TBNodeRefTree::ResolveNode - No tree found for request \"%s\" from node \"%s\"\n",
  109. node_str, node->GetValue().GetString());
  110. break;
  111. }
  112. }
  113. return node;
  114. }
  115. //static
  116. void TBNodeRefTree::ResolveConditions(TBNode *parent_node)
  117. {
  118. bool condition_ret = false;
  119. TBNode *node = parent_node->GetFirstChild();
  120. while (node)
  121. {
  122. bool delete_node = false;
  123. bool move_children = false;
  124. if (strcmp(node->GetName(), "@if") == 0)
  125. {
  126. condition_ret = node->GetValueFollowRef().GetInt() ? true : false;
  127. if (condition_ret)
  128. move_children = true;
  129. delete_node = true;
  130. }
  131. else if (strcmp(node->GetName(), "@else") == 0)
  132. {
  133. condition_ret = !condition_ret;
  134. if (condition_ret)
  135. move_children = true;
  136. delete_node = true;
  137. }
  138. // Make sure we'll skip any nodes added from a conditional branch.
  139. TBNode *node_next = node->GetNext();
  140. if (move_children)
  141. {
  142. // Resolve the branch first, since we'll skip it.
  143. ResolveConditions(node);
  144. while (TBNode *content = node->GetLastChild())
  145. {
  146. node->Remove(content);
  147. parent_node->AddAfter(content, node);
  148. }
  149. }
  150. if (delete_node)
  151. parent_node->Delete(node);
  152. else
  153. ResolveConditions(node);
  154. node = node_next;
  155. }
  156. }
  157. }; // namespace tb