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.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1434 @@
|
|
1
|
+
"""
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py
|
3
|
+
|
4
|
+
Tree Graph Hybrid Strategy Implementation
|
5
|
+
|
6
|
+
This module provides a unified tree engine strategy that contains all the XWNodeBase functionality
|
7
|
+
in one file, decoupled from XWNode. This eliminates the dual architecture and provides
|
8
|
+
a single, clean implementation.
|
9
|
+
"""
|
10
|
+
|
11
|
+
import threading
|
12
|
+
import copy
|
13
|
+
from abc import ABC, abstractmethod
|
14
|
+
from typing import Any, Union, List, Dict, Optional, Iterator, Tuple, Callable
|
15
|
+
from collections import OrderedDict
|
16
|
+
|
17
|
+
from ...types import NodeMode, NodeTrait
|
18
|
+
from ...abc import iNodeStrategy, NodeTrait
|
19
|
+
from ...errors import xNodePathError
|
20
|
+
from exonware.xwsystem import get_logger
|
21
|
+
|
22
|
+
logger = get_logger(__name__)
|
23
|
+
|
24
|
+
# Import shared utilities
|
25
|
+
from ..utils import (
|
26
|
+
PathParser, TrieNode, UnionFind, MinHeap,
|
27
|
+
create_path_parser, create_performance_tracker
|
28
|
+
)
|
29
|
+
|
30
|
+
logger = get_logger('xnode.tree_engine')
|
31
|
+
|
32
|
+
# ============================================================================
|
33
|
+
# TREE GRAPH HYBRID INTERNAL NODE CLASSES
|
34
|
+
# ============================================================================
|
35
|
+
|
36
|
+
class TreeGraphNode(ABC):
|
37
|
+
"""Abstract base for all internal nodes in the TreeGraphHybrid strategy."""
|
38
|
+
__slots__ = ('_parent', '_cached_native', '_hash')
|
39
|
+
|
40
|
+
def __init__(self, parent: Optional['TreeGraphNode'] = None):
|
41
|
+
self._parent: Optional['TreeGraphNode'] = parent
|
42
|
+
self._cached_native: Optional[Any] = None
|
43
|
+
self._hash: Optional[int] = None
|
44
|
+
|
45
|
+
@property
|
46
|
+
def parent(self) -> Optional['TreeGraphNode']:
|
47
|
+
"""Get the parent node."""
|
48
|
+
return self._parent
|
49
|
+
|
50
|
+
@parent.setter
|
51
|
+
def parent(self, value: Optional['TreeGraphNode']):
|
52
|
+
"""Set the parent node."""
|
53
|
+
self._parent = value
|
54
|
+
|
55
|
+
def _get_child(self, key_or_index: Union[str, int]) -> 'TreeGraphNode':
|
56
|
+
"""Get a child node by key or index."""
|
57
|
+
raise TypeError(f"Node type {type(self).__name__} does not support child access.")
|
58
|
+
|
59
|
+
@abstractmethod
|
60
|
+
def _to_native(self) -> Any:
|
61
|
+
"""Convert this node and its children to a native Python object."""
|
62
|
+
pass
|
63
|
+
|
64
|
+
def _invalidate_cache(self):
|
65
|
+
"""Invalidate cached data when the node changes."""
|
66
|
+
self._cached_native = None
|
67
|
+
self._hash = None
|
68
|
+
# Propagate invalidation up the tree
|
69
|
+
if self._parent:
|
70
|
+
self._parent._invalidate_cache()
|
71
|
+
|
72
|
+
def _get_root(self) -> 'TreeGraphNode':
|
73
|
+
"""Get the root node of the tree."""
|
74
|
+
current = self
|
75
|
+
while current._parent is not None:
|
76
|
+
current = current._parent
|
77
|
+
return current
|
78
|
+
|
79
|
+
def _get_key_in_parent(self) -> Optional[Union[str, int]]:
|
80
|
+
"""Get the key or index of this node in its parent."""
|
81
|
+
if self._parent is None:
|
82
|
+
return None
|
83
|
+
|
84
|
+
if isinstance(self._parent, TreeGraphDictNode):
|
85
|
+
for key, child in self._parent._children.items():
|
86
|
+
if child is self:
|
87
|
+
return key
|
88
|
+
elif isinstance(self._parent, TreeGraphListNode):
|
89
|
+
try:
|
90
|
+
return self._parent._children.index(self)
|
91
|
+
except ValueError:
|
92
|
+
pass
|
93
|
+
|
94
|
+
return None
|
95
|
+
|
96
|
+
def _get_path(self) -> str:
|
97
|
+
"""Get the path from the root to this node as a dot-separated string."""
|
98
|
+
if self._parent is None:
|
99
|
+
return ""
|
100
|
+
|
101
|
+
parent_path = self._parent._get_path()
|
102
|
+
key = self._get_key_in_parent()
|
103
|
+
|
104
|
+
if key is None:
|
105
|
+
return parent_path
|
106
|
+
|
107
|
+
if parent_path:
|
108
|
+
return f"{parent_path}.{key}"
|
109
|
+
else:
|
110
|
+
return str(key)
|
111
|
+
|
112
|
+
def cleanup(self) -> None:
|
113
|
+
"""Clean up the node before returning to pool."""
|
114
|
+
self._parent = None
|
115
|
+
self._cached_native = None
|
116
|
+
self._hash = None
|
117
|
+
|
118
|
+
def reset(self, parent: Optional['TreeGraphNode'] = None) -> None:
|
119
|
+
"""Reset the node to initial state."""
|
120
|
+
self._parent = parent
|
121
|
+
self._cached_native = None
|
122
|
+
self._hash = None
|
123
|
+
|
124
|
+
|
125
|
+
class TreeGraphValueNode(TreeGraphNode):
|
126
|
+
"""Internal node for a primitive value in TreeGraphHybrid strategy."""
|
127
|
+
__slots__ = ('_value',)
|
128
|
+
|
129
|
+
def __init__(self, value: Any, parent: Optional['TreeGraphNode'] = None):
|
130
|
+
super().__init__(parent)
|
131
|
+
self._value = value
|
132
|
+
|
133
|
+
@property
|
134
|
+
def value(self) -> Any:
|
135
|
+
"""Get the primitive value stored in this leaf node."""
|
136
|
+
return self._value
|
137
|
+
|
138
|
+
def _to_native(self) -> Any:
|
139
|
+
"""Convert this node to a native Python object."""
|
140
|
+
return self._value
|
141
|
+
|
142
|
+
|
143
|
+
class TreeGraphListNode(TreeGraphNode):
|
144
|
+
"""Internal node for a list with lazy-loading in TreeGraphHybrid strategy."""
|
145
|
+
__slots__ = ('_children', '_source_data', '_is_lazy')
|
146
|
+
|
147
|
+
def __init__(self, source_data: List[Any], is_lazy: bool, parent: Optional['TreeGraphNode'] = None):
|
148
|
+
super().__init__(parent)
|
149
|
+
self._source_data = source_data
|
150
|
+
self._is_lazy = is_lazy
|
151
|
+
self._children: List['TreeGraphNode'] = []
|
152
|
+
|
153
|
+
# Don't load children here to avoid recursion
|
154
|
+
# They will be loaded on-demand in _get_child, _to_native, etc.
|
155
|
+
|
156
|
+
def _eager_load(self, data: List[Any]) -> List['TreeGraphNode']:
|
157
|
+
"""Eagerly load all children."""
|
158
|
+
self._children = []
|
159
|
+
for item in data:
|
160
|
+
if item is None:
|
161
|
+
self._children.append(TreeGraphValueNode(None, self))
|
162
|
+
elif isinstance(item, (str, int, float, bool)):
|
163
|
+
self._children.append(TreeGraphValueNode(item, self))
|
164
|
+
elif isinstance(item, list):
|
165
|
+
is_lazy = len(item) > 100
|
166
|
+
self._children.append(TreeGraphListNode(item, is_lazy, self))
|
167
|
+
elif isinstance(item, dict):
|
168
|
+
is_lazy = len(item) > 100
|
169
|
+
self._children.append(TreeGraphDictNode(item, is_lazy, self))
|
170
|
+
else:
|
171
|
+
self._children.append(TreeGraphValueNode(item, self))
|
172
|
+
|
173
|
+
self._source_data = None # Clear source data after loading
|
174
|
+
return self._children
|
175
|
+
|
176
|
+
def _get_child(self, index: Union[str, int]) -> 'TreeGraphNode':
|
177
|
+
"""Get a child node by index."""
|
178
|
+
if isinstance(index, str):
|
179
|
+
try:
|
180
|
+
index = int(index)
|
181
|
+
except ValueError:
|
182
|
+
raise TypeError(f"List index must be an integer, got {index}")
|
183
|
+
|
184
|
+
if not isinstance(index, int):
|
185
|
+
raise TypeError(f"List index must be an integer, got {type(index).__name__}")
|
186
|
+
|
187
|
+
if index < 0:
|
188
|
+
index = len(self) + index
|
189
|
+
|
190
|
+
if index < 0 or index >= len(self):
|
191
|
+
raise IndexError(f"List index {index} out of range")
|
192
|
+
|
193
|
+
# Lazy load if needed
|
194
|
+
if self._source_data is not None:
|
195
|
+
self._eager_load(self._source_data)
|
196
|
+
|
197
|
+
return self._children[index]
|
198
|
+
|
199
|
+
def _to_native(self) -> List[Any]:
|
200
|
+
"""Convert this node and its children to a native Python object."""
|
201
|
+
# Lazy load if needed
|
202
|
+
if self._source_data is not None:
|
203
|
+
self._eager_load(self._source_data)
|
204
|
+
|
205
|
+
return [child._to_native() for child in self._children]
|
206
|
+
|
207
|
+
def __iter__(self) -> Iterator['TreeGraphNode']:
|
208
|
+
"""Iterate over child nodes."""
|
209
|
+
if self._source_data is not None:
|
210
|
+
self._eager_load(self._source_data)
|
211
|
+
return iter(self._children)
|
212
|
+
|
213
|
+
def __len__(self) -> int:
|
214
|
+
"""Get the number of child nodes."""
|
215
|
+
if self._source_data is not None:
|
216
|
+
return len(self._source_data)
|
217
|
+
return len(self._children)
|
218
|
+
|
219
|
+
|
220
|
+
class TreeGraphDictNode(TreeGraphNode):
|
221
|
+
"""Internal node for a dictionary with lazy-loading in TreeGraphHybrid strategy."""
|
222
|
+
__slots__ = ('_children', '_source_data', '_is_lazy', '_keys')
|
223
|
+
|
224
|
+
def __init__(self, source_data: Dict[str, Any], is_lazy: bool, parent: Optional['TreeGraphNode'] = None):
|
225
|
+
super().__init__(parent)
|
226
|
+
self._source_data = source_data
|
227
|
+
self._is_lazy = is_lazy
|
228
|
+
self._children: Dict[str, 'TreeGraphNode'] = {}
|
229
|
+
self._keys = list(source_data.keys())
|
230
|
+
|
231
|
+
# Don't load children here to avoid recursion
|
232
|
+
# They will be loaded on-demand in _get_child, _to_native, etc.
|
233
|
+
|
234
|
+
def _eager_load(self, data: Dict[str, Any]) -> Dict[str, 'TreeGraphNode']:
|
235
|
+
"""Eagerly load all children."""
|
236
|
+
# Don't clear existing children if we're loading from empty data
|
237
|
+
if not data:
|
238
|
+
self._source_data = None
|
239
|
+
return self._children
|
240
|
+
|
241
|
+
self._children = {}
|
242
|
+
for key, value in data.items():
|
243
|
+
if value is None:
|
244
|
+
self._children[key] = TreeGraphValueNode(None, self)
|
245
|
+
elif isinstance(value, (str, int, float, bool)):
|
246
|
+
self._children[key] = TreeGraphValueNode(value, self)
|
247
|
+
elif isinstance(value, list):
|
248
|
+
is_lazy = len(value) > 100
|
249
|
+
self._children[key] = TreeGraphListNode(value, is_lazy, self)
|
250
|
+
elif isinstance(value, dict):
|
251
|
+
is_lazy = len(value) > 100
|
252
|
+
self._children[key] = TreeGraphDictNode(value, is_lazy, self)
|
253
|
+
else:
|
254
|
+
self._children[key] = TreeGraphValueNode(value, self)
|
255
|
+
|
256
|
+
self._source_data = None # Clear source data after loading
|
257
|
+
return self._children
|
258
|
+
|
259
|
+
def _get_child(self, key: Union[str, int]) -> 'TreeGraphNode':
|
260
|
+
"""Get a child node by key."""
|
261
|
+
if not isinstance(key, str):
|
262
|
+
raise TypeError(f"Dictionary key must be a string, got {type(key).__name__}")
|
263
|
+
|
264
|
+
# Lazy load if needed
|
265
|
+
if self._source_data is not None:
|
266
|
+
self._eager_load(self._source_data)
|
267
|
+
|
268
|
+
# Check if key exists in children
|
269
|
+
if key not in self._children:
|
270
|
+
raise xNodePathError(f"Key '{key}' not found in dictionary")
|
271
|
+
|
272
|
+
return self._children[key]
|
273
|
+
|
274
|
+
def _to_native(self) -> Dict[str, Any]:
|
275
|
+
"""Convert this node and its children to a native Python object."""
|
276
|
+
# Lazy load if needed
|
277
|
+
if self._source_data is not None:
|
278
|
+
self._eager_load(self._source_data)
|
279
|
+
|
280
|
+
return {key: child._to_native() for key, child in self._children.items()}
|
281
|
+
|
282
|
+
def items(self) -> Iterator[Tuple[str, 'TreeGraphNode']]:
|
283
|
+
"""Iterate over key-value pairs."""
|
284
|
+
if self._source_data is not None:
|
285
|
+
self._eager_load(self._source_data)
|
286
|
+
return self._children.items()
|
287
|
+
|
288
|
+
def __iter__(self) -> Iterator['TreeGraphNode']:
|
289
|
+
"""Iterate over child nodes."""
|
290
|
+
if self._source_data is not None:
|
291
|
+
self._eager_load(self._source_data)
|
292
|
+
return iter(self._children.values())
|
293
|
+
|
294
|
+
def __len__(self) -> int:
|
295
|
+
"""Get the number of child nodes."""
|
296
|
+
if self._source_data is not None:
|
297
|
+
return len(self._source_data)
|
298
|
+
return len(self._children)
|
299
|
+
|
300
|
+
def keys(self) -> Iterator[str]:
|
301
|
+
"""Iterate over keys."""
|
302
|
+
if self._source_data is not None:
|
303
|
+
return iter(self._source_data.keys())
|
304
|
+
return iter(self._children.keys())
|
305
|
+
|
306
|
+
|
307
|
+
class TreeGraphReferenceNode(TreeGraphNode):
|
308
|
+
"""Internal node for a reference in TreeGraphHybrid strategy."""
|
309
|
+
__slots__ = ('_uri', '_reference_type', '_metadata')
|
310
|
+
|
311
|
+
def __init__(self, uri: str, reference_type: str, metadata: Dict[str, Any], parent: Optional['TreeGraphNode'] = None):
|
312
|
+
super().__init__(parent)
|
313
|
+
self._uri = uri
|
314
|
+
self._reference_type = reference_type
|
315
|
+
self._metadata = metadata or {}
|
316
|
+
|
317
|
+
@property
|
318
|
+
def uri(self) -> str:
|
319
|
+
"""Get the URI of the reference."""
|
320
|
+
return self._uri
|
321
|
+
|
322
|
+
@property
|
323
|
+
def reference_type(self) -> str:
|
324
|
+
"""Get the type of the reference."""
|
325
|
+
return self._reference_type
|
326
|
+
|
327
|
+
@property
|
328
|
+
def metadata(self) -> Dict[str, Any]:
|
329
|
+
"""Get the metadata of the reference."""
|
330
|
+
return self._metadata
|
331
|
+
|
332
|
+
def _to_native(self) -> Dict[str, Any]:
|
333
|
+
"""Convert this node to a native Python object."""
|
334
|
+
return {
|
335
|
+
'type': 'reference',
|
336
|
+
'uri': self._uri,
|
337
|
+
'reference_type': self._reference_type,
|
338
|
+
'metadata': self._metadata
|
339
|
+
}
|
340
|
+
|
341
|
+
def cleanup(self) -> None:
|
342
|
+
"""Clean up the node before returning to pool."""
|
343
|
+
super().cleanup()
|
344
|
+
self._uri = ""
|
345
|
+
self._reference_type = ""
|
346
|
+
self._metadata = {}
|
347
|
+
|
348
|
+
def reset(self, uri: str, reference_type: str, metadata: Dict[str, Any], parent: Optional['TreeGraphNode'] = None) -> None:
|
349
|
+
"""Reset the node to initial state."""
|
350
|
+
super().reset(parent)
|
351
|
+
self._uri = uri
|
352
|
+
self._reference_type = reference_type
|
353
|
+
self._metadata = metadata or {}
|
354
|
+
|
355
|
+
|
356
|
+
class TreeGraphObjectNode(TreeGraphNode):
|
357
|
+
"""Internal node for an object reference in TreeGraphHybrid strategy."""
|
358
|
+
__slots__ = ('_uri', '_object_type', '_mime_type', '_size', '_metadata')
|
359
|
+
|
360
|
+
def __init__(self, uri: str, object_type: str, mime_type: Optional[str], size: Optional[int], metadata: Dict[str, Any], parent: Optional['TreeGraphNode'] = None):
|
361
|
+
super().__init__(parent)
|
362
|
+
self._uri = uri
|
363
|
+
self._object_type = object_type
|
364
|
+
self._mime_type = mime_type
|
365
|
+
self._size = size
|
366
|
+
self._metadata = metadata or {}
|
367
|
+
|
368
|
+
@property
|
369
|
+
def uri(self) -> str:
|
370
|
+
"""Get the URI of the object."""
|
371
|
+
return self._uri
|
372
|
+
|
373
|
+
@property
|
374
|
+
def object_type(self) -> str:
|
375
|
+
"""Get the type of the object."""
|
376
|
+
return self._object_type
|
377
|
+
|
378
|
+
@property
|
379
|
+
def mime_type(self) -> Optional[str]:
|
380
|
+
"""Get the MIME type of the object."""
|
381
|
+
return self._mime_type
|
382
|
+
|
383
|
+
@property
|
384
|
+
def size(self) -> Optional[int]:
|
385
|
+
"""Get the size of the object."""
|
386
|
+
return self._size
|
387
|
+
|
388
|
+
@property
|
389
|
+
def metadata(self) -> Dict[str, Any]:
|
390
|
+
"""Get the metadata of the object."""
|
391
|
+
return self._metadata
|
392
|
+
|
393
|
+
def _to_native(self) -> Dict[str, Any]:
|
394
|
+
"""Convert this node to a native Python object."""
|
395
|
+
result = {
|
396
|
+
'type': 'object',
|
397
|
+
'uri': self._uri,
|
398
|
+
'object_type': self._object_type,
|
399
|
+
'metadata': self._metadata
|
400
|
+
}
|
401
|
+
if self._mime_type:
|
402
|
+
result['mime_type'] = self._mime_type
|
403
|
+
if self._size is not None:
|
404
|
+
result['size'] = self._size
|
405
|
+
return result
|
406
|
+
|
407
|
+
def cleanup(self) -> None:
|
408
|
+
"""Clean up the node before returning to pool."""
|
409
|
+
super().cleanup()
|
410
|
+
self._uri = ""
|
411
|
+
self._object_type = ""
|
412
|
+
self._mime_type = None
|
413
|
+
self._size = None
|
414
|
+
self._metadata = {}
|
415
|
+
|
416
|
+
def reset(self, uri: str, object_type: str, mime_type: Optional[str], size: Optional[int], metadata: Dict[str, Any], parent: Optional['TreeGraphNode'] = None) -> None:
|
417
|
+
"""Reset the node to initial state."""
|
418
|
+
super().reset(parent)
|
419
|
+
self._uri = uri
|
420
|
+
self._object_type = object_type
|
421
|
+
self._mime_type = mime_type
|
422
|
+
self._size = size
|
423
|
+
self._metadata = metadata or {}
|
424
|
+
|
425
|
+
|
426
|
+
# ============================================================================
|
427
|
+
# TREE GRAPH HYBRID NODE FACTORY
|
428
|
+
# ============================================================================
|
429
|
+
|
430
|
+
class TreeGraphNodeFactory:
|
431
|
+
"""Factory for creating TreeGraphHybrid internal nodes with performance optimizations."""
|
432
|
+
|
433
|
+
@staticmethod
|
434
|
+
def from_native(data: Any, parent: Optional[TreeGraphNode] = None, depth: int = 0, visited: Optional[set] = None) -> TreeGraphNode:
|
435
|
+
"""Create a node from native Python data."""
|
436
|
+
# Check depth limit
|
437
|
+
if depth > 1000: # Simple depth limit
|
438
|
+
raise ValueError("Maximum depth exceeded")
|
439
|
+
|
440
|
+
# Initialize visited set for circular reference detection
|
441
|
+
if visited is None:
|
442
|
+
visited = set()
|
443
|
+
|
444
|
+
# Check for circular references
|
445
|
+
if id(data) in visited:
|
446
|
+
# Return a placeholder node for circular references
|
447
|
+
return TreeGraphValueNode(f"<circular_reference_{id(data)}>", parent)
|
448
|
+
|
449
|
+
# Add current object to visited set
|
450
|
+
visited.add(id(data))
|
451
|
+
|
452
|
+
try:
|
453
|
+
if data is None:
|
454
|
+
node = TreeGraphValueNode(None, parent)
|
455
|
+
elif isinstance(data, (str, int, float, bool)):
|
456
|
+
node = TreeGraphValueNode(data, parent)
|
457
|
+
elif isinstance(data, list):
|
458
|
+
# Determine if lazy loading should be used
|
459
|
+
is_lazy = len(data) > 100
|
460
|
+
node = TreeGraphListNode(data, is_lazy, parent)
|
461
|
+
elif isinstance(data, dict):
|
462
|
+
# Determine if lazy loading should be used
|
463
|
+
is_lazy = len(data) > 100
|
464
|
+
node = TreeGraphDictNode(data, is_lazy, parent)
|
465
|
+
else:
|
466
|
+
# For other types, create a value node
|
467
|
+
node = TreeGraphValueNode(data, parent)
|
468
|
+
|
469
|
+
return node
|
470
|
+
finally:
|
471
|
+
# Remove current object from visited set when done
|
472
|
+
visited.discard(id(data))
|
473
|
+
|
474
|
+
@staticmethod
|
475
|
+
def to_native(node: TreeGraphNode) -> Any:
|
476
|
+
"""Convert a node to native Python data."""
|
477
|
+
return node._to_native()
|
478
|
+
|
479
|
+
|
480
|
+
# ============================================================================
|
481
|
+
# PATH PARSER (Moved from core.py)
|
482
|
+
# ============================================================================
|
483
|
+
|
484
|
+
class PathParser:
|
485
|
+
"""Thread-safe path parser with caching."""
|
486
|
+
|
487
|
+
def __init__(self, max_cache_size: int = 1024):
|
488
|
+
self._cache = OrderedDict()
|
489
|
+
self._max_cache_size = max_cache_size
|
490
|
+
self._lock = threading.RLock()
|
491
|
+
|
492
|
+
def parse(self, path: str) -> List[str]:
|
493
|
+
"""Parse a path string into parts."""
|
494
|
+
with self._lock:
|
495
|
+
if path in self._cache:
|
496
|
+
return self._cache[path]
|
497
|
+
|
498
|
+
parts = self._parse_path(path)
|
499
|
+
|
500
|
+
# Cache the result
|
501
|
+
if len(self._cache) >= self._max_cache_size:
|
502
|
+
self._cache.popitem(last=False)
|
503
|
+
self._cache[path] = parts
|
504
|
+
|
505
|
+
return parts
|
506
|
+
|
507
|
+
def _parse_path(self, path: str) -> List[str]:
|
508
|
+
"""Internal path parsing logic."""
|
509
|
+
if not path:
|
510
|
+
return []
|
511
|
+
|
512
|
+
# Simple dot-separated path parsing
|
513
|
+
return [part for part in path.split('.') if part]
|
514
|
+
|
515
|
+
|
516
|
+
# ============================================================================
|
517
|
+
# TREE GRAPH HYBRID STRATEGY
|
518
|
+
# ============================================================================
|
519
|
+
|
520
|
+
class TreeGraphHybridStrategy(iNodeStrategy):
|
521
|
+
"""
|
522
|
+
Unified TreeGraphHybrid strategy combining aNode model with advanced data structures.
|
523
|
+
|
524
|
+
This strategy provides:
|
525
|
+
- Tree-based navigation with graph capabilities
|
526
|
+
- Advanced data structures (Trie, Heap, Union-Find, etc.)
|
527
|
+
- Performance optimizations and caching
|
528
|
+
- Circular reference detection
|
529
|
+
- Lazy loading and object pooling
|
530
|
+
"""
|
531
|
+
|
532
|
+
def __init__(self):
|
533
|
+
"""Initialize the TreeGraphHybrid strategy."""
|
534
|
+
self._root: Optional[TreeGraphNode] = None
|
535
|
+
self._node_count = 0
|
536
|
+
self._edge_count = 0
|
537
|
+
self._cache = {}
|
538
|
+
self._size = 0
|
539
|
+
self._performance_stats = {
|
540
|
+
'cache_hits': 0,
|
541
|
+
'cache_misses': 0,
|
542
|
+
'operations': 0
|
543
|
+
}
|
544
|
+
# Initialize shared utilities
|
545
|
+
self._path_parser = create_path_parser()
|
546
|
+
self._performance_tracker = create_performance_tracker()
|
547
|
+
|
548
|
+
@property
|
549
|
+
def strategy_name(self) -> str:
|
550
|
+
"""Get the name of this strategy."""
|
551
|
+
return "tree_graph_hybrid"
|
552
|
+
|
553
|
+
@property
|
554
|
+
def supported_traits(self) -> List[NodeTrait]:
|
555
|
+
"""Get supported traits for this strategy."""
|
556
|
+
return [
|
557
|
+
NodeTrait.LAZY_LOADING,
|
558
|
+
NodeTrait.OBJECT_POOLING,
|
559
|
+
NodeTrait.CIRCULAR_REF_DETECTION,
|
560
|
+
NodeTrait.PERFORMANCE_TRACKING,
|
561
|
+
NodeTrait.GRAPH_CAPABILITIES,
|
562
|
+
NodeTrait.QUERY_CAPABILITIES,
|
563
|
+
NodeTrait.SECURITY_FEATURES
|
564
|
+
]
|
565
|
+
|
566
|
+
def create_from_data(self, data: Any) -> 'TreeGraphHybridStrategy':
|
567
|
+
"""Create the tree from data."""
|
568
|
+
self._root = TreeGraphNodeFactory.from_native(data)
|
569
|
+
self._size = self._calculate_size(self._root)
|
570
|
+
# Force eager loading for immediate access
|
571
|
+
if isinstance(self._root, TreeGraphDictNode) and self._root._source_data is not None:
|
572
|
+
self._root._eager_load(self._root._source_data)
|
573
|
+
elif isinstance(self._root, TreeGraphListNode) and self._root._source_data is not None:
|
574
|
+
self._root._eager_load(self._root._source_data)
|
575
|
+
return self
|
576
|
+
|
577
|
+
def _calculate_size(self, node: TreeGraphNode) -> int:
|
578
|
+
"""Calculate the size of the tree."""
|
579
|
+
if isinstance(node, TreeGraphValueNode):
|
580
|
+
return 1
|
581
|
+
elif isinstance(node, TreeGraphListNode):
|
582
|
+
# For lists, return the number of items
|
583
|
+
if node._source_data is not None:
|
584
|
+
return len(node._source_data)
|
585
|
+
else:
|
586
|
+
return len(node._children)
|
587
|
+
elif isinstance(node, TreeGraphDictNode):
|
588
|
+
# For dictionaries, return the number of key-value pairs
|
589
|
+
if node._source_data is not None:
|
590
|
+
return len(node._source_data)
|
591
|
+
else:
|
592
|
+
return len(node._children)
|
593
|
+
else:
|
594
|
+
return 1
|
595
|
+
|
596
|
+
def get_native(self, key: str, default: Any = None) -> Any:
|
597
|
+
"""Get a native Python value by key."""
|
598
|
+
self._performance_tracker.record_access()
|
599
|
+
|
600
|
+
try:
|
601
|
+
if self._root is None:
|
602
|
+
return default
|
603
|
+
|
604
|
+
# Handle path navigation
|
605
|
+
if '.' in key:
|
606
|
+
return self._get_by_path(key, default)
|
607
|
+
|
608
|
+
# Handle direct key access
|
609
|
+
if isinstance(self._root, TreeGraphDictNode):
|
610
|
+
child = self._root._get_child(key)
|
611
|
+
return child._to_native()
|
612
|
+
elif isinstance(self._root, TreeGraphListNode):
|
613
|
+
try:
|
614
|
+
index = int(key)
|
615
|
+
child = self._root._get_child(index)
|
616
|
+
return child._to_native()
|
617
|
+
except ValueError:
|
618
|
+
return default
|
619
|
+
elif isinstance(self._root, TreeGraphValueNode):
|
620
|
+
# If root is a value node, only return it if key is empty or matches
|
621
|
+
if key == "" or key == "value":
|
622
|
+
return self._root._to_native()
|
623
|
+
return default
|
624
|
+
else:
|
625
|
+
return default
|
626
|
+
|
627
|
+
except (KeyError, IndexError, TypeError):
|
628
|
+
return default
|
629
|
+
|
630
|
+
def _get_by_path(self, path: str, default: Any = None) -> Any:
|
631
|
+
"""Get a value by path."""
|
632
|
+
if self._root is None:
|
633
|
+
return default
|
634
|
+
|
635
|
+
try:
|
636
|
+
segments = self._path_parser.parse(path)
|
637
|
+
current = self._root
|
638
|
+
|
639
|
+
for segment in segments:
|
640
|
+
if isinstance(current, TreeGraphDictNode):
|
641
|
+
current = current._get_child(segment)
|
642
|
+
elif isinstance(current, TreeGraphListNode):
|
643
|
+
try:
|
644
|
+
index = int(segment)
|
645
|
+
current = current._get_child(index)
|
646
|
+
except ValueError:
|
647
|
+
return default
|
648
|
+
else:
|
649
|
+
return default
|
650
|
+
|
651
|
+
return current._to_native()
|
652
|
+
|
653
|
+
except (KeyError, IndexError, TypeError):
|
654
|
+
return default
|
655
|
+
|
656
|
+
def _get_by_key(self, key: str, default: Any = None) -> Any:
|
657
|
+
"""Get a value by direct key."""
|
658
|
+
if self._root is None:
|
659
|
+
return default
|
660
|
+
|
661
|
+
try:
|
662
|
+
if isinstance(self._root, TreeGraphDictNode):
|
663
|
+
child = self._root._get_child(key)
|
664
|
+
return child._to_native()
|
665
|
+
elif isinstance(self._root, TreeGraphListNode):
|
666
|
+
try:
|
667
|
+
index = int(key)
|
668
|
+
child = self._root._get_child(index)
|
669
|
+
return child._to_native()
|
670
|
+
except ValueError:
|
671
|
+
return default
|
672
|
+
elif isinstance(self._root, TreeGraphValueNode):
|
673
|
+
# If root is a value node, only return it if key is empty or matches
|
674
|
+
if key == "" or key == "value":
|
675
|
+
return self._root._to_native()
|
676
|
+
return default
|
677
|
+
else:
|
678
|
+
return default
|
679
|
+
|
680
|
+
except (KeyError, IndexError, TypeError):
|
681
|
+
return default
|
682
|
+
|
683
|
+
def put(self, key: str, value: Any) -> None:
|
684
|
+
"""Set a value by key."""
|
685
|
+
self._performance_tracker.record_access()
|
686
|
+
|
687
|
+
if self._root is None:
|
688
|
+
# Create root if it doesn't exist
|
689
|
+
self._root = TreeGraphDictNode({}, False)
|
690
|
+
|
691
|
+
# Handle path navigation
|
692
|
+
if '.' in key:
|
693
|
+
self._put_by_path(key, value)
|
694
|
+
else:
|
695
|
+
# Handle direct key access
|
696
|
+
if isinstance(self._root, TreeGraphDictNode):
|
697
|
+
# Create child node directly to avoid recursion
|
698
|
+
if value is None:
|
699
|
+
child = TreeGraphValueNode(None, self._root)
|
700
|
+
elif isinstance(value, (str, int, float, bool)):
|
701
|
+
child = TreeGraphValueNode(value, self._root)
|
702
|
+
elif isinstance(value, list):
|
703
|
+
is_lazy = len(value) > 100
|
704
|
+
child = TreeGraphListNode(value, is_lazy, self._root)
|
705
|
+
elif isinstance(value, dict):
|
706
|
+
is_lazy = len(value) > 100
|
707
|
+
child = TreeGraphDictNode(value, is_lazy, self._root)
|
708
|
+
else:
|
709
|
+
child = TreeGraphValueNode(value, self._root)
|
710
|
+
|
711
|
+
self._root._children[key] = child
|
712
|
+
self._root._invalidate_cache()
|
713
|
+
|
714
|
+
def _put_by_path(self, path: str, value: Any) -> None:
|
715
|
+
"""Set a value by path."""
|
716
|
+
if self._root is None:
|
717
|
+
self._root = TreeGraphDictNode({}, False)
|
718
|
+
|
719
|
+
try:
|
720
|
+
segments = self._path_parser.parse(path)
|
721
|
+
current = self._root
|
722
|
+
|
723
|
+
# Navigate to parent of target
|
724
|
+
for segment in segments[:-1]:
|
725
|
+
if isinstance(current, TreeGraphDictNode):
|
726
|
+
if segment not in current._children:
|
727
|
+
current._children[segment] = TreeGraphDictNode({}, False)
|
728
|
+
current = current._children[segment]
|
729
|
+
elif isinstance(current, TreeGraphListNode):
|
730
|
+
try:
|
731
|
+
index = int(segment)
|
732
|
+
while len(current._children) <= index:
|
733
|
+
current._children.append(TreeGraphValueNode(None, current))
|
734
|
+
current = current._children[index]
|
735
|
+
except ValueError:
|
736
|
+
return
|
737
|
+
else:
|
738
|
+
return
|
739
|
+
|
740
|
+
# Set the final value
|
741
|
+
final_segment = segments[-1]
|
742
|
+
if isinstance(current, TreeGraphDictNode):
|
743
|
+
# Create child node directly to avoid recursion
|
744
|
+
if value is None:
|
745
|
+
child = TreeGraphValueNode(None, current)
|
746
|
+
elif isinstance(value, (str, int, float, bool)):
|
747
|
+
child = TreeGraphValueNode(value, current)
|
748
|
+
elif isinstance(value, list):
|
749
|
+
is_lazy = len(value) > 100
|
750
|
+
child = TreeGraphListNode(value, is_lazy, current)
|
751
|
+
elif isinstance(value, dict):
|
752
|
+
is_lazy = len(value) > 100
|
753
|
+
child = TreeGraphDictNode(value, is_lazy, current)
|
754
|
+
else:
|
755
|
+
child = TreeGraphValueNode(value, current)
|
756
|
+
|
757
|
+
current._children[final_segment] = child
|
758
|
+
current._invalidate_cache()
|
759
|
+
elif isinstance(current, TreeGraphListNode):
|
760
|
+
try:
|
761
|
+
index = int(final_segment)
|
762
|
+
while len(current._children) <= index:
|
763
|
+
current._children.append(TreeGraphValueNode(None, current))
|
764
|
+
|
765
|
+
# Create child node directly to avoid recursion
|
766
|
+
if value is None:
|
767
|
+
child = TreeGraphValueNode(None, current)
|
768
|
+
elif isinstance(value, (str, int, float, bool)):
|
769
|
+
child = TreeGraphValueNode(value, current)
|
770
|
+
elif isinstance(value, list):
|
771
|
+
is_lazy = len(value) > 100
|
772
|
+
child = TreeGraphListNode(value, is_lazy, current)
|
773
|
+
elif isinstance(value, dict):
|
774
|
+
is_lazy = len(value) > 100
|
775
|
+
child = TreeGraphDictNode(value, is_lazy, current)
|
776
|
+
else:
|
777
|
+
child = TreeGraphValueNode(value, current)
|
778
|
+
|
779
|
+
current._children[index] = child
|
780
|
+
current._invalidate_cache()
|
781
|
+
except ValueError:
|
782
|
+
return
|
783
|
+
|
784
|
+
except (KeyError, IndexError, TypeError):
|
785
|
+
pass
|
786
|
+
|
787
|
+
def has(self, key: str) -> bool:
|
788
|
+
"""Check if key exists."""
|
789
|
+
return self.get_native(key) is not None
|
790
|
+
|
791
|
+
def delete(self, key: str) -> bool:
|
792
|
+
"""Remove a key-value pair."""
|
793
|
+
# Implementation for deletion
|
794
|
+
# This would need to handle both direct keys and paths
|
795
|
+
return False
|
796
|
+
|
797
|
+
def remove(self, key: str) -> bool:
|
798
|
+
"""Remove a key-value pair (alias for delete)."""
|
799
|
+
return self.delete(key)
|
800
|
+
|
801
|
+
def clear(self) -> None:
|
802
|
+
"""Clear all data."""
|
803
|
+
self._root = None
|
804
|
+
self._size = 0
|
805
|
+
|
806
|
+
def keys(self) -> Iterator[str]:
|
807
|
+
"""Get all keys."""
|
808
|
+
if self._root is None or not isinstance(self._root, TreeGraphDictNode):
|
809
|
+
return iter([])
|
810
|
+
# Trigger lazy loading if needed
|
811
|
+
if self._root._source_data is not None:
|
812
|
+
self._root._eager_load(self._root._source_data)
|
813
|
+
return self._root.keys()
|
814
|
+
|
815
|
+
def values(self) -> Iterator[Any]:
|
816
|
+
"""Get all values."""
|
817
|
+
if self._root is None or not isinstance(self._root, TreeGraphDictNode):
|
818
|
+
return iter([])
|
819
|
+
# Trigger lazy loading if needed
|
820
|
+
if self._root._source_data is not None:
|
821
|
+
self._root._eager_load(self._root._source_data)
|
822
|
+
return (child._to_native() for child in self._root._children.values())
|
823
|
+
|
824
|
+
def items(self) -> Iterator[Tuple[str, Any]]:
|
825
|
+
"""Get all key-value pairs."""
|
826
|
+
if self._root is None or not isinstance(self._root, TreeGraphDictNode):
|
827
|
+
return iter([])
|
828
|
+
# Trigger lazy loading if needed
|
829
|
+
if self._root._source_data is not None:
|
830
|
+
self._root._eager_load(self._root._source_data)
|
831
|
+
return ((key, child._to_native()) for key, child in self._root._children.items())
|
832
|
+
|
833
|
+
def __len__(self) -> int:
|
834
|
+
"""Get the number of items."""
|
835
|
+
return self._size
|
836
|
+
|
837
|
+
def to_native(self) -> Any:
|
838
|
+
"""Convert to native Python object."""
|
839
|
+
if self._root is None:
|
840
|
+
return {}
|
841
|
+
return self._root._to_native()
|
842
|
+
|
843
|
+
def backend_info(self) -> Dict[str, Any]:
|
844
|
+
"""Get information about the backend implementation."""
|
845
|
+
return {
|
846
|
+
"strategy": "TREE_GRAPH_HYBRID",
|
847
|
+
"backend": "TreeGraphNode tree with lazy loading and advanced data structures",
|
848
|
+
"complexity": {
|
849
|
+
"get": "O(depth)",
|
850
|
+
"put": "O(depth)",
|
851
|
+
"has": "O(depth)",
|
852
|
+
"delete": "O(depth)",
|
853
|
+
"union_find": "O(α(n))",
|
854
|
+
"trie": "O(k) where k = word length",
|
855
|
+
"heap": "O(log n)"
|
856
|
+
},
|
857
|
+
"features": [
|
858
|
+
"lazy_loading",
|
859
|
+
"object_pooling",
|
860
|
+
"caching",
|
861
|
+
"tree_navigation",
|
862
|
+
"path_parsing",
|
863
|
+
"union_find",
|
864
|
+
"trie_operations",
|
865
|
+
"priority_queue",
|
866
|
+
"advanced_traits"
|
867
|
+
]
|
868
|
+
}
|
869
|
+
|
870
|
+
def metrics(self) -> Dict[str, Any]:
|
871
|
+
"""Get performance metrics."""
|
872
|
+
metrics = self._performance_tracker.get_metrics()
|
873
|
+
metrics.update({
|
874
|
+
"size": self._size,
|
875
|
+
"has_root": self._root is not None
|
876
|
+
})
|
877
|
+
return metrics
|
878
|
+
|
879
|
+
# ============================================================================
|
880
|
+
# ADVANCED DATA STRUCTURE OPERATIONS
|
881
|
+
# ============================================================================
|
882
|
+
|
883
|
+
def union_find_make_set(self, x: Any) -> None:
|
884
|
+
"""Create new set with element x in Union-Find structure."""
|
885
|
+
uf = self._get_union_find()
|
886
|
+
uf.make_set(x)
|
887
|
+
self._save_union_find()
|
888
|
+
|
889
|
+
def union_find_find(self, x: Any) -> Any:
|
890
|
+
"""Find root of set containing x in Union-Find structure."""
|
891
|
+
uf = self._get_union_find()
|
892
|
+
return uf.find(x)
|
893
|
+
|
894
|
+
def union_find_union(self, x: Any, y: Any) -> None:
|
895
|
+
"""Union sets containing x and y in Union-Find structure."""
|
896
|
+
uf = self._get_union_find()
|
897
|
+
uf.union(x, y)
|
898
|
+
self._save_union_find()
|
899
|
+
|
900
|
+
def union_find_connected(self, x: Any, y: Any) -> bool:
|
901
|
+
"""Check if x and y are in same set in Union-Find structure."""
|
902
|
+
uf = self._get_union_find()
|
903
|
+
return uf.connected(x, y)
|
904
|
+
|
905
|
+
def union_find_size(self) -> int:
|
906
|
+
"""Get number of elements in Union-Find structure."""
|
907
|
+
uf = self._get_union_find()
|
908
|
+
return uf.size()
|
909
|
+
|
910
|
+
def union_find_sets_count(self) -> int:
|
911
|
+
"""Get number of disjoint sets in Union-Find structure."""
|
912
|
+
uf = self._get_union_find()
|
913
|
+
return uf.sets_count()
|
914
|
+
|
915
|
+
def trie_insert(self, word: str, value: Any = None) -> None:
|
916
|
+
"""Insert word into Trie structure."""
|
917
|
+
if not isinstance(word, str):
|
918
|
+
raise ValueError(f"Word must be string, got {type(word)}")
|
919
|
+
self._insert_trie_word(word, value)
|
920
|
+
self._save_trie()
|
921
|
+
|
922
|
+
def trie_contains(self, word: str) -> bool:
|
923
|
+
"""Check if word exists in Trie structure."""
|
924
|
+
if not isinstance(word, str):
|
925
|
+
return False
|
926
|
+
|
927
|
+
current = self._get_trie()
|
928
|
+
for char in word:
|
929
|
+
if char not in current.children:
|
930
|
+
return False
|
931
|
+
current = current.children[char]
|
932
|
+
|
933
|
+
return current.is_end_word
|
934
|
+
|
935
|
+
def trie_get(self, word: str) -> Any:
|
936
|
+
"""Get value associated with word in Trie structure."""
|
937
|
+
if not isinstance(word, str):
|
938
|
+
raise ValueError(f"Word must be string, got {type(word)}")
|
939
|
+
|
940
|
+
current = self._get_trie()
|
941
|
+
for char in word:
|
942
|
+
if char not in current.children:
|
943
|
+
raise ValueError(f"Word '{word}' not found in trie")
|
944
|
+
current = current.children[char]
|
945
|
+
|
946
|
+
return current.value
|
947
|
+
|
948
|
+
def trie_starts_with(self, prefix: str) -> List[str]:
|
949
|
+
"""Find all words starting with prefix in Trie structure."""
|
950
|
+
if not isinstance(prefix, str):
|
951
|
+
return []
|
952
|
+
|
953
|
+
current = self._get_trie()
|
954
|
+
for char in prefix:
|
955
|
+
if char not in current.children:
|
956
|
+
return []
|
957
|
+
current = current.children[char]
|
958
|
+
|
959
|
+
# Collect all words from this node
|
960
|
+
result = {}
|
961
|
+
self._collect_trie_words(current, prefix, result)
|
962
|
+
return list(result.keys())
|
963
|
+
|
964
|
+
def heap_push(self, value: Any, priority: float = 0.0) -> None:
|
965
|
+
"""Push item with priority into MinHeap."""
|
966
|
+
heap = self._get_heap()
|
967
|
+
heap.push(value, priority)
|
968
|
+
self._save_heap()
|
969
|
+
|
970
|
+
def heap_pop_min(self) -> Any:
|
971
|
+
"""Pop minimum priority item from MinHeap."""
|
972
|
+
heap = self._get_heap()
|
973
|
+
result = heap.pop_min()
|
974
|
+
self._save_heap()
|
975
|
+
return result
|
976
|
+
|
977
|
+
def heap_peek_min(self) -> Any:
|
978
|
+
"""Peek at minimum without removing from MinHeap."""
|
979
|
+
heap = self._get_heap()
|
980
|
+
return heap.peek_min()
|
981
|
+
|
982
|
+
def heap_size(self) -> int:
|
983
|
+
"""Get heap size."""
|
984
|
+
heap = self._get_heap()
|
985
|
+
return heap.size()
|
986
|
+
|
987
|
+
def heap_is_empty(self) -> bool:
|
988
|
+
"""Check if heap is empty."""
|
989
|
+
heap = self._get_heap()
|
990
|
+
return heap.is_empty()
|
991
|
+
|
992
|
+
# ============================================================================
|
993
|
+
# ADVANCED OPERATIONS HELPER METHODS
|
994
|
+
# ============================================================================
|
995
|
+
|
996
|
+
def _get_union_find(self) -> UnionFind:
|
997
|
+
"""Get or create Union-Find structure from node data."""
|
998
|
+
if not hasattr(self, '_union_find'):
|
999
|
+
self._union_find = UnionFind()
|
1000
|
+
# Load existing data if available
|
1001
|
+
data = self.to_native()
|
1002
|
+
if isinstance(data, dict) and 'union_find' in data:
|
1003
|
+
uf_data = data['union_find']
|
1004
|
+
if isinstance(uf_data, dict):
|
1005
|
+
for element, parent in uf_data.get('sets', {}).items():
|
1006
|
+
self._union_find._parent[element] = parent
|
1007
|
+
for element, rank in uf_data.get('ranks', {}).items():
|
1008
|
+
self._union_find._rank[element] = rank
|
1009
|
+
self._union_find._sets_count = uf_data.get('sets_count', 0)
|
1010
|
+
return self._union_find
|
1011
|
+
|
1012
|
+
def _save_union_find(self) -> None:
|
1013
|
+
"""Save Union-Find structure to node data."""
|
1014
|
+
if hasattr(self, '_union_find'):
|
1015
|
+
uf_data = {
|
1016
|
+
'sets': dict(self._union_find._parent),
|
1017
|
+
'ranks': dict(self._union_find._rank),
|
1018
|
+
'sets_count': self._union_find._sets_count
|
1019
|
+
}
|
1020
|
+
# Save to node data
|
1021
|
+
current_data = self.to_native()
|
1022
|
+
if not isinstance(current_data, dict):
|
1023
|
+
current_data = {}
|
1024
|
+
current_data['union_find'] = uf_data
|
1025
|
+
# Update the strategy with new data
|
1026
|
+
self._root = TreeGraphNodeFactory.from_native(current_data, None)
|
1027
|
+
|
1028
|
+
def _get_trie(self) -> TrieNode:
|
1029
|
+
"""Get or create Trie structure from node data."""
|
1030
|
+
if not hasattr(self, '_trie_root'):
|
1031
|
+
self._trie_root = TrieNode()
|
1032
|
+
# Load existing data if available
|
1033
|
+
data = self.to_native()
|
1034
|
+
if isinstance(data, dict):
|
1035
|
+
for word, value in data.items():
|
1036
|
+
if isinstance(word, str):
|
1037
|
+
self._insert_trie_word(word, value)
|
1038
|
+
return self._trie_root
|
1039
|
+
|
1040
|
+
def _insert_trie_word(self, word: str, value: Any) -> None:
|
1041
|
+
"""Insert word into trie."""
|
1042
|
+
current = self._get_trie()
|
1043
|
+
for char in word:
|
1044
|
+
if char not in current.children:
|
1045
|
+
current.children[char] = TrieNode()
|
1046
|
+
current = current.children[char]
|
1047
|
+
current.is_end_word = True
|
1048
|
+
current.value = value
|
1049
|
+
|
1050
|
+
def _save_trie(self) -> None:
|
1051
|
+
"""Save Trie structure to node data."""
|
1052
|
+
if hasattr(self, '_trie_root'):
|
1053
|
+
all_words = {}
|
1054
|
+
self._collect_trie_words(self._trie_root, "", all_words)
|
1055
|
+
# Save to node data
|
1056
|
+
current_data = self.to_native()
|
1057
|
+
if not isinstance(current_data, dict):
|
1058
|
+
current_data = {}
|
1059
|
+
current_data.update(all_words)
|
1060
|
+
# Update the strategy with new data
|
1061
|
+
self._root = TreeGraphNodeFactory.from_native(current_data, None)
|
1062
|
+
|
1063
|
+
def _collect_trie_words(self, node: TrieNode, prefix: str, result: Dict[str, Any]) -> None:
|
1064
|
+
"""Recursively collect all words from trie."""
|
1065
|
+
if node.is_end_word:
|
1066
|
+
result[prefix] = node.value
|
1067
|
+
|
1068
|
+
for char, child in node.children.items():
|
1069
|
+
self._collect_trie_words(child, prefix + char, result)
|
1070
|
+
|
1071
|
+
def _get_heap(self) -> MinHeap:
|
1072
|
+
"""Get or create MinHeap structure from node data."""
|
1073
|
+
if not hasattr(self, '_heap'):
|
1074
|
+
self._heap = MinHeap()
|
1075
|
+
# Load existing data if available
|
1076
|
+
data = self.to_native()
|
1077
|
+
if isinstance(data, dict) and 'heap' in data:
|
1078
|
+
heap_data = data['heap']
|
1079
|
+
if isinstance(heap_data, list):
|
1080
|
+
for priority, value in heap_data:
|
1081
|
+
self._heap.push(value, priority)
|
1082
|
+
return self._heap
|
1083
|
+
|
1084
|
+
def _save_heap(self) -> None:
|
1085
|
+
"""Save MinHeap structure to node data."""
|
1086
|
+
if hasattr(self, '_heap'):
|
1087
|
+
heap_data = list(self._heap._heap)
|
1088
|
+
# Save to node data
|
1089
|
+
current_data = self.to_native()
|
1090
|
+
if not isinstance(current_data, dict):
|
1091
|
+
current_data = {}
|
1092
|
+
current_data['heap'] = heap_data
|
1093
|
+
# Update the strategy with new data
|
1094
|
+
self._root = TreeGraphNodeFactory.from_native(current_data, None)
|
1095
|
+
|
1096
|
+
# ============================================================================
|
1097
|
+
# REQUIRED INTERFACE METHODS
|
1098
|
+
# ============================================================================
|
1099
|
+
|
1100
|
+
def to_native(self) -> Any:
|
1101
|
+
"""Convert to native Python object."""
|
1102
|
+
if self._root is None:
|
1103
|
+
return {}
|
1104
|
+
return self._root._to_native()
|
1105
|
+
|
1106
|
+
def get(self, path: str, default: Any = None) -> Optional['TreeGraphHybridStrategy']:
|
1107
|
+
"""Get a child node by path."""
|
1108
|
+
if self._root is None:
|
1109
|
+
return None
|
1110
|
+
|
1111
|
+
try:
|
1112
|
+
if '.' in path:
|
1113
|
+
result = self._get_node_by_path(path)
|
1114
|
+
else:
|
1115
|
+
result = self._get_node_by_key(path)
|
1116
|
+
|
1117
|
+
if result is None:
|
1118
|
+
return None
|
1119
|
+
|
1120
|
+
# Create a new strategy instance with the result
|
1121
|
+
new_strategy = TreeGraphHybridStrategy()
|
1122
|
+
new_strategy._root = result
|
1123
|
+
return new_strategy
|
1124
|
+
except Exception:
|
1125
|
+
return None
|
1126
|
+
|
1127
|
+
def _get_node_by_path(self, path: str) -> Optional[TreeGraphNode]:
|
1128
|
+
"""Get a node by path."""
|
1129
|
+
if self._root is None:
|
1130
|
+
return None
|
1131
|
+
|
1132
|
+
try:
|
1133
|
+
segments = self._path_parser.parse(path)
|
1134
|
+
current = self._root
|
1135
|
+
|
1136
|
+
for segment in segments:
|
1137
|
+
if isinstance(current, TreeGraphDictNode):
|
1138
|
+
current = current._get_child(segment)
|
1139
|
+
elif isinstance(current, TreeGraphListNode):
|
1140
|
+
try:
|
1141
|
+
index = int(segment)
|
1142
|
+
current = current._get_child(index)
|
1143
|
+
except ValueError:
|
1144
|
+
return None
|
1145
|
+
else:
|
1146
|
+
return None
|
1147
|
+
|
1148
|
+
return current
|
1149
|
+
|
1150
|
+
except (KeyError, IndexError, TypeError):
|
1151
|
+
return None
|
1152
|
+
|
1153
|
+
def _get_node_by_key(self, key: str) -> Optional[TreeGraphNode]:
|
1154
|
+
"""Get a node by direct key."""
|
1155
|
+
if self._root is None:
|
1156
|
+
return None
|
1157
|
+
|
1158
|
+
try:
|
1159
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1160
|
+
return self._root._get_child(key)
|
1161
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1162
|
+
try:
|
1163
|
+
index = int(key)
|
1164
|
+
return self._root._get_child(index)
|
1165
|
+
except ValueError:
|
1166
|
+
return None
|
1167
|
+
elif isinstance(self._root, TreeGraphValueNode):
|
1168
|
+
# If root is a value node, only return it if key is empty or matches
|
1169
|
+
if key == "" or key == "value":
|
1170
|
+
return self._root
|
1171
|
+
return None
|
1172
|
+
else:
|
1173
|
+
return None
|
1174
|
+
|
1175
|
+
except (KeyError, IndexError, TypeError):
|
1176
|
+
return None
|
1177
|
+
|
1178
|
+
def put(self, path: str, value: Any) -> 'TreeGraphHybridStrategy':
|
1179
|
+
"""Set a value at path."""
|
1180
|
+
if self._root is None:
|
1181
|
+
self._root = TreeGraphNodeFactory.from_native({})
|
1182
|
+
|
1183
|
+
# For now, implement a simple put operation
|
1184
|
+
# This would need to be enhanced for complex path navigation
|
1185
|
+
if '.' not in path:
|
1186
|
+
# Simple key assignment
|
1187
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1188
|
+
value_node = TreeGraphNodeFactory.from_native(value, self._root)
|
1189
|
+
self._root._children[path] = value_node
|
1190
|
+
else:
|
1191
|
+
# Convert to dict if needed
|
1192
|
+
current_data = self.to_native()
|
1193
|
+
if not isinstance(current_data, dict):
|
1194
|
+
current_data = {}
|
1195
|
+
current_data[path] = value
|
1196
|
+
self._root = TreeGraphNodeFactory.from_native(current_data)
|
1197
|
+
|
1198
|
+
return self
|
1199
|
+
|
1200
|
+
def delete(self, path: str) -> bool:
|
1201
|
+
"""Delete a node at path."""
|
1202
|
+
if self._root is None:
|
1203
|
+
return False
|
1204
|
+
|
1205
|
+
try:
|
1206
|
+
if '.' not in path:
|
1207
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1208
|
+
if path in self._root._children:
|
1209
|
+
del self._root._children[path]
|
1210
|
+
return True
|
1211
|
+
return False
|
1212
|
+
except Exception:
|
1213
|
+
return False
|
1214
|
+
|
1215
|
+
def exists(self, path: str) -> bool:
|
1216
|
+
"""Check if path exists."""
|
1217
|
+
return self.get(path) is not None
|
1218
|
+
|
1219
|
+
def keys(self) -> Iterator[str]:
|
1220
|
+
"""Get keys (for dict-like nodes)."""
|
1221
|
+
if self._root is None:
|
1222
|
+
return iter([])
|
1223
|
+
|
1224
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1225
|
+
return iter(self._root._children.keys())
|
1226
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1227
|
+
return iter(str(i) for i in range(len(self._root._children)))
|
1228
|
+
else:
|
1229
|
+
return iter([])
|
1230
|
+
|
1231
|
+
def values(self) -> Iterator['TreeGraphHybridStrategy']:
|
1232
|
+
"""Get values (for dict-like nodes)."""
|
1233
|
+
if self._root is None:
|
1234
|
+
return iter([])
|
1235
|
+
|
1236
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1237
|
+
for child in self._root._children.values():
|
1238
|
+
new_strategy = TreeGraphHybridStrategy()
|
1239
|
+
new_strategy._root = child
|
1240
|
+
yield new_strategy
|
1241
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1242
|
+
for child in self._root._children:
|
1243
|
+
new_strategy = TreeGraphHybridStrategy()
|
1244
|
+
new_strategy._root = child
|
1245
|
+
yield new_strategy
|
1246
|
+
else:
|
1247
|
+
return iter([])
|
1248
|
+
|
1249
|
+
def items(self) -> Iterator[tuple[str, 'TreeGraphHybridStrategy']]:
|
1250
|
+
"""Get items (for dict-like nodes)."""
|
1251
|
+
if self._root is None:
|
1252
|
+
return iter([])
|
1253
|
+
|
1254
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1255
|
+
for key, child in self._root._children.items():
|
1256
|
+
new_strategy = TreeGraphHybridStrategy()
|
1257
|
+
new_strategy._root = child
|
1258
|
+
yield (key, new_strategy)
|
1259
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1260
|
+
for i, child in enumerate(self._root._children):
|
1261
|
+
new_strategy = TreeGraphHybridStrategy()
|
1262
|
+
new_strategy._root = child
|
1263
|
+
yield (str(i), new_strategy)
|
1264
|
+
else:
|
1265
|
+
return iter([])
|
1266
|
+
|
1267
|
+
def __len__(self) -> int:
|
1268
|
+
"""Get length."""
|
1269
|
+
if self._root is None:
|
1270
|
+
return 0
|
1271
|
+
|
1272
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1273
|
+
return len(self._root._children)
|
1274
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1275
|
+
return len(self._root._children)
|
1276
|
+
else:
|
1277
|
+
return 1
|
1278
|
+
|
1279
|
+
def __iter__(self) -> Iterator['TreeGraphHybridStrategy']:
|
1280
|
+
"""Iterate over children."""
|
1281
|
+
return self.values()
|
1282
|
+
|
1283
|
+
def __getitem__(self, key: Union[str, int]) -> 'TreeGraphHybridStrategy':
|
1284
|
+
"""Get child by key or index."""
|
1285
|
+
if self._root is None:
|
1286
|
+
raise KeyError(f"Key '{key}' not found")
|
1287
|
+
|
1288
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1289
|
+
if key not in self._root._children:
|
1290
|
+
raise KeyError(f"Key '{key}' not found")
|
1291
|
+
child = self._root._children[key]
|
1292
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1293
|
+
if not isinstance(key, int):
|
1294
|
+
raise TypeError("List indices must be integers")
|
1295
|
+
if key < 0 or key >= len(self._root._children):
|
1296
|
+
raise IndexError("List index out of range")
|
1297
|
+
child = self._root._children[key]
|
1298
|
+
else:
|
1299
|
+
raise TypeError(f"Cannot access key '{key}' on node of type {type(self._root)}")
|
1300
|
+
|
1301
|
+
new_strategy = TreeGraphHybridStrategy()
|
1302
|
+
new_strategy._root = child
|
1303
|
+
return new_strategy
|
1304
|
+
|
1305
|
+
def __setitem__(self, key: Union[str, int], value: Any) -> None:
|
1306
|
+
"""Set child by key or index."""
|
1307
|
+
if self._root is None:
|
1308
|
+
self._root = TreeGraphNodeFactory.from_native({})
|
1309
|
+
|
1310
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1311
|
+
value_node = TreeGraphNodeFactory.from_native(value, self._root)
|
1312
|
+
self._root._children[key] = value_node
|
1313
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1314
|
+
if not isinstance(key, int):
|
1315
|
+
raise TypeError("List indices must be integers")
|
1316
|
+
value_node = TreeGraphNodeFactory.from_native(value, self._root)
|
1317
|
+
if key >= len(self._root._children):
|
1318
|
+
# Extend list if needed
|
1319
|
+
while len(self._root._children) <= key:
|
1320
|
+
self._root._children.append(TreeGraphNodeFactory.from_native(None, self._root))
|
1321
|
+
self._root._children[key] = value_node
|
1322
|
+
else:
|
1323
|
+
raise TypeError(f"Cannot set key '{key}' on node of type {type(self._root)}")
|
1324
|
+
|
1325
|
+
def __contains__(self, key: Union[str, int]) -> bool:
|
1326
|
+
"""Check if key exists."""
|
1327
|
+
if self._root is None:
|
1328
|
+
return False
|
1329
|
+
|
1330
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1331
|
+
return key in self._root._children
|
1332
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1333
|
+
if not isinstance(key, int):
|
1334
|
+
return False
|
1335
|
+
return 0 <= key < len(self._root._children)
|
1336
|
+
else:
|
1337
|
+
return False
|
1338
|
+
|
1339
|
+
# Type checking properties
|
1340
|
+
@property
|
1341
|
+
def is_leaf(self) -> bool:
|
1342
|
+
"""Check if this is a leaf node."""
|
1343
|
+
if self._root is None:
|
1344
|
+
return True
|
1345
|
+
return isinstance(self._root, TreeGraphValueNode)
|
1346
|
+
|
1347
|
+
@property
|
1348
|
+
def is_list(self) -> bool:
|
1349
|
+
"""Check if this is a list node."""
|
1350
|
+
if self._root is None:
|
1351
|
+
return False
|
1352
|
+
return isinstance(self._root, TreeGraphListNode)
|
1353
|
+
|
1354
|
+
@property
|
1355
|
+
def is_dict(self) -> bool:
|
1356
|
+
"""Check if this is a dict node."""
|
1357
|
+
if self._root is None:
|
1358
|
+
return False
|
1359
|
+
return isinstance(self._root, TreeGraphDictNode)
|
1360
|
+
|
1361
|
+
@property
|
1362
|
+
def is_reference(self) -> bool:
|
1363
|
+
"""Check if this is a reference node."""
|
1364
|
+
if self._root is None:
|
1365
|
+
return False
|
1366
|
+
return isinstance(self._root, TreeGraphReferenceNode)
|
1367
|
+
|
1368
|
+
@property
|
1369
|
+
def is_object(self) -> bool:
|
1370
|
+
"""Check if this is an object node."""
|
1371
|
+
if self._root is None:
|
1372
|
+
return False
|
1373
|
+
return isinstance(self._root, TreeGraphObjectNode)
|
1374
|
+
|
1375
|
+
@property
|
1376
|
+
def type(self) -> str:
|
1377
|
+
"""Get the type of this node."""
|
1378
|
+
if self._root is None:
|
1379
|
+
return 'empty'
|
1380
|
+
elif isinstance(self._root, TreeGraphValueNode):
|
1381
|
+
return 'value'
|
1382
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1383
|
+
return 'list'
|
1384
|
+
elif isinstance(self._root, TreeGraphDictNode):
|
1385
|
+
return 'dict'
|
1386
|
+
elif isinstance(self._root, TreeGraphReferenceNode):
|
1387
|
+
return 'reference'
|
1388
|
+
elif isinstance(self._root, TreeGraphObjectNode):
|
1389
|
+
return 'object'
|
1390
|
+
else:
|
1391
|
+
return 'unknown'
|
1392
|
+
|
1393
|
+
@property
|
1394
|
+
def value(self) -> Any:
|
1395
|
+
"""Get the value of this node."""
|
1396
|
+
if self._root is None:
|
1397
|
+
return None
|
1398
|
+
return self._root._to_native()
|
1399
|
+
|
1400
|
+
# Optional properties with default implementations
|
1401
|
+
@property
|
1402
|
+
def uri(self) -> Optional[str]:
|
1403
|
+
"""Get URI (for reference/object nodes)."""
|
1404
|
+
if self._root is None:
|
1405
|
+
return None
|
1406
|
+
return getattr(self._root, 'uri', None)
|
1407
|
+
|
1408
|
+
@property
|
1409
|
+
def reference_type(self) -> Optional[str]:
|
1410
|
+
"""Get reference type (for reference nodes)."""
|
1411
|
+
if self._root is None:
|
1412
|
+
return None
|
1413
|
+
return getattr(self._root, 'reference_type', None)
|
1414
|
+
|
1415
|
+
@property
|
1416
|
+
def object_type(self) -> Optional[str]:
|
1417
|
+
"""Get object type (for object nodes)."""
|
1418
|
+
if self._root is None:
|
1419
|
+
return None
|
1420
|
+
return getattr(self._root, 'object_type', None)
|
1421
|
+
|
1422
|
+
@property
|
1423
|
+
def mime_type(self) -> Optional[str]:
|
1424
|
+
"""Get MIME type (for object nodes)."""
|
1425
|
+
if self._root is None:
|
1426
|
+
return None
|
1427
|
+
return getattr(self._root, 'mime_type', None)
|
1428
|
+
|
1429
|
+
@property
|
1430
|
+
def metadata(self) -> Optional[Dict[str, Any]]:
|
1431
|
+
"""Get metadata (for reference/object nodes)."""
|
1432
|
+
if self._root is None:
|
1433
|
+
return None
|
1434
|
+
return getattr(self._root, 'metadata', None)
|