numbers-parser 4.14.4__py3-none-any.whl → 4.15.1__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.
- numbers_parser/__init__.py +1 -0
- numbers_parser/cell.py +28 -202
- numbers_parser/constants.py +8 -4
- numbers_parser/document.py +26 -3
- numbers_parser/formula.py +423 -56
- numbers_parser/generated/TSCEArchives_pb2.py +209 -193
- numbers_parser/generated/TSSArchives_pb2.py +36 -36
- numbers_parser/generated/TSTArchives_pb2.py +328 -332
- numbers_parser/generated/mapping.py +1 -2
- numbers_parser/model.py +277 -186
- numbers_parser/numbers_uuid.py +6 -0
- numbers_parser/tokenizer.py +548 -0
- numbers_parser/xrefs.py +850 -0
- {numbers_parser-4.14.4.dist-info → numbers_parser-4.15.1.dist-info}/METADATA +5 -5
- {numbers_parser-4.14.4.dist-info → numbers_parser-4.15.1.dist-info}/RECORD +18 -16
- {numbers_parser-4.14.4.dist-info → numbers_parser-4.15.1.dist-info}/LICENSE.rst +0 -0
- {numbers_parser-4.14.4.dist-info → numbers_parser-4.15.1.dist-info}/WHEEL +0 -0
- {numbers_parser-4.14.4.dist-info → numbers_parser-4.15.1.dist-info}/entry_points.txt +0 -0
|
@@ -524,9 +524,7 @@ TSPRegistryMapping = {
|
|
|
524
524
|
"6293": "TST.CommandRewriteFilterFormulasForRewriteSpecArchive",
|
|
525
525
|
"6294": "TST.CommandRewriteSortOrderForRewriteSpecArchive",
|
|
526
526
|
"6295": "TST.StrokeSelectionArchive",
|
|
527
|
-
"6297": "TST.LetNodeArchive",
|
|
528
527
|
"6298": "TST.VariableNodeArchive",
|
|
529
|
-
"6299": "TST.InNodeArchive",
|
|
530
528
|
"6300": "TST.CommandInverseMergeArchive",
|
|
531
529
|
"6301": "TST.CommandMoveCellsArchive",
|
|
532
530
|
"6302": "TST.DefaultCellStylesContainerArchive",
|
|
@@ -579,6 +577,7 @@ TSPRegistryMapping = {
|
|
|
579
577
|
"6381": "TST.CommandExtendTableIDHistoryArchive",
|
|
580
578
|
"6382": "TST.GroupByArchive.AggregatorArchive",
|
|
581
579
|
"6383": "TST.GroupByArchive.GroupNodeArchive",
|
|
580
|
+
"6384": "TST.SpillOriginRefNodeArchive",
|
|
582
581
|
"10011": "TSWP.SectionPlaceholderArchive",
|
|
583
582
|
"10020": "TSWP.ShapeSelectionTransformerArchive",
|
|
584
583
|
"10021": "TSWP.SelectionTransformerArchive",
|
numbers_parser/model.py
CHANGED
|
@@ -4,6 +4,7 @@ import re
|
|
|
4
4
|
from array import array
|
|
5
5
|
from collections import defaultdict
|
|
6
6
|
from hashlib import sha1
|
|
7
|
+
from itertools import chain
|
|
7
8
|
from math import floor
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from struct import pack
|
|
@@ -29,8 +30,6 @@ from numbers_parser.cell import (
|
|
|
29
30
|
PaddingType,
|
|
30
31
|
Style,
|
|
31
32
|
VerticalJustification,
|
|
32
|
-
xl_col_to_name,
|
|
33
|
-
xl_rowcol_to_cell,
|
|
34
33
|
)
|
|
35
34
|
from numbers_parser.constants import (
|
|
36
35
|
ALLOWED_FORMATTING_PARAMETERS,
|
|
@@ -74,7 +73,8 @@ from numbers_parser.generated.TSWPArchives_pb2 import (
|
|
|
74
73
|
)
|
|
75
74
|
from numbers_parser.iwafile import find_extension
|
|
76
75
|
from numbers_parser.numbers_cache import Cacheable, cache
|
|
77
|
-
from numbers_parser.numbers_uuid import NumbersUUID
|
|
76
|
+
from numbers_parser.numbers_uuid import NumbersUUID, uuid_to_hex
|
|
77
|
+
from numbers_parser.xrefs import CellRange, ScopedNameRefCache
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
def create_font_name_map(font_map: dict) -> dict:
|
|
@@ -224,6 +224,7 @@ class _NumbersModel(Cacheable):
|
|
|
224
224
|
self._table_styles = DataLists(self, "styleTable", "reference")
|
|
225
225
|
self._table_strings = DataLists(self, "stringTable", "string")
|
|
226
226
|
self._control_specs = DataLists(self, "control_cell_spec_table", "cell_spec")
|
|
227
|
+
self._formulas = DataLists(self, "formula_table", "formula")
|
|
227
228
|
self._table_data = {}
|
|
228
229
|
self._styles = None
|
|
229
230
|
self._images = {}
|
|
@@ -236,6 +237,8 @@ class _NumbersModel(Cacheable):
|
|
|
236
237
|
"bottom": defaultdict(),
|
|
237
238
|
"left": defaultdict(),
|
|
238
239
|
}
|
|
240
|
+
self.name_ref_cache = ScopedNameRefCache(self)
|
|
241
|
+
self.calculate_table_uuid_map()
|
|
239
242
|
|
|
240
243
|
def save(self, filepath: Path, package: bool) -> None:
|
|
241
244
|
self.objects.save(filepath, package)
|
|
@@ -258,13 +261,16 @@ class _NumbersModel(Cacheable):
|
|
|
258
261
|
self._table_data[table_id] = data
|
|
259
262
|
|
|
260
263
|
# Don't cache: new tables can be added at runtime
|
|
261
|
-
def table_ids(self, sheet_id: int) -> list:
|
|
262
|
-
"""
|
|
264
|
+
def table_ids(self, sheet_id: int | None = None) -> list:
|
|
265
|
+
"""
|
|
266
|
+
Return a list of table IDs for a given sheet ID or all table
|
|
267
|
+
IDs id the sheet ID is None
|
|
268
|
+
"""
|
|
263
269
|
table_info_ids = self.find_refs("TableInfoArchive")
|
|
264
270
|
return [
|
|
265
271
|
self.objects[t_id].tableModel.identifier
|
|
266
272
|
for t_id in table_info_ids
|
|
267
|
-
if self.objects[t_id].super.parent.identifier == sheet_id
|
|
273
|
+
if (sheet_id is None or self.objects[t_id].super.parent.identifier == sheet_id)
|
|
268
274
|
]
|
|
269
275
|
|
|
270
276
|
# Don't cache: new tables can be added at runtime
|
|
@@ -282,14 +288,7 @@ class _NumbersModel(Cacheable):
|
|
|
282
288
|
# The base data store contains a reference to rowHeaders.buckets
|
|
283
289
|
# which is an ordered list that matches the storage buffers, but
|
|
284
290
|
# identifies which row a storage buffer belongs to (empty rows have
|
|
285
|
-
# no storage buffers).
|
|
286
|
-
#
|
|
287
|
-
# {
|
|
288
|
-
# "hiding_state": 0,
|
|
289
|
-
# "index": 0,
|
|
290
|
-
# "number_of_cells": 3,
|
|
291
|
-
# "size": 0.0
|
|
292
|
-
# },
|
|
291
|
+
# no storage buffers).
|
|
293
292
|
row_bucket_map = {i: None for i in range(self.objects[table_id].number_of_rows)}
|
|
294
293
|
bds = self.objects[table_id].base_data_store
|
|
295
294
|
bucket_ids = [x.identifier for x in bds.rowHeaders.buckets]
|
|
@@ -316,6 +315,13 @@ class _NumbersModel(Cacheable):
|
|
|
316
315
|
self.objects[table_id].table_name = value
|
|
317
316
|
return None
|
|
318
317
|
|
|
318
|
+
def table_names(self):
|
|
319
|
+
return list(
|
|
320
|
+
chain.from_iterable(
|
|
321
|
+
[[self.table_name(tid) for tid in self.table_ids(sid)] for sid in self.sheet_ids()],
|
|
322
|
+
),
|
|
323
|
+
)
|
|
324
|
+
|
|
319
325
|
def table_name_enabled(self, table_id: int, enabled: bool | None = None):
|
|
320
326
|
if enabled is not None:
|
|
321
327
|
self.objects[table_id].table_name_enabled = enabled
|
|
@@ -751,51 +757,77 @@ class _NumbersModel(Cacheable):
|
|
|
751
757
|
owner_id_map[e.internal_owner_id] = NumbersUUID(e.owner_id).hex
|
|
752
758
|
return owner_id_map
|
|
753
759
|
|
|
760
|
+
def calculate_table_uuid_map(self) -> None:
|
|
761
|
+
# Each Table Model has a UUID which is used in references to the table. See
|
|
762
|
+
# Numbers.md#uuid-mapping for more details.
|
|
763
|
+
|
|
764
|
+
# For haunted owner archive types, map formula_owner_uids to their base_owner_uids
|
|
765
|
+
haunted_owner_ids = [
|
|
766
|
+
obj_id
|
|
767
|
+
for obj_id in self.find_refs("FormulaOwnerDependenciesArchive")
|
|
768
|
+
if self.objects[obj_id].owner_kind == OwnerKind.HAUNTED_OWNER
|
|
769
|
+
]
|
|
770
|
+
if len(haunted_owner_ids) == 0:
|
|
771
|
+
# Some older documents (see issue-18) do not use FormulaOwnerDependenciesArchive
|
|
772
|
+
self._table_id_to_base_id = {}
|
|
773
|
+
return
|
|
774
|
+
|
|
775
|
+
formula_owner_to_base_owner_map = {
|
|
776
|
+
uuid_to_hex(self.objects[obj_id].formula_owner_uid): uuid_to_hex(
|
|
777
|
+
self.objects[obj_id].base_owner_uid,
|
|
778
|
+
)
|
|
779
|
+
for obj_id in haunted_owner_ids
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
# Map table IDs to the base_owner_uids of the formula owners that match
|
|
783
|
+
# the table model's haunted owner
|
|
784
|
+
self._table_id_to_base_id = {
|
|
785
|
+
table_id: formula_owner_to_base_owner_map[
|
|
786
|
+
uuid_to_hex(self.objects[table_id].haunted_owner.owner_uid)
|
|
787
|
+
]
|
|
788
|
+
for table_id in self.table_ids()
|
|
789
|
+
}
|
|
790
|
+
self._table_base_id_to_formula_owner_id = {
|
|
791
|
+
uuid_to_hex(self.objects[obj_id].base_owner_uid): obj_id for obj_id in haunted_owner_ids
|
|
792
|
+
}
|
|
793
|
+
|
|
754
794
|
@cache()
|
|
755
795
|
def table_base_id(self, table_id: int) -> int:
|
|
756
796
|
""" "Finds the UUID of a table."""
|
|
757
|
-
#
|
|
758
|
-
|
|
759
|
-
#
|
|
760
|
-
# "base_owner_uid": "6a4a5281-7b06-f5a1-904b-7f9ec784b368"",
|
|
761
|
-
# "formula_owner_uid": "6a4a5281-7b06-f5a1-904b-7f9ec784b36d"
|
|
762
|
-
#
|
|
763
|
-
# The Table UUID is the TSCE.FormulaOwnerDependenciesArchive whose formula_owner_uid
|
|
764
|
-
# matches the UUID of the haunted_owner of the Table:
|
|
765
|
-
#
|
|
766
|
-
# "haunted_owner": {
|
|
767
|
-
# "owner_uid": "6a4a5281-7b06-f5a1-904b-7f9ec784b368""
|
|
768
|
-
# }
|
|
769
|
-
haunted_owner = NumbersUUID(self.objects[table_id].haunted_owner.owner_uid).hex
|
|
770
|
-
formula_owner_ids = self.find_refs("FormulaOwnerDependenciesArchive")
|
|
771
|
-
for dependency_id in formula_owner_ids: # pragma: no branch
|
|
772
|
-
obj = self.objects[dependency_id]
|
|
773
|
-
# if obj.owner_kind == OwnerKind.HAUNTED_OWNER:
|
|
774
|
-
if obj.HasField("base_owner_uid") and obj.HasField(
|
|
775
|
-
"formula_owner_uid",
|
|
776
|
-
): # pragma: no branch
|
|
777
|
-
base_owner_uid = NumbersUUID(obj.base_owner_uid).hex
|
|
778
|
-
formula_owner_uid = NumbersUUID(obj.formula_owner_uid).hex
|
|
779
|
-
if formula_owner_uid == haunted_owner:
|
|
780
|
-
return base_owner_uid
|
|
781
|
-
return None
|
|
797
|
+
# Table can be empty if the document does not use FormulaOwnerDependenciesArchive
|
|
798
|
+
return self._table_id_to_base_id.get(table_id)
|
|
782
799
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
800
|
+
def get_formula_owner(self, table_id: int) -> object:
|
|
801
|
+
table_uuid = self.table_base_id(table_id)
|
|
802
|
+
return self.objects[self._table_base_id_to_formula_owner_id[table_uuid]]
|
|
803
|
+
|
|
804
|
+
def add_formula_dependency(self, row: int, col: int, table_id: int) -> None:
|
|
787
805
|
calc_engine = self.calc_engine()
|
|
806
|
+
calc_engine.dependency_tracker.number_of_formulas += 1
|
|
807
|
+
internal_formula_id = calc_engine.dependency_tracker.number_of_formulas
|
|
788
808
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
809
|
+
formula_owner = self.get_formula_owner(table_id)
|
|
810
|
+
formula_owner.cell_dependencies.cell_record.append(
|
|
811
|
+
TSCEArchives.CellRecordExpandedArchive(column=col, row=row),
|
|
812
|
+
)
|
|
813
|
+
if len(formula_owner.tiled_cell_dependencies.cell_record_tiles) == 0:
|
|
814
|
+
cell_record_id, cell_record = self.objects.create_object_from_dict(
|
|
815
|
+
"CalculationEngine",
|
|
816
|
+
{
|
|
817
|
+
"internal_owner_id": internal_formula_id,
|
|
818
|
+
"tile_column_begin": 0,
|
|
819
|
+
"tile_row_begin": 0,
|
|
820
|
+
},
|
|
821
|
+
TSCEArchives.CellRecordTileArchive,
|
|
822
|
+
)
|
|
823
|
+
formula_owner.tiled_cell_dependencies.cell_record_tiles.append(
|
|
824
|
+
TSPMessages.Reference(identifier=cell_record_id),
|
|
825
|
+
)
|
|
826
|
+
else:
|
|
827
|
+
cell_record_id = formula_owner.tiled_cell_dependencies.cell_record_tiles[0].identifier
|
|
828
|
+
cell_record = self.objects[cell_record_id]
|
|
829
|
+
|
|
830
|
+
cell_record.cell_records.append(formula_owner.cell_dependencies.cell_record[-1])
|
|
799
831
|
|
|
800
832
|
@cache(num_args=0)
|
|
801
833
|
def calc_engine_id(self):
|
|
@@ -815,8 +847,8 @@ class _NumbersModel(Cacheable):
|
|
|
815
847
|
|
|
816
848
|
@cache()
|
|
817
849
|
def calculate_merge_cell_ranges(self, table_id) -> None:
|
|
818
|
-
"""
|
|
819
|
-
#
|
|
850
|
+
"""Extract all the merge cell ranges for the Table."""
|
|
851
|
+
# See details in Numbers.md#merge-ranges.
|
|
820
852
|
owner_id_map = self.owner_id_map()
|
|
821
853
|
table_base_id = self.table_base_id(table_id)
|
|
822
854
|
|
|
@@ -879,6 +911,17 @@ class _NumbersModel(Cacheable):
|
|
|
879
911
|
return sheet_id
|
|
880
912
|
return None
|
|
881
913
|
|
|
914
|
+
def table_name_to_uuid(self, sheet_name: str, table_name: str) -> str:
|
|
915
|
+
table_ids = [tid for tid in self.table_ids() if table_name == self.table_name(tid)]
|
|
916
|
+
if len(table_ids) == 1:
|
|
917
|
+
return self.table_base_id(table_ids[0])
|
|
918
|
+
|
|
919
|
+
sheet_name_to_id = {self.sheet_name(x): x for x in self.sheet_ids()}
|
|
920
|
+
sheet_id = sheet_name_to_id[sheet_name]
|
|
921
|
+
table_name_to_id = {self.table_name(x): x for x in self.table_ids(sheet_id)}
|
|
922
|
+
table_id = table_name_to_id[table_name]
|
|
923
|
+
return self.table_base_id(table_id)
|
|
924
|
+
|
|
882
925
|
@cache()
|
|
883
926
|
def table_uuids_to_id(self, table_uuid) -> int | None:
|
|
884
927
|
for sheet_id in self.sheet_ids(): # pragma: no branch # noqa: RET503
|
|
@@ -886,66 +929,103 @@ class _NumbersModel(Cacheable):
|
|
|
886
929
|
if table_uuid == self.table_base_id(table_id):
|
|
887
930
|
return table_id
|
|
888
931
|
|
|
889
|
-
def node_to_ref(self,
|
|
932
|
+
def node_to_ref(self, table_id: int, row: int, col: int, node):
|
|
933
|
+
def resolve_range(is_absolute, absolute_list, relative_list, offset, max_val):
|
|
934
|
+
if is_absolute:
|
|
935
|
+
return absolute_list[0].range_begin
|
|
936
|
+
if not relative_list and absolute_list[0].range_begin == max_val:
|
|
937
|
+
return max_val
|
|
938
|
+
return offset + relative_list[0].range_begin
|
|
939
|
+
|
|
940
|
+
def resolve_range_end(is_absolute, absolute_list, relative_list, offset, max_val):
|
|
941
|
+
if is_absolute:
|
|
942
|
+
return range_end(absolute_list[0])
|
|
943
|
+
if not relative_list and range_end(absolute_list[0]) == max_val:
|
|
944
|
+
return max_val
|
|
945
|
+
return offset + range_end(relative_list[0])
|
|
946
|
+
|
|
890
947
|
if node.HasField("AST_cross_table_reference_extra_info"):
|
|
891
948
|
table_uuid = NumbersUUID(node.AST_cross_table_reference_extra_info.table_id).hex
|
|
892
|
-
|
|
893
|
-
other_table_name = self.table_name(other_table_id)
|
|
949
|
+
to_table_id = self.table_uuids_to_id(table_uuid)
|
|
894
950
|
else:
|
|
895
|
-
|
|
896
|
-
other_table_name = None
|
|
897
|
-
|
|
898
|
-
if other_table_id is not None:
|
|
899
|
-
this_sheet_id = self.table_id_to_sheet_id(this_table_id)
|
|
900
|
-
other_sheet_id = self.table_id_to_sheet_id(other_table_id)
|
|
901
|
-
if this_sheet_id != other_sheet_id:
|
|
902
|
-
other_sheet_name = self.sheet_name(other_sheet_id)
|
|
903
|
-
other_table_name = f"{other_sheet_name}::" + other_table_name
|
|
951
|
+
to_table_id = None
|
|
904
952
|
|
|
905
953
|
if node.HasField("AST_colon_tract"):
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
954
|
+
row_begin = resolve_range(
|
|
955
|
+
node.AST_sticky_bits.begin_row_is_absolute,
|
|
956
|
+
node.AST_colon_tract.absolute_row,
|
|
957
|
+
node.AST_colon_tract.relative_row,
|
|
958
|
+
row,
|
|
959
|
+
0x7FFFFFFF,
|
|
960
|
+
)
|
|
912
961
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
962
|
+
row_end = resolve_range_end(
|
|
963
|
+
node.AST_sticky_bits.end_row_is_absolute,
|
|
964
|
+
node.AST_colon_tract.absolute_row,
|
|
965
|
+
node.AST_colon_tract.relative_row,
|
|
966
|
+
row,
|
|
967
|
+
0x7FFFFFFF,
|
|
968
|
+
)
|
|
918
969
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
970
|
+
col_begin = resolve_range(
|
|
971
|
+
node.AST_sticky_bits.begin_column_is_absolute,
|
|
972
|
+
node.AST_colon_tract.absolute_column,
|
|
973
|
+
node.AST_colon_tract.relative_column,
|
|
974
|
+
col,
|
|
975
|
+
0x7FFF,
|
|
976
|
+
)
|
|
923
977
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
978
|
+
col_end = resolve_range_end(
|
|
979
|
+
node.AST_sticky_bits.end_column_is_absolute,
|
|
980
|
+
node.AST_colon_tract.absolute_column,
|
|
981
|
+
node.AST_colon_tract.relative_column,
|
|
982
|
+
col,
|
|
983
|
+
0x7FFF,
|
|
984
|
+
)
|
|
928
985
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
986
|
+
return CellRange(
|
|
987
|
+
model=self,
|
|
988
|
+
row_start=None if row_begin == 0x7FFFFFFF else row_begin,
|
|
989
|
+
row_end=None if row_end == 0x7FFFFFFF else row_end,
|
|
990
|
+
col_start=None if col_begin == 0x7FFF else col_begin,
|
|
991
|
+
col_end=None if col_end == 0x7FFF else col_end,
|
|
992
|
+
row_start_is_abs=node.AST_sticky_bits.begin_row_is_absolute,
|
|
993
|
+
row_end_is_abs=node.AST_sticky_bits.end_row_is_absolute,
|
|
994
|
+
col_start_is_abs=node.AST_sticky_bits.begin_column_is_absolute,
|
|
995
|
+
col_end_is_abs=node.AST_sticky_bits.end_column_is_absolute,
|
|
996
|
+
from_table_id=table_id,
|
|
997
|
+
to_table_id=to_table_id,
|
|
998
|
+
)
|
|
933
999
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1000
|
+
row = node.AST_row.row if node.AST_row.absolute else row + node.AST_row.row
|
|
1001
|
+
col = node.AST_column.column if node.AST_column.absolute else col + node.AST_column.column
|
|
1002
|
+
if node.HasField("AST_row") and not node.HasField("AST_column"):
|
|
1003
|
+
return CellRange(
|
|
1004
|
+
model=self,
|
|
1005
|
+
row_start=row,
|
|
1006
|
+
row_start_is_abs=node.AST_row.absolute,
|
|
1007
|
+
from_table_id=table_id,
|
|
1008
|
+
to_table_id=to_table_id,
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
if node.HasField("AST_column") and not node.HasField("AST_row"):
|
|
1012
|
+
return CellRange(
|
|
1013
|
+
model=self,
|
|
1014
|
+
col_start=col,
|
|
1015
|
+
col_start_is_abs=node.AST_column.absolute,
|
|
1016
|
+
from_table_id=table_id,
|
|
1017
|
+
to_table_id=to_table_id,
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
return CellRange(
|
|
1021
|
+
model=self,
|
|
1022
|
+
row_start=row,
|
|
1023
|
+
col_start=col,
|
|
1024
|
+
row_start_is_abs=node.AST_row.absolute,
|
|
1025
|
+
col_start_is_abs=node.AST_column.absolute,
|
|
1026
|
+
from_table_id=table_id,
|
|
1027
|
+
to_table_id=to_table_id,
|
|
945
1028
|
)
|
|
946
|
-
if table_name is not None:
|
|
947
|
-
return f"{table_name}::{begin_ref}:{end_ref}"
|
|
948
|
-
return f"{begin_ref}:{end_ref}"
|
|
949
1029
|
|
|
950
1030
|
@cache()
|
|
951
1031
|
def formula_ast(self, table_id: int):
|
|
@@ -1480,12 +1560,6 @@ class _NumbersModel(Cacheable):
|
|
|
1480
1560
|
TSPMessages.Reference(identifier=row_headers_id),
|
|
1481
1561
|
)
|
|
1482
1562
|
|
|
1483
|
-
self._table_data[table_model_id] = [
|
|
1484
|
-
[Cell._empty_cell(table_model_id, row, col, self) for col in range(num_cols)]
|
|
1485
|
-
for row in range(num_rows)
|
|
1486
|
-
]
|
|
1487
|
-
self.recalculate_table_data(table_model_id, self._table_data[table_model_id])
|
|
1488
|
-
|
|
1489
1563
|
table_info_id, table_info = self.objects.create_object_from_dict(
|
|
1490
1564
|
"CalculationEngine",
|
|
1491
1565
|
{},
|
|
@@ -1494,6 +1568,22 @@ class _NumbersModel(Cacheable):
|
|
|
1494
1568
|
table_info.tableModel.MergeFrom(TSPMessages.Reference(identifier=table_model_id))
|
|
1495
1569
|
table_info.super.MergeFrom(self.create_drawable(sheet_id, x, y))
|
|
1496
1570
|
|
|
1571
|
+
haunted_owner_uuid = self.add_formula_owner(
|
|
1572
|
+
table_info_id,
|
|
1573
|
+
num_rows,
|
|
1574
|
+
num_cols,
|
|
1575
|
+
number_of_header_rows,
|
|
1576
|
+
number_of_header_columns,
|
|
1577
|
+
)
|
|
1578
|
+
table_model.haunted_owner.owner_uid.MergeFrom(haunted_owner_uuid.protobuf2)
|
|
1579
|
+
self.calculate_table_uuid_map()
|
|
1580
|
+
|
|
1581
|
+
self._table_data[table_model_id] = [
|
|
1582
|
+
[Cell._empty_cell(table_model_id, row, col, self) for col in range(num_cols)]
|
|
1583
|
+
for row in range(num_rows)
|
|
1584
|
+
]
|
|
1585
|
+
self.recalculate_table_data(table_model_id, self._table_data[table_model_id])
|
|
1586
|
+
|
|
1497
1587
|
self.add_component_reference(
|
|
1498
1588
|
table_info_id,
|
|
1499
1589
|
location="Document",
|
|
@@ -1502,17 +1592,11 @@ class _NumbersModel(Cacheable):
|
|
|
1502
1592
|
self.create_caption_archive(table_model_id)
|
|
1503
1593
|
self.caption_enabled(table_model_id, False)
|
|
1504
1594
|
|
|
1505
|
-
self.add_formula_owner(
|
|
1506
|
-
table_info_id,
|
|
1507
|
-
num_rows,
|
|
1508
|
-
num_cols,
|
|
1509
|
-
number_of_header_rows,
|
|
1510
|
-
number_of_header_columns,
|
|
1511
|
-
)
|
|
1512
|
-
|
|
1513
1595
|
self.objects[sheet_id].drawable_infos.append(
|
|
1514
1596
|
TSPMessages.Reference(identifier=table_info_id),
|
|
1515
1597
|
)
|
|
1598
|
+
|
|
1599
|
+
self.name_ref_cache.mark_dirty()
|
|
1516
1600
|
return table_model_id
|
|
1517
1601
|
|
|
1518
1602
|
def add_formula_owner(
|
|
@@ -1522,7 +1606,7 @@ class _NumbersModel(Cacheable):
|
|
|
1522
1606
|
num_cols: int,
|
|
1523
1607
|
number_of_header_rows: int,
|
|
1524
1608
|
number_of_header_columns: int,
|
|
1525
|
-
) ->
|
|
1609
|
+
) -> NumbersUUID:
|
|
1526
1610
|
"""
|
|
1527
1611
|
Create a FormulaOwnerDependenciesArchive that references a TableInfoArchive
|
|
1528
1612
|
so that cross-references to cells in this table will work.
|
|
@@ -1531,49 +1615,43 @@ class _NumbersModel(Cacheable):
|
|
|
1531
1615
|
calc_engine = self.calc_engine()
|
|
1532
1616
|
owner_id_map = calc_engine.dependency_tracker.owner_id_map.map_entry
|
|
1533
1617
|
next_owner_id = max([x.internal_owner_id for x in owner_id_map]) + 1
|
|
1534
|
-
|
|
1618
|
+
volatile_dependencies = {
|
|
1619
|
+
"volatile_time_cells": {},
|
|
1620
|
+
"volatile_random_cells": {},
|
|
1621
|
+
"volatile_locale_cells": {},
|
|
1622
|
+
"volatile_sheet_table_name_cells": {},
|
|
1623
|
+
"volatile_remote_data_cells": {},
|
|
1624
|
+
"volatile_geometry_cell_refs": {},
|
|
1625
|
+
}
|
|
1626
|
+
total_range_for_table = {
|
|
1627
|
+
"top_left_column": 0,
|
|
1628
|
+
"top_left_row": 0,
|
|
1629
|
+
"bottom_right_column": num_cols - 1,
|
|
1630
|
+
"bottom_right_row": num_cols - 1,
|
|
1631
|
+
}
|
|
1632
|
+
body_range_for_table = {
|
|
1633
|
+
"top_left_column": number_of_header_columns,
|
|
1634
|
+
"top_left_row": number_of_header_rows,
|
|
1635
|
+
"bottom_right_column": num_cols - 1,
|
|
1636
|
+
"bottom_right_row": num_cols - 1,
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
formula_deps_id, _ = self.objects.create_object_from_dict(
|
|
1535
1640
|
"CalculationEngine",
|
|
1536
1641
|
{
|
|
1537
1642
|
"formula_owner_uid": formula_owner_uuid.dict2,
|
|
1538
1643
|
"internal_formula_owner_id": next_owner_id,
|
|
1539
|
-
"owner_kind":
|
|
1644
|
+
"owner_kind": OwnerKind.TABLE_MODEL,
|
|
1540
1645
|
"cell_dependencies": {},
|
|
1541
1646
|
"range_dependencies": {},
|
|
1542
|
-
"volatile_dependencies":
|
|
1543
|
-
"volatile_time_cells": {},
|
|
1544
|
-
"volatile_random_cells": {},
|
|
1545
|
-
"volatile_locale_cells": {},
|
|
1546
|
-
"volatile_sheet_table_name_cells": {},
|
|
1547
|
-
"volatile_remote_data_cells": {},
|
|
1548
|
-
"volatile_geometry_cell_refs": {},
|
|
1549
|
-
},
|
|
1647
|
+
"volatile_dependencies": volatile_dependencies,
|
|
1550
1648
|
"spanning_column_dependencies": {
|
|
1551
|
-
"total_range_for_table":
|
|
1552
|
-
|
|
1553
|
-
"top_left_row": 0,
|
|
1554
|
-
"bottom_right_column": num_cols - 1,
|
|
1555
|
-
"bottom_right_row": num_cols - 1,
|
|
1556
|
-
},
|
|
1557
|
-
"body_range_for_table": {
|
|
1558
|
-
"top_left_column": number_of_header_columns,
|
|
1559
|
-
"top_left_row": number_of_header_rows,
|
|
1560
|
-
"bottom_right_column": num_cols - 1,
|
|
1561
|
-
"bottom_right_row": num_cols - 1,
|
|
1562
|
-
},
|
|
1649
|
+
"total_range_for_table": total_range_for_table,
|
|
1650
|
+
"body_range_for_table": body_range_for_table,
|
|
1563
1651
|
},
|
|
1564
1652
|
"spanning_row_dependencies": {
|
|
1565
|
-
"total_range_for_table":
|
|
1566
|
-
|
|
1567
|
-
"top_left_row": 0,
|
|
1568
|
-
"bottom_right_column": num_cols - 1,
|
|
1569
|
-
"bottom_right_row": num_cols - 1,
|
|
1570
|
-
},
|
|
1571
|
-
"body_range_for_table": {
|
|
1572
|
-
"top_left_column": number_of_header_columns,
|
|
1573
|
-
"top_left_row": number_of_header_rows,
|
|
1574
|
-
"bottom_right_column": num_cols - 1,
|
|
1575
|
-
"bottom_right_row": num_cols - 1,
|
|
1576
|
-
},
|
|
1653
|
+
"total_range_for_table": total_range_for_table,
|
|
1654
|
+
"body_range_for_table": body_range_for_table,
|
|
1577
1655
|
},
|
|
1578
1656
|
"whole_owner_dependencies": {"dependent_cells": {}},
|
|
1579
1657
|
"cell_errors": {},
|
|
@@ -1594,6 +1672,52 @@ class _NumbersModel(Cacheable):
|
|
|
1594
1672
|
),
|
|
1595
1673
|
)
|
|
1596
1674
|
|
|
1675
|
+
# See Numbers.md#uuid-mapping for more details on mapping table model
|
|
1676
|
+
# UUID to the formula owner.
|
|
1677
|
+
formula_owner_uuid = NumbersUUID()
|
|
1678
|
+
base_owner_uuid = NumbersUUID()
|
|
1679
|
+
next_owner_id += 1
|
|
1680
|
+
null_range_ref = {
|
|
1681
|
+
"top_left_column": 0x7FFF,
|
|
1682
|
+
"top_left_row": 0x7FFFFFFF,
|
|
1683
|
+
"bottom_right_column": 0x7FFF,
|
|
1684
|
+
"bottom_right_row": 0x7FFFFFFF,
|
|
1685
|
+
}
|
|
1686
|
+
spanning_depdendencies = {
|
|
1687
|
+
"total_range_for_table": null_range_ref,
|
|
1688
|
+
"body_range_for_table": null_range_ref,
|
|
1689
|
+
}
|
|
1690
|
+
formula_deps_id, formula_deps = self.objects.create_object_from_dict(
|
|
1691
|
+
"CalculationEngine",
|
|
1692
|
+
{
|
|
1693
|
+
"formula_owner_uid": formula_owner_uuid.dict2,
|
|
1694
|
+
"internal_formula_owner_id": next_owner_id,
|
|
1695
|
+
"owner_kind": OwnerKind.HAUNTED_OWNER,
|
|
1696
|
+
"cell_dependencies": {},
|
|
1697
|
+
"range_dependencies": {},
|
|
1698
|
+
"volatile_dependencies": volatile_dependencies,
|
|
1699
|
+
"spanning_column_dependencies": spanning_depdendencies,
|
|
1700
|
+
"spanning_row_dependencies": spanning_depdendencies,
|
|
1701
|
+
"whole_owner_dependencies": {"dependent_cells": {}},
|
|
1702
|
+
"cell_errors": {},
|
|
1703
|
+
"base_owner_uid": base_owner_uuid.dict2,
|
|
1704
|
+
"tiled_cell_dependencies": {},
|
|
1705
|
+
"uuid_references": {},
|
|
1706
|
+
"tiled_range_dependencies": {},
|
|
1707
|
+
},
|
|
1708
|
+
TSCEArchives.FormulaOwnerDependenciesArchive,
|
|
1709
|
+
)
|
|
1710
|
+
calc_engine.dependency_tracker.formula_owner_dependencies.append(
|
|
1711
|
+
TSPMessages.Reference(identifier=formula_deps_id),
|
|
1712
|
+
)
|
|
1713
|
+
owner_id_map.append(
|
|
1714
|
+
TSCEArchives.OwnerIDMapArchive.OwnerIDMapArchiveEntry(
|
|
1715
|
+
internal_owner_id=next_owner_id,
|
|
1716
|
+
owner_id=formula_owner_uuid.protobuf4,
|
|
1717
|
+
),
|
|
1718
|
+
)
|
|
1719
|
+
return formula_owner_uuid
|
|
1720
|
+
|
|
1597
1721
|
def add_sheet(self, sheet_name: str) -> int:
|
|
1598
1722
|
"""Add a new sheet with a copy of a table from another sheet."""
|
|
1599
1723
|
sheet_id, _ = self.objects.create_object_from_dict(
|
|
@@ -2448,39 +2572,6 @@ def formatted_number(number_type, index):
|
|
|
2448
2572
|
return bullet_char
|
|
2449
2573
|
|
|
2450
2574
|
|
|
2451
|
-
def node_to_col_ref(node: object, table_name: str, col: int) -> str:
|
|
2452
|
-
col = node.AST_column.column if node.AST_column.absolute else col + node.AST_column.column
|
|
2453
|
-
|
|
2454
|
-
col_name = xl_col_to_name(col, node.AST_column.absolute)
|
|
2455
|
-
if table_name is not None:
|
|
2456
|
-
return f"{table_name}::{col_name}"
|
|
2457
|
-
return col_name
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
def node_to_row_ref(node: object, table_name: str, row: int) -> str:
|
|
2461
|
-
row = node.AST_row.row if node.AST_row.absolute else row + node.AST_row.row
|
|
2462
|
-
|
|
2463
|
-
row_name = f"${row + 1}" if node.AST_row.absolute else f"{row + 1}"
|
|
2464
|
-
if table_name is not None:
|
|
2465
|
-
return f"{table_name}::{row_name}:{row_name}"
|
|
2466
|
-
return f"{row_name}:{row_name}"
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
def node_to_row_col_ref(node: object, table_name: str, row: int, col: int) -> str:
|
|
2470
|
-
row = node.AST_row.row if node.AST_row.absolute else row + node.AST_row.row
|
|
2471
|
-
col = node.AST_column.column if node.AST_column.absolute else col + node.AST_column.column
|
|
2472
|
-
|
|
2473
|
-
ref = xl_rowcol_to_cell(
|
|
2474
|
-
row,
|
|
2475
|
-
col,
|
|
2476
|
-
row_abs=node.AST_row.absolute,
|
|
2477
|
-
col_abs=node.AST_column.absolute,
|
|
2478
|
-
)
|
|
2479
|
-
if table_name is not None:
|
|
2480
|
-
return f"{table_name}::{ref}"
|
|
2481
|
-
return ref
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
2575
|
def get_storage_buffers_for_row(
|
|
2485
2576
|
storage_buffer: bytes,
|
|
2486
2577
|
offsets: list,
|