exonware-xwnode 0.0.1.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. exonware/__init__.py +14 -0
  2. exonware/xwnode/__init__.py +127 -0
  3. exonware/xwnode/base.py +676 -0
  4. exonware/xwnode/config.py +178 -0
  5. exonware/xwnode/contracts.py +730 -0
  6. exonware/xwnode/errors.py +503 -0
  7. exonware/xwnode/facade.py +460 -0
  8. exonware/xwnode/strategies/__init__.py +158 -0
  9. exonware/xwnode/strategies/advisor.py +463 -0
  10. exonware/xwnode/strategies/edges/__init__.py +32 -0
  11. exonware/xwnode/strategies/edges/adj_list.py +227 -0
  12. exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
  13. exonware/xwnode/strategies/edges/base.py +169 -0
  14. exonware/xwnode/strategies/flyweight.py +328 -0
  15. exonware/xwnode/strategies/impls/__init__.py +13 -0
  16. exonware/xwnode/strategies/impls/_base_edge.py +403 -0
  17. exonware/xwnode/strategies/impls/_base_node.py +307 -0
  18. exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
  19. exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
  20. exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
  21. exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
  22. exonware/xwnode/strategies/impls/edge_coo.py +533 -0
  23. exonware/xwnode/strategies/impls/edge_csc.py +447 -0
  24. exonware/xwnode/strategies/impls/edge_csr.py +492 -0
  25. exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
  26. exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
  27. exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
  28. exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
  29. exonware/xwnode/strategies/impls/edge_octree.py +574 -0
  30. exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
  31. exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
  32. exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
  33. exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
  34. exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
  35. exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
  36. exonware/xwnode/strategies/manager.py +775 -0
  37. exonware/xwnode/strategies/metrics.py +538 -0
  38. exonware/xwnode/strategies/migration.py +432 -0
  39. exonware/xwnode/strategies/nodes/__init__.py +50 -0
  40. exonware/xwnode/strategies/nodes/_base_node.py +307 -0
  41. exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
  42. exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
  43. exonware/xwnode/strategies/nodes/array_list.py +209 -0
  44. exonware/xwnode/strategies/nodes/base.py +247 -0
  45. exonware/xwnode/strategies/nodes/deque.py +200 -0
  46. exonware/xwnode/strategies/nodes/hash_map.py +135 -0
  47. exonware/xwnode/strategies/nodes/heap.py +307 -0
  48. exonware/xwnode/strategies/nodes/linked_list.py +232 -0
  49. exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
  50. exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
  51. exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
  52. exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
  53. exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
  54. exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
  55. exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
  56. exonware/xwnode/strategies/nodes/node_btree.py +357 -0
  57. exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
  58. exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
  59. exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
  60. exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
  61. exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
  62. exonware/xwnode/strategies/nodes/node_heap.py +191 -0
  63. exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
  64. exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
  65. exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
  66. exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
  67. exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
  68. exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
  69. exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
  70. exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
  71. exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
  72. exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
  73. exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
  74. exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
  75. exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
  76. exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
  77. exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
  78. exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
  79. exonware/xwnode/strategies/nodes/node_treap.py +387 -0
  80. exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
  81. exonware/xwnode/strategies/nodes/node_trie.py +252 -0
  82. exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
  83. exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
  84. exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
  85. exonware/xwnode/strategies/nodes/queue.py +161 -0
  86. exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
  87. exonware/xwnode/strategies/nodes/stack.py +152 -0
  88. exonware/xwnode/strategies/nodes/trie.py +274 -0
  89. exonware/xwnode/strategies/nodes/union_find.py +283 -0
  90. exonware/xwnode/strategies/pattern_detector.py +603 -0
  91. exonware/xwnode/strategies/performance_monitor.py +487 -0
  92. exonware/xwnode/strategies/queries/__init__.py +24 -0
  93. exonware/xwnode/strategies/queries/base.py +236 -0
  94. exonware/xwnode/strategies/queries/cql.py +201 -0
  95. exonware/xwnode/strategies/queries/cypher.py +181 -0
  96. exonware/xwnode/strategies/queries/datalog.py +70 -0
  97. exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
  98. exonware/xwnode/strategies/queries/eql.py +70 -0
  99. exonware/xwnode/strategies/queries/flux.py +70 -0
  100. exonware/xwnode/strategies/queries/gql.py +70 -0
  101. exonware/xwnode/strategies/queries/graphql.py +240 -0
  102. exonware/xwnode/strategies/queries/gremlin.py +181 -0
  103. exonware/xwnode/strategies/queries/hiveql.py +214 -0
  104. exonware/xwnode/strategies/queries/hql.py +70 -0
  105. exonware/xwnode/strategies/queries/jmespath.py +219 -0
  106. exonware/xwnode/strategies/queries/jq.py +66 -0
  107. exonware/xwnode/strategies/queries/json_query.py +66 -0
  108. exonware/xwnode/strategies/queries/jsoniq.py +248 -0
  109. exonware/xwnode/strategies/queries/kql.py +70 -0
  110. exonware/xwnode/strategies/queries/linq.py +238 -0
  111. exonware/xwnode/strategies/queries/logql.py +70 -0
  112. exonware/xwnode/strategies/queries/mql.py +68 -0
  113. exonware/xwnode/strategies/queries/n1ql.py +210 -0
  114. exonware/xwnode/strategies/queries/partiql.py +70 -0
  115. exonware/xwnode/strategies/queries/pig.py +215 -0
  116. exonware/xwnode/strategies/queries/promql.py +70 -0
  117. exonware/xwnode/strategies/queries/sparql.py +220 -0
  118. exonware/xwnode/strategies/queries/sql.py +275 -0
  119. exonware/xwnode/strategies/queries/xml_query.py +66 -0
  120. exonware/xwnode/strategies/queries/xpath.py +223 -0
  121. exonware/xwnode/strategies/queries/xquery.py +258 -0
  122. exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
  123. exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
  124. exonware/xwnode/strategies/registry.py +604 -0
  125. exonware/xwnode/strategies/simple.py +273 -0
  126. exonware/xwnode/strategies/utils.py +532 -0
  127. exonware/xwnode/types.py +912 -0
  128. exonware/xwnode/version.py +78 -0
  129. exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
  130. exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
  131. exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
  132. exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,424 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ XWQuery Script Strategy
