numbers-parser 4.14.3__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/_cat_numbers.py +15 -6
- 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 +281 -187
- numbers_parser/numbers_uuid.py +6 -0
- numbers_parser/tokenizer.py +548 -0
- numbers_parser/xrefs.py +850 -0
- {numbers_parser-4.14.3.dist-info → numbers_parser-4.15.1.dist-info}/METADATA +8 -25
- {numbers_parser-4.14.3.dist-info → numbers_parser-4.15.1.dist-info}/RECORD +19 -17
- {numbers_parser-4.14.3.dist-info → numbers_parser-4.15.1.dist-info}/WHEEL +1 -1
- {numbers_parser-4.14.3.dist-info → numbers_parser-4.15.1.dist-info}/LICENSE.rst +0 -0
- {numbers_parser-4.14.3.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
|
|
@@ -706,7 +712,10 @@ class _NumbersModel(Cacheable):
|
|
|
706
712
|
@cache(num_args=2)
|
|
707
713
|
def table_string(self, table_id: int, key: int) -> str:
|
|
708
714
|
"""Return the string associated with a string ID for a particular table."""
|
|
709
|
-
|
|
715
|
+
try:
|
|
716
|
+
return self._table_strings.lookup_value(table_id, key).string
|
|
717
|
+
except KeyError:
|
|
718
|
+
return ""
|
|
710
719
|
|
|
711
720
|
def init_table_strings(self, table_id: int) -> None:
|
|
712
721
|
"""Cache table strings reference and delete all existing keys/values."""
|
|
@@ -748,51 +757,77 @@ class _NumbersModel(Cacheable):
|
|
|
748
757
|
owner_id_map[e.internal_owner_id] = NumbersUUID(e.owner_id).hex
|
|
749
758
|
return owner_id_map
|
|
750
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
|
+
|
|
751
794
|
@cache()
|
|
752
795
|
def table_base_id(self, table_id: int) -> int:
|
|
753
796
|
""" "Finds the UUID of a table."""
|
|
754
|
-
#
|
|
755
|
-
|
|
756
|
-
#
|
|
757
|
-
# "base_owner_uid": "6a4a5281-7b06-f5a1-904b-7f9ec784b368"",
|
|
758
|
-
# "formula_owner_uid": "6a4a5281-7b06-f5a1-904b-7f9ec784b36d"
|
|
759
|
-
#
|
|
760
|
-
# The Table UUID is the TSCE.FormulaOwnerDependenciesArchive whose formula_owner_uid
|
|
761
|
-
# matches the UUID of the haunted_owner of the Table:
|
|
762
|
-
#
|
|
763
|
-
# "haunted_owner": {
|
|
764
|
-
# "owner_uid": "6a4a5281-7b06-f5a1-904b-7f9ec784b368""
|
|
765
|
-
# }
|
|
766
|
-
haunted_owner = NumbersUUID(self.objects[table_id].haunted_owner.owner_uid).hex
|
|
767
|
-
formula_owner_ids = self.find_refs("FormulaOwnerDependenciesArchive")
|
|
768
|
-
for dependency_id in formula_owner_ids: # pragma: no branch
|
|
769
|
-
obj = self.objects[dependency_id]
|
|
770
|
-
# if obj.owner_kind == OwnerKind.HAUNTED_OWNER:
|
|
771
|
-
if obj.HasField("base_owner_uid") and obj.HasField(
|
|
772
|
-
"formula_owner_uid",
|
|
773
|
-
): # pragma: no branch
|
|
774
|
-
base_owner_uid = NumbersUUID(obj.base_owner_uid).hex
|
|
775
|
-
formula_owner_uid = NumbersUUID(obj.formula_owner_uid).hex
|
|
776
|
-
if formula_owner_uid == haunted_owner:
|
|
777
|
-
return base_owner_uid
|
|
778
|
-
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)
|
|
779
799
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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:
|
|
784
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
|
|
785
808
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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])
|
|
796
831
|
|
|
797
832
|
@cache(num_args=0)
|
|
798
833
|
def calc_engine_id(self):
|
|
@@ -812,8 +847,8 @@ class _NumbersModel(Cacheable):
|
|
|
812
847
|
|
|
813
848
|
@cache()
|
|
814
849
|
def calculate_merge_cell_ranges(self, table_id) -> None:
|
|
815
|
-
"""
|
|
816
|
-
#
|
|
850
|
+
"""Extract all the merge cell ranges for the Table."""
|
|
851
|
+
# See details in Numbers.md#merge-ranges.
|
|
817
852
|
owner_id_map = self.owner_id_map()
|
|
818
853
|
table_base_id = self.table_base_id(table_id)
|
|
819
854
|
|
|
@@ -876,6 +911,17 @@ class _NumbersModel(Cacheable):
|
|
|
876
911
|
return sheet_id
|
|
877
912
|
return None
|
|
878
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
|
+
|
|
879
925
|
@cache()
|
|
880
926
|
def table_uuids_to_id(self, table_uuid) -> int | None:
|
|
881
927
|
for sheet_id in self.sheet_ids(): # pragma: no branch # noqa: RET503
|
|
@@ -883,66 +929,103 @@ class _NumbersModel(Cacheable):
|
|
|
883
929
|
if table_uuid == self.table_base_id(table_id):
|
|
884
930
|
return table_id
|
|
885
931
|
|
|
886
|
-
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
|
+
|
|
887
947
|
if node.HasField("AST_cross_table_reference_extra_info"):
|
|
888
948
|
table_uuid = NumbersUUID(node.AST_cross_table_reference_extra_info.table_id).hex
|
|
889
|
-
|
|
890
|
-
other_table_name = self.table_name(other_table_id)
|
|
949
|
+
to_table_id = self.table_uuids_to_id(table_uuid)
|
|
891
950
|
else:
|
|
892
|
-
|
|
893
|
-
other_table_name = None
|
|
894
|
-
|
|
895
|
-
if other_table_id is not None:
|
|
896
|
-
this_sheet_id = self.table_id_to_sheet_id(this_table_id)
|
|
897
|
-
other_sheet_id = self.table_id_to_sheet_id(other_table_id)
|
|
898
|
-
if this_sheet_id != other_sheet_id:
|
|
899
|
-
other_sheet_name = self.sheet_name(other_sheet_id)
|
|
900
|
-
other_table_name = f"{other_sheet_name}::" + other_table_name
|
|
951
|
+
to_table_id = None
|
|
901
952
|
|
|
902
953
|
if node.HasField("AST_colon_tract"):
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
+
)
|
|
909
961
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|
+
)
|
|
915
969
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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
|
+
)
|
|
920
977
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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
|
+
)
|
|
925
985
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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
|
+
)
|
|
930
999
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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,
|
|
942
1028
|
)
|
|
943
|
-
if table_name is not None:
|
|
944
|
-
return f"{table_name}::{begin_ref}:{end_ref}"
|
|
945
|
-
return f"{begin_ref}:{end_ref}"
|
|
946
1029
|
|
|
947
1030
|
@cache()
|
|
948
1031
|
def formula_ast(self, table_id: int):
|
|
@@ -1477,12 +1560,6 @@ class _NumbersModel(Cacheable):
|
|
|
1477
1560
|
TSPMessages.Reference(identifier=row_headers_id),
|
|
1478
1561
|
)
|
|
1479
1562
|
|
|
1480
|
-
self._table_data[table_model_id] = [
|
|
1481
|
-
[Cell._empty_cell(table_model_id, row, col, self) for col in range(num_cols)]
|
|
1482
|
-
for row in range(num_rows)
|
|
1483
|
-
]
|
|
1484
|
-
self.recalculate_table_data(table_model_id, self._table_data[table_model_id])
|
|
1485
|
-
|
|
1486
1563
|
table_info_id, table_info = self.objects.create_object_from_dict(
|
|
1487
1564
|
"CalculationEngine",
|
|
1488
1565
|
{},
|
|
@@ -1491,6 +1568,22 @@ class _NumbersModel(Cacheable):
|
|
|
1491
1568
|
table_info.tableModel.MergeFrom(TSPMessages.Reference(identifier=table_model_id))
|
|
1492
1569
|
table_info.super.MergeFrom(self.create_drawable(sheet_id, x, y))
|
|
1493
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
|
+
|
|
1494
1587
|
self.add_component_reference(
|
|
1495
1588
|
table_info_id,
|
|
1496
1589
|
location="Document",
|
|
@@ -1499,17 +1592,11 @@ class _NumbersModel(Cacheable):
|
|
|
1499
1592
|
self.create_caption_archive(table_model_id)
|
|
1500
1593
|
self.caption_enabled(table_model_id, False)
|
|
1501
1594
|
|
|
1502
|
-
self.add_formula_owner(
|
|
1503
|
-
table_info_id,
|
|
1504
|
-
num_rows,
|
|
1505
|
-
num_cols,
|
|
1506
|
-
number_of_header_rows,
|
|
1507
|
-
number_of_header_columns,
|
|
1508
|
-
)
|
|
1509
|
-
|
|
1510
1595
|
self.objects[sheet_id].drawable_infos.append(
|
|
1511
1596
|
TSPMessages.Reference(identifier=table_info_id),
|
|
1512
1597
|
)
|
|
1598
|
+
|
|
1599
|
+
self.name_ref_cache.mark_dirty()
|
|
1513
1600
|
return table_model_id
|
|
1514
1601
|
|
|
1515
1602
|
def add_formula_owner(
|
|
@@ -1519,7 +1606,7 @@ class _NumbersModel(Cacheable):
|
|
|
1519
1606
|
num_cols: int,
|
|
1520
1607
|
number_of_header_rows: int,
|
|
1521
1608
|
number_of_header_columns: int,
|
|
1522
|
-
) ->
|
|
1609
|
+
) -> NumbersUUID:
|
|
1523
1610
|
"""
|
|
1524
1611
|
Create a FormulaOwnerDependenciesArchive that references a TableInfoArchive
|
|
1525
1612
|
so that cross-references to cells in this table will work.
|
|
@@ -1528,49 +1615,43 @@ class _NumbersModel(Cacheable):
|
|
|
1528
1615
|
calc_engine = self.calc_engine()
|
|
1529
1616
|
owner_id_map = calc_engine.dependency_tracker.owner_id_map.map_entry
|
|
1530
1617
|
next_owner_id = max([x.internal_owner_id for x in owner_id_map]) + 1
|
|
1531
|
-
|
|
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(
|
|
1532
1640
|
"CalculationEngine",
|
|
1533
1641
|
{
|
|
1534
1642
|
"formula_owner_uid": formula_owner_uuid.dict2,
|
|
1535
1643
|
"internal_formula_owner_id": next_owner_id,
|
|
1536
|
-
"owner_kind":
|
|
1644
|
+
"owner_kind": OwnerKind.TABLE_MODEL,
|
|
1537
1645
|
"cell_dependencies": {},
|
|
1538
1646
|
"range_dependencies": {},
|
|
1539
|
-
"volatile_dependencies":
|
|
1540
|
-
"volatile_time_cells": {},
|
|
1541
|
-
"volatile_random_cells": {},
|
|
1542
|
-
"volatile_locale_cells": {},
|
|
1543
|
-
"volatile_sheet_table_name_cells": {},
|
|
1544
|
-
"volatile_remote_data_cells": {},
|
|
1545
|
-
"volatile_geometry_cell_refs": {},
|
|
1546
|
-
},
|
|
1647
|
+
"volatile_dependencies": volatile_dependencies,
|
|
1547
1648
|
"spanning_column_dependencies": {
|
|
1548
|
-
"total_range_for_table":
|
|
1549
|
-
|
|
1550
|
-
"top_left_row": 0,
|
|
1551
|
-
"bottom_right_column": num_cols - 1,
|
|
1552
|
-
"bottom_right_row": num_cols - 1,
|
|
1553
|
-
},
|
|
1554
|
-
"body_range_for_table": {
|
|
1555
|
-
"top_left_column": number_of_header_columns,
|
|
1556
|
-
"top_left_row": number_of_header_rows,
|
|
1557
|
-
"bottom_right_column": num_cols - 1,
|
|
1558
|
-
"bottom_right_row": num_cols - 1,
|
|
1559
|
-
},
|
|
1649
|
+
"total_range_for_table": total_range_for_table,
|
|
1650
|
+
"body_range_for_table": body_range_for_table,
|
|
1560
1651
|
},
|
|
1561
1652
|
"spanning_row_dependencies": {
|
|
1562
|
-
"total_range_for_table":
|
|
1563
|
-
|
|
1564
|
-
"top_left_row": 0,
|
|
1565
|
-
"bottom_right_column": num_cols - 1,
|
|
1566
|
-
"bottom_right_row": num_cols - 1,
|
|
1567
|
-
},
|
|
1568
|
-
"body_range_for_table": {
|
|
1569
|
-
"top_left_column": number_of_header_columns,
|
|
1570
|
-
"top_left_row": number_of_header_rows,
|
|
1571
|
-
"bottom_right_column": num_cols - 1,
|
|
1572
|
-
"bottom_right_row": num_cols - 1,
|
|
1573
|
-
},
|
|
1653
|
+
"total_range_for_table": total_range_for_table,
|
|
1654
|
+
"body_range_for_table": body_range_for_table,
|
|
1574
1655
|
},
|
|
1575
1656
|
"whole_owner_dependencies": {"dependent_cells": {}},
|
|
1576
1657
|
"cell_errors": {},
|
|
@@ -1591,6 +1672,52 @@ class _NumbersModel(Cacheable):
|
|
|
1591
1672
|
),
|
|
1592
1673
|
)
|
|
1593
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
|
+
|
|
1594
1721
|
def add_sheet(self, sheet_name: str) -> int:
|
|
1595
1722
|
"""Add a new sheet with a copy of a table from another sheet."""
|
|
1596
1723
|
sheet_id, _ = self.objects.create_object_from_dict(
|
|
@@ -2445,39 +2572,6 @@ def formatted_number(number_type, index):
|
|
|
2445
2572
|
return bullet_char
|
|
2446
2573
|
|
|
2447
2574
|
|
|
2448
|
-
def node_to_col_ref(node: object, table_name: str, col: int) -> str:
|
|
2449
|
-
col = node.AST_column.column if node.AST_column.absolute else col + node.AST_column.column
|
|
2450
|
-
|
|
2451
|
-
col_name = xl_col_to_name(col, node.AST_column.absolute)
|
|
2452
|
-
if table_name is not None:
|
|
2453
|
-
return f"{table_name}::{col_name}"
|
|
2454
|
-
return col_name
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
def node_to_row_ref(node: object, table_name: str, row: int) -> str:
|
|
2458
|
-
row = node.AST_row.row if node.AST_row.absolute else row + node.AST_row.row
|
|
2459
|
-
|
|
2460
|
-
row_name = f"${row + 1}" if node.AST_row.absolute else f"{row + 1}"
|
|
2461
|
-
if table_name is not None:
|
|
2462
|
-
return f"{table_name}::{row_name}:{row_name}"
|
|
2463
|
-
return f"{row_name}:{row_name}"
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
def node_to_row_col_ref(node: object, table_name: str, row: int, col: int) -> str:
|
|
2467
|
-
row = node.AST_row.row if node.AST_row.absolute else row + node.AST_row.row
|
|
2468
|
-
col = node.AST_column.column if node.AST_column.absolute else col + node.AST_column.column
|
|
2469
|
-
|
|
2470
|
-
ref = xl_rowcol_to_cell(
|
|
2471
|
-
row,
|
|
2472
|
-
col,
|
|
2473
|
-
row_abs=node.AST_row.absolute,
|
|
2474
|
-
col_abs=node.AST_column.absolute,
|
|
2475
|
-
)
|
|
2476
|
-
if table_name is not None:
|
|
2477
|
-
return f"{table_name}::{ref}"
|
|
2478
|
-
return ref
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
2575
|
def get_storage_buffers_for_row(
|
|
2482
2576
|
storage_buffer: bytes,
|
|
2483
2577
|
offsets: list,
|