4
+
5
+ This module implements the central XWQuery Script strategy that handles all 50 action types
6
+ and provides conversion between different query formats using actions in tree format.
7
+
8
+ Company: eXonware.com
9
+ Author: Eng. Muhammad AlShehri
10
+ Email: connect@exonware.com
11
+ Version: 0.0.1.12
12
+ Generation Date: January 2, 2025
13
+ """
14
+
15
+ import re
16
+ from abc import ABC, abstractmethod
17
+ from typing import Any, Dict, List, Optional, Union, Type
18
+ from datetime import datetime
19
+
20
+ from .base import AQueryStrategy
21
+ from ...base import XWNodeBase
22
+ from ...contracts import QueryMode, QueryTrait
23
+ from ...errors import XWNodeTypeError, XWNodeValueError
24
+
25
+
26
+ class XWQueryScriptStrategy(AQueryStrategy):
27
+ """
28
+ Central script strategy using 50 action types in tree format.
29
+
30
+ This strategy serves as the universal converter between all query formats
31
+ by parsing them into a standardized action tree structure.
32
+ """
33
+
34
+ # 50 Action Headers from XWQUERY_SCRIPT.md
35
+ ACTION_TYPES = [
36
+ # Core SQL Operations (1-45)
37
+ "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP",
38
+ "MERGE", "LOAD", "STORE", "WHERE", "FILTER", "OPTIONAL", "UNION",
39
+ "BETWEEN", "LIKE", "IN", "TERM", "RANGE", "HAS", "MATCH", "JOIN",
40
+ "WITH", "OUT", "IN_TRAVERSE", "PATH", "RETURN", "PROJECT", "EXTEND",
41
+ "FOREACH", "LET", "FOR", "DESCRIBE", "CONSTRUCT", "ORDER", "BY",
42
+ "GROUP", "HAVING", "SUMMARIZE", "AGGREGATE", "WINDOW", "SLICING",
43
+ "INDEXING", "ASK", "SUBSCRIBE", "SUBSCRIPTION", "MUTATION", "VALUES",
44
+ # Additional Operations (46-50)
45
+ "DISTINCT", "PIPE", "OPTIONS"
46
+ ]
47
+
48
+ def __init__(self, actions_tree: Optional[XWNodeBase] = None, **options):
49
+ super().__init__(**options)
50
+ self._mode = QueryMode.AUTO
51
+ self._traits = QueryTrait.STRUCTURED | QueryTrait.ANALYTICAL | QueryTrait.BATCH
52
+
53
+ if actions_tree is None:
54
+ self._actions_tree = XWNodeBase.from_native({
55
+ "root": {
56
+ "type": "PROGRAM",
57
+ "statements": [],
58
+ "comments": [],
59
+ "metadata": {
60
+ "version": "1.0",
61
+ "created": datetime.now().isoformat(),
62
+ "source_format": "XWQUERY_SCRIPT"
63
+ }
64
+ }
65
+ })
66
+ else:
67
+ self._actions_tree = actions_tree
68
+
69
+ self._comments = []
70
+ self._metadata = {}
71
+
72
+ def execute(self, query: str, context: Dict[str, Any] = None, **kwargs) -> Any:
73
+ """Execute XWQuery script."""
74
+ if not self.validate_query(query):
75
+ raise XWNodeValueError(f"Invalid XWQuery script: {query}")
76
+
77
+ # Parse and execute the script
78
+ script_strategy = self.parse_script(query)
79
+ return self._execute_actions_tree(script_strategy._actions_tree, **kwargs)
80
+
81
+ def validate_query(self, query: str) -> bool:
82
+ """Validate XWQuery script syntax."""
83
+ if not query or not isinstance(query, str):
84
+ return False
85
+
86
+ # Basic validation - check for valid action types
87
+ query_upper = query.upper()
88
+ valid_actions = any(action in query_upper for action in self.ACTION_TYPES)
89
+
90
+ return valid_actions
91
+
92
+ def get_query_plan(self, query: str) -> Dict[str, Any]:
93
+ """Get XWQuery script execution plan."""
94
+ return {
95
+ "query_type": "XWQUERY_SCRIPT",
96
+ "action_count": len(self._extract_actions(query)),
97
+ "complexity": self._estimate_complexity(query),
98
+ "estimated_cost": self._estimate_cost(query),
99
+ "optimization_hints": self._get_optimization_hints(query)
100
+ }
101
+
102
+ def can_handle(self, query_string: str) -> bool:
103
+ """Check if this strategy can handle the given query string."""
104
+ return self.validate_query(query_string)
105
+
106
+ def get_supported_operations(self) -> List[str]:
107
+ """Get list of supported query operations."""
108
+ return self.ACTION_TYPES.copy()
109
+
110
+ def estimate_complexity(self, query_string: str) -> Dict[str, Any]:
111
+ """Estimate query complexity and resource requirements."""
112
+ actions = self._extract_actions(query_string)
113
+ complexity_level = self._estimate_complexity(query_string)
114
+
115
+ return {
116
+ "complexity": complexity_level,
117
+ "action_count": len(actions),
118
+ "estimated_cost": self._estimate_cost(query_string),
119
+ "memory_usage": "low" if complexity_level == "LOW" else "medium" if complexity_level == "MEDIUM" else "high",
120
+ "execution_time": f"{self._estimate_cost(query_string)}ms"
121
+ }
122
+
123
+ def parse_script(self, script_content: str) -> 'XWQueryScriptStrategy':
124
+ """Parse XWQuery script content into actions tree."""
125
+ parsed_actions = self._parse_xwquery_script(script_content)
126
+ self._actions_tree = XWNodeBase.from_native(parsed_actions)
127
+ return self
128
+
129
+ def to_format(self, target_format: str) -> str:
130
+ """Convert script to any supported format using available strategies."""
131
+ strategy_class = self._get_strategy_class(target_format)
132
+ if not strategy_class:
133
+ raise ValueError(f"No strategy available for format: {target_format}")
134
+
135
+ strategy = strategy_class()
136
+ return strategy.from_actions_tree(self._actions_tree)
137
+
138
+ def from_format(self, query_content: str, source_format: str) -> 'XWQueryScriptStrategy':
139
+ """Create script strategy from any supported format."""
140
+ strategy_class = self._get_strategy_class(source_format)
141
+ if not strategy_class:
142
+ raise ValueError(f"No strategy available for format: {source_format}")
143
+
144
+ strategy = strategy_class()
145
+ actions_tree = strategy.to_actions_tree(query_content)
146
+ self._actions_tree = actions_tree
147
+ return self
148
+
149
+ def _parse_xwquery_script(self, script_content: str) -> Dict[str, Any]:
150
+ """Parse XWQuery script into tree structure with nesting support."""
151
+ return {
152
+ "root": {
153
+ "type": "PROGRAM",
154
+ "statements": self._parse_statements(script_content),
155
+ "comments": self._extract_comments(script_content),
156
+ "metadata": {
157
+ "version": "1.0",
158
+ "created": datetime.now().isoformat(),
159
+ "source_format": "XWQUERY_SCRIPT"
160
+ }
161
+ }
162
+ }
163
+
164
+ def _parse_statements(self, script_content: str) -> List[Dict[str, Any]]:
165
+ """Parse individual statements with nesting support."""
166
+ statements = []
167
+ lines = script_content.split('\n')
168
+ current_statement = None
169
+
170
+ for line_num, line in enumerate(lines, 1):
171
+ line = line.strip()
172
+ if not line or line.startswith('--'):
173
+ continue
174
+
175
+ # Parse statement based on action type
176
+ for action_type in self.ACTION_TYPES:
177
+ if line.upper().startswith(action_type):
178
+ statement = self._parse_statement_line(line, action_type, line_num)
179
+ if statement:
180
+ statements.append(statement)
181
+ break
182
+
183
+ return statements
184
+
185
+ def _parse_statement_line(self, line: str, action_type: str, line_num: int) -> Optional[Dict[str, Any]]:
186
+ """Parse a single statement line."""
187
+ return {
188
+ "type": action_type,
189
+ "id": f"action_{line_num}",
190
+ "content": line,
191
+ "line_number": line_num,
192
+ "timestamp": datetime.now().isoformat(),
193
+ "children": [] # For nested actions
194
+ }
195
+
196
+ def _extract_comments(self, script_content: str) -> List[Dict[str, Any]]:
197
+ """Extract comments from script content."""
198
+ comments = []
199
+ lines = script_content.split('\n')
200
+
201
+ for line_num, line in enumerate(lines, 1):
202
+ if line.strip().startswith('--'):
203
+ comments.append({
204
+ "text": line.strip(),
205
+ "line_number": line_num,
206
+ "timestamp": datetime.now().isoformat()
207
+ })
208
+
209
+ return comments
210
+
211
+ def _extract_actions(self, query: str) -> List[str]:
212
+ """Extract action types from query."""
213
+ actions = []
214
+ query_upper = query.upper()
215
+
216
+ for action_type in self.ACTION_TYPES:
217
+ if action_type in query_upper:
218
+ actions.append(action_type)
219
+
220
+ return actions
221
+
222
+ def _estimate_complexity(self, query: str) -> str:
223
+ """Estimate query complexity."""
224
+ actions = self._extract_actions(query)
225
+
226
+ if len(actions) > 10:
227
+ return "HIGH"
228
+ elif len(actions) > 5:
229
+ return "MEDIUM"
230
+ else:
231
+ return "LOW"
232
+
233
+ def _estimate_cost(self, query: str) -> int:
234
+ """Estimate query cost."""
235
+ complexity = self._estimate_complexity(query)
236
+ if complexity == "HIGH":
237
+ return 100
238
+ elif complexity == "MEDIUM":
239
+ return 50
240
+ else:
241
+ return 10
242
+
243
+ def _get_optimization_hints(self, query: str) -> List[str]:
244
+ """Get query optimization hints."""
245
+ hints = []
246
+
247
+ if "SELECT *" in query.upper():
248
+ hints.append("Consider specifying columns instead of using *")
249
+ if "WHERE" not in query.upper() and "SELECT" in query.upper():
250
+ hints.append("Consider adding WHERE clause to limit results")
251
+
252
+ return hints
253
+
254
+ def _get_strategy_class(self, format_name: str) -> Optional[Type[AQueryStrategy]]:
255
+ """Get strategy class for format using XWNode's strategy registry."""
256
+ from ..registry import get_strategy_registry
257
+ registry = get_strategy_registry()
258
+ return registry.get_query_strategy(format_name.upper())
259
+
260
+ def _get_strategy_class_fallback(self, format_name: str) -> Optional[Type[AQueryStrategy]]:
261
+ """Fallback strategy class lookup."""
262
+ strategy_map = {
263
+ "SQL": "sql",
264
+ "GRAPHQL": "graphql",
265
+ "CYPHER": "cypher",
266
+ "SPARQL": "sparql",
267
+ "JSON_QUERY": "json_query",
268
+ "XML_QUERY": "xml_query",
269
+ "XPATH": "xpath",
270
+ "XQUERY": "xquery",
271
+ "JQ": "jq",
272
+ "JMESPATH": "jmespath",
273
+ "JSONIQ": "jsoniq",
274
+ "GREMLIN": "gremlin",
275
+ "ELASTIC_DSL": "elastic_dsl",
276
+ "EQL": "eql",
277
+ "FLUX": "flux",
278
+ "PROMQL": "promql",
279
+ "LOGQL": "logql",
280
+ "SPL": "spl",
281
+ "KQL": "kql",
282
+ "CQL": "cql",
283
+ "N1QL": "n1ql",
284
+ "HIVEQL": "hiveql",
285
+ "PIG": "pig",
286
+ "MQL": "mql",
287
+ "PARTIQL": "partiql",
288
+ "LINQ": "linq",
289
+ "HQL": "hql",
290
+ "DATALOG": "datalog",
291
+ "KSQL": "ksql",
292
+ "GQL": "gql"
293
+ }
294
+
295
+ module_name = strategy_map.get(format_name.upper())
296
+ if module_name:
297
+ try:
298
+ module = __import__(f'.{module_name}', fromlist=['.'], package=__package__)
299
+ strategy_class_name = f"{format_name.title()}Strategy"
300
+ return getattr(module, strategy_class_name, None)
301
+ except (ImportError, AttributeError):
302
+ pass
303
+
304
+ return None
305
+
306
+ def _execute_actions_tree(self, actions_tree: XWNodeBase, **kwargs) -> Any:
307
+ """Execute actions tree."""
308
+ # This would execute the parsed actions
309
+ # For now, return a mock result
310
+ return {
311
+ "result": "XWQuery Script executed successfully",
312
+ "actions_executed": len(actions_tree.get('root', {}).get('statements', [])),
313
+ "execution_time": "0.001s"
314
+ }
315
+
316
+ def add_action(self, action_type: str, **action_params) -> 'XWQueryScriptStrategy':
317
+ """Add an action to the actions tree with proper nesting."""
318
+ if action_type not in self.ACTION_TYPES:
319
+ raise ValueError(f"Unknown action type: {action_type}")
320
+
321
+ action = {
322
+ "type": action_type,
323
+ "id": f"action_{len(self._get_all_actions())}",
324
+ "params": action_params,
325
+ "timestamp": datetime.now().isoformat(),
326
+ "children": []
327
+ }
328
+
329
+ # Get current statements and add new action
330
+ root_data = self._actions_tree.to_native()
331
+ if 'root' not in root_data:
332
+ root_data['root'] = {"statements": [], "comments": [], "metadata": {}}
333
+
334
+ if 'statements' not in root_data['root']:
335
+ root_data['root']['statements'] = []
336
+
337
+ root_data['root']['statements'].append(action)
338
+
339
+ # Update the actions tree
340
+ self._actions_tree = XWNodeBase.from_native(root_data)
341
+
342
+ return self
343
+
344
+ def add_nested_action(self, parent_action_id: str, action_type: str, **action_params) -> 'XWQueryScriptStrategy':
345
+ """Add a nested action (e.g., subquery, JOIN condition)."""
346
+ parent = self._find_action_by_id(parent_action_id)
347
+ if parent:
348
+ child_action = {
349
+ "type": action_type,
350
+ "id": f"action_{len(self._get_all_actions())}",
351
+ "params": action_params,
352
+ "timestamp": datetime.now().isoformat(),
353
+ "children": []
354
+ }
355
+ parent.get('children', []).append(child_action)
356
+
357
+ return self
358
+
359
+ def _find_action_by_id(self, action_id: str) -> Optional[Dict[str, Any]]:
360
+ """Find action by ID in the tree."""
361
+ return self._search_tree(self._actions_tree, action_id)
362
+
363
+ def _search_tree(self, node: XWNodeBase, action_id: str) -> Optional[Dict[str, Any]]:
364
+ """Recursively search for action in tree."""
365
+ if isinstance(node, XWNodeBase):
366
+ node_data = node.to_native()
367
+ else:
368
+ node_data = node
369
+
370
+ if isinstance(node_data, dict):
371
+ if node_data.get('id') == action_id:
372
+ return node_data
373
+
374
+ for key, value in node_data.items():
375
+ if isinstance(value, (list, dict)):
376
+ result = self._search_tree(value, action_id)
377
+ if result:
378
+ return result
379
+
380
+ elif isinstance(node_data, list):
381
+ for item in node_data:
382
+ result = self._search_tree(item, action_id)
383
+ if result:
384
+ return result
385
+
386
+ return None
387
+
388
+ def _get_all_actions(self) -> List[Dict[str, Any]]:
389
+ """Get all actions from the tree (flattened)."""
390
+ actions = []
391
+ self._flatten_tree(self._actions_tree, actions)
392
+ return actions
393
+
394
+ def _flatten_tree(self, node: XWNodeBase, actions: List[Dict[str, Any]]):
395
+ """Flatten the tree to get all actions."""
396
+ if isinstance(node, XWNodeBase):
397
+ node_data = node.to_native()
398
+ else:
399
+ node_data = node
400
+
401
+ if isinstance(node_data, dict):
402
+ if 'type' in node_data and 'id' in node_data:
403
+ actions.append(node_data)
404
+
405
+ for value in node_data.values():
406
+ if isinstance(value, (list, dict)):
407
+ self._flatten_tree(value, actions)
408
+
409
+ elif isinstance(node_data, list):
410
+ for item in node_data:
411
+ self._flatten_tree(item, actions)
412
+
413
+ def get_actions_tree(self) -> XWNodeBase:
414
+ """Get the actions tree as XWNodeBase."""
415
+ return self._actions_tree
416
+
417
+ def to_native(self) -> Dict[str, Any]:
418
+ """Convert to native Python object."""
419
+ return {
420
+ "actions_tree": self._actions_tree.to_native(),
421
+ "comments": self._comments,
422
+ "metadata": self._metadata,
423
+ "action_types": self.ACTION_TYPES
424
+ }