berkeley_library-tind 0.4.0
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.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +18 -0
- data/.gitignore +388 -0
- data/.idea/inspectionProfiles/Project_Default.xml +20 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/tind.iml +138 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +334 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/.yardopts +1 -0
- data/CHANGES.md +58 -0
- data/Dockerfile +57 -0
- data/Gemfile +3 -0
- data/Jenkinsfile +18 -0
- data/LICENSE.md +21 -0
- data/README.md +73 -0
- data/Rakefile +20 -0
- data/berkeley_library-tind.gemspec +50 -0
- data/bin/tind-export +14 -0
- data/docker-compose.yml +15 -0
- data/lib/berkeley_library/tind.rb +3 -0
- data/lib/berkeley_library/tind/api.rb +1 -0
- data/lib/berkeley_library/tind/api/api.rb +132 -0
- data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
- data/lib/berkeley_library/tind/api/collection.rb +82 -0
- data/lib/berkeley_library/tind/api/date_range.rb +67 -0
- data/lib/berkeley_library/tind/api/format.rb +32 -0
- data/lib/berkeley_library/tind/api/search.rb +100 -0
- data/lib/berkeley_library/tind/config.rb +103 -0
- data/lib/berkeley_library/tind/export.rb +1 -0
- data/lib/berkeley_library/tind/export/column.rb +54 -0
- data/lib/berkeley_library/tind/export/column_group.rb +144 -0
- data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
- data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
- data/lib/berkeley_library/tind/export/config.rb +154 -0
- data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
- data/lib/berkeley_library/tind/export/export.rb +47 -0
- data/lib/berkeley_library/tind/export/export_command.rb +168 -0
- data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
- data/lib/berkeley_library/tind/export/export_format.rb +67 -0
- data/lib/berkeley_library/tind/export/exporter.rb +105 -0
- data/lib/berkeley_library/tind/export/filter.rb +52 -0
- data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
- data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
- data/lib/berkeley_library/tind/export/row.rb +24 -0
- data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
- data/lib/berkeley_library/tind/export/table.rb +175 -0
- data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
- data/lib/berkeley_library/tind/marc.rb +1 -0
- data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
- data/lib/berkeley_library/tind/module_info.rb +14 -0
- data/lib/berkeley_library/util/arrays.rb +178 -0
- data/lib/berkeley_library/util/logging.rb +1 -0
- data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
- data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
- data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
- data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
- data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
- data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
- data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
- data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
- data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
- data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
- data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
- data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
- data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
- data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
- data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
- data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
- data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
- data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
- data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
- data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
- data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
- data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
- data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
- data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
- data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
- data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
- data/lib/berkeley_library/util/paths.rb +111 -0
- data/lib/berkeley_library/util/stringios.rb +30 -0
- data/lib/berkeley_library/util/strings.rb +42 -0
- data/lib/berkeley_library/util/sys_exits.rb +15 -0
- data/lib/berkeley_library/util/times.rb +22 -0
- data/lib/berkeley_library/util/uris.rb +44 -0
- data/lib/berkeley_library/util/uris/appender.rb +162 -0
- data/lib/berkeley_library/util/uris/requester.rb +62 -0
- data/lib/berkeley_library/util/uris/validator.rb +32 -0
- data/rakelib/bundle.rake +8 -0
- data/rakelib/coverage.rake +11 -0
- data/rakelib/gem.rake +54 -0
- data/rakelib/rubocop.rake +18 -0
- data/rakelib/spec.rake +2 -0
- data/spec/.rubocop.yml +40 -0
- data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
- data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
- data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
- data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
- data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
- data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
- data/spec/berkeley_library/tind/config_spec.rb +86 -0
- data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
- data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
- data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
- data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
- data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
- data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
- data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
- data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
- data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
- data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
- data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
- data/spec/berkeley_library/util/arrays_spec.rb +340 -0
- data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
- data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
- data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
- data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
- data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
- data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
- data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
- data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
- data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
- data/spec/berkeley_library/util/paths_spec.rb +90 -0
- data/spec/berkeley_library/util/stringios_spec.rb +34 -0
- data/spec/berkeley_library/util/strings_spec.rb +27 -0
- data/spec/berkeley_library/util/times_spec.rb +39 -0
- data/spec/berkeley_library/util/uris_spec.rb +118 -0
- data/spec/data/collection-names.txt +438 -0
- data/spec/data/collections.json +4827 -0
- data/spec/data/disjoint-records.xml +187 -0
- data/spec/data/record-184453.xml +58 -0
- data/spec/data/record-184458.xml +63 -0
- data/spec/data/record-187888.xml +78 -0
- data/spec/data/records-api-search-cjk-p1.xml +6381 -0
- data/spec/data/records-api-search-cjk-p2.xml +5 -0
- data/spec/data/records-api-search-p1.xml +4506 -0
- data/spec/data/records-api-search-p2.xml +4509 -0
- data/spec/data/records-api-search-p3.xml +4506 -0
- data/spec/data/records-api-search-p4.xml +4509 -0
- data/spec/data/records-api-search-p5.xml +4506 -0
- data/spec/data/records-api-search-p6.xml +2436 -0
- data/spec/data/records-api-search-p7.xml +5 -0
- data/spec/data/records-api-search.xml +234 -0
- data/spec/data/records-manual-search.xml +547 -0
- data/spec/spec_helper.rb +30 -0
- data/test/profile/table_from_records_profile.rb +46 -0
- metadata +585 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
require 'berkeley_library/util/ods/xml/element_node'
|
|
2
|
+
require 'berkeley_library/util/ods/xml/loext/table_protection'
|
|
3
|
+
require 'berkeley_library/util/ods/xml/style/column_style'
|
|
4
|
+
require 'berkeley_library/util/ods/xml/table/table_column'
|
|
5
|
+
require 'berkeley_library/util/ods/xml/table/table_row'
|
|
6
|
+
|
|
7
|
+
module BerkeleyLibrary
|
|
8
|
+
module Util
|
|
9
|
+
module ODS
|
|
10
|
+
module XML
|
|
11
|
+
module Table
|
|
12
|
+
class Table < XML::ElementNode
|
|
13
|
+
# ------------------------------------------------------------
|
|
14
|
+
# Constants
|
|
15
|
+
|
|
16
|
+
MIN_COLUMNS = 1024
|
|
17
|
+
MIN_ROWS = 1_048_576
|
|
18
|
+
|
|
19
|
+
# ------------------------------------------------------------
|
|
20
|
+
# Accessors
|
|
21
|
+
|
|
22
|
+
attr_reader :table_name
|
|
23
|
+
|
|
24
|
+
attr_reader :table_style
|
|
25
|
+
|
|
26
|
+
# @return [XML::Office::AutomaticStyles] the document styles
|
|
27
|
+
attr_reader :styles
|
|
28
|
+
|
|
29
|
+
# ------------------------------------------------------------
|
|
30
|
+
# Initializers
|
|
31
|
+
|
|
32
|
+
# Initializes a new table
|
|
33
|
+
#
|
|
34
|
+
# @param name [String] the table name
|
|
35
|
+
# @param style [XML::Style::TableStyle] the table style, if other than default
|
|
36
|
+
# @param styles [XML::Office::AutomaticStyles] the document styles
|
|
37
|
+
# @param protected [Boolean] whether the table is protected
|
|
38
|
+
def initialize(table_name, table_style = nil, styles:, protected: true)
|
|
39
|
+
super(:table, 'table', doc: styles.doc)
|
|
40
|
+
|
|
41
|
+
@table_name = table_name
|
|
42
|
+
@table_style = table_style || styles.default_style(:table)
|
|
43
|
+
@styles = styles
|
|
44
|
+
|
|
45
|
+
set_attribute('name', self.table_name)
|
|
46
|
+
set_attribute('style-name', self.table_style.style_name)
|
|
47
|
+
|
|
48
|
+
protect! if protected
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# ------------------------------------------------------------
|
|
52
|
+
# Accessors and utility methods
|
|
53
|
+
|
|
54
|
+
def column_count
|
|
55
|
+
@column_count ||= 0
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def row_count
|
|
59
|
+
@row_count ||= 0
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get_value_at(row_index, column_index)
|
|
63
|
+
(row = rows[row_index]) && row.get_value_at(column_index)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def add_column(header, width = nil, protected: false)
|
|
67
|
+
add_column_with_styles(
|
|
68
|
+
header,
|
|
69
|
+
column_style: styles.find_or_create_column_style(width),
|
|
70
|
+
default_cell_style: styles.find_or_create_cell_style(protected)
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def add_column_with_styles(header, column_style:, default_cell_style: nil, header_cell_style: nil)
|
|
75
|
+
cell_style = default_cell_style || styles.find_or_create_cell_style
|
|
76
|
+
add_or_repeat_column(column_style, cell_style).tap do
|
|
77
|
+
header_row = rows[0] || add_row
|
|
78
|
+
header_row.set_value_at(column_count, header, header_cell_style)
|
|
79
|
+
self.column_count += 1
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def add_empty_columns(number_repeated, width = nil, protected: false)
|
|
84
|
+
column_style = styles.find_or_create_column_style(width)
|
|
85
|
+
default_cell_style = styles.find_or_create_cell_style(protected)
|
|
86
|
+
|
|
87
|
+
TableColumn.new(column_style, default_cell_style, number_repeated, table: self).tap do |col|
|
|
88
|
+
columns << col
|
|
89
|
+
self.column_count += number_repeated
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Adds a new row with the specified height.
|
|
94
|
+
# @param height [String] the row height. Defaults to {XML::Style::RowStyle::DEFAULT_HEIGHT}.
|
|
95
|
+
# @param number_repeated [Integer] the number of identical rows to repeat
|
|
96
|
+
# @return [TableRow] the new row
|
|
97
|
+
def add_row(height = nil, number_repeated = 1)
|
|
98
|
+
row_style = styles.find_or_create_row_style(height)
|
|
99
|
+
TableRow.new(row_style, number_repeated, table: self).tap do |row|
|
|
100
|
+
rows << row
|
|
101
|
+
self.row_count += number_repeated
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# ------------------------------------------------------------
|
|
106
|
+
# Public XML::ElementNode overrides
|
|
107
|
+
|
|
108
|
+
def add_child(child)
|
|
109
|
+
return child.tap { |column| columns << column } if child.is_a?(TableColumn)
|
|
110
|
+
return child.tap { |row| rows << row } if child.is_a?(TableRow)
|
|
111
|
+
|
|
112
|
+
child.tap { |c| other_children << c }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# ------------------------------------------------------------
|
|
116
|
+
# Protected methods
|
|
117
|
+
|
|
118
|
+
protected
|
|
119
|
+
|
|
120
|
+
# ----------------------------------------
|
|
121
|
+
# Protected XML::ElementNode overrides
|
|
122
|
+
|
|
123
|
+
def children
|
|
124
|
+
[other_children, columns, rows].flatten
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def create_element
|
|
128
|
+
ensure_empty_columns!
|
|
129
|
+
ensure_empty_rows!
|
|
130
|
+
|
|
131
|
+
super
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ------------------------------------------------------------
|
|
135
|
+
# Private methods
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
# ------------------------------
|
|
140
|
+
# Private writers
|
|
141
|
+
|
|
142
|
+
attr_writer :column_count
|
|
143
|
+
|
|
144
|
+
attr_writer :row_count
|
|
145
|
+
|
|
146
|
+
# ------------------------------
|
|
147
|
+
# Private readers
|
|
148
|
+
|
|
149
|
+
def columns
|
|
150
|
+
@columns ||= []
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def rows
|
|
154
|
+
@rows ||= []
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def other_children
|
|
158
|
+
@other_children ||= []
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# ------------------------------
|
|
162
|
+
# Private utility methods
|
|
163
|
+
|
|
164
|
+
def protect!
|
|
165
|
+
set_attribute('protected', 'true')
|
|
166
|
+
add_child(LOExt::TableProtection.new(doc: doc))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def add_or_repeat_column(column_style, default_cell_style)
|
|
170
|
+
if (last_column = columns.last).nil? || !last_column.has_styles?(column_style, default_cell_style)
|
|
171
|
+
TableColumn.new(column_style, default_cell_style, table: self).tap { |c| columns << c }
|
|
172
|
+
else
|
|
173
|
+
last_column.tap(&:increment_repeats!)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# TODO: do we really need to use the LibreOffice default width/height?
|
|
178
|
+
|
|
179
|
+
def ensure_empty_columns!
|
|
180
|
+
empty_required = MIN_COLUMNS - column_count
|
|
181
|
+
add_empty_columns(empty_required, '0.889in') if empty_required > 0
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def ensure_empty_rows!
|
|
185
|
+
empty_required = MIN_ROWS - row_count
|
|
186
|
+
add_row('0.178in', empty_required) if empty_required > 0
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'berkeley_library/util/ods/xml/table/repeatable'
|
|
2
|
+
require 'berkeley_library/util/ods/xml/text/p'
|
|
3
|
+
|
|
4
|
+
module BerkeleyLibrary
|
|
5
|
+
module Util
|
|
6
|
+
module ODS
|
|
7
|
+
module XML
|
|
8
|
+
module Table
|
|
9
|
+
class TableCell < Repeatable
|
|
10
|
+
attr_reader :value
|
|
11
|
+
attr_reader :cell_style
|
|
12
|
+
|
|
13
|
+
def initialize(value = nil, cell_style = nil, number_repeated = 1, table:)
|
|
14
|
+
super('table-cell', 'number-columns-repeated', number_repeated, table: table)
|
|
15
|
+
|
|
16
|
+
@value = value
|
|
17
|
+
@cell_style = cell_style
|
|
18
|
+
|
|
19
|
+
set_default_attributes!
|
|
20
|
+
add_default_children!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
def repeat_empty(number_repeated, cell_style = nil, table:)
|
|
25
|
+
TableCell.new(nil, cell_style, number_repeated, table: table)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def set_default_attributes!
|
|
32
|
+
set_attribute('style-name', cell_style.style_name) if cell_style
|
|
33
|
+
set_attribute(:office, 'value-type', 'string') if value
|
|
34
|
+
set_attribute(:calcext, 'value-type', 'string') if value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def add_default_children!
|
|
38
|
+
children << XML::Text::P.new(value, doc: doc) if value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'berkeley_library/util/ods/xml/element_node'
|
|
2
|
+
require 'berkeley_library/util/ods/xml/table/repeatable'
|
|
3
|
+
|
|
4
|
+
module BerkeleyLibrary
|
|
5
|
+
module Util
|
|
6
|
+
module ODS
|
|
7
|
+
module XML
|
|
8
|
+
module Table
|
|
9
|
+
class TableColumn < Repeatable
|
|
10
|
+
|
|
11
|
+
attr_reader :column_style
|
|
12
|
+
attr_reader :default_cell_style
|
|
13
|
+
|
|
14
|
+
# Initializes a new column
|
|
15
|
+
#
|
|
16
|
+
# @param column_style [XML::Style::ColumnStyle] the column style
|
|
17
|
+
# @param default_cell_style [XML::Style::CellStyle] the default cell style for this column
|
|
18
|
+
def initialize(column_style, default_cell_style, number_repeated = 1, table:)
|
|
19
|
+
super('table-column', 'number-columns-repeated', number_repeated, table: table)
|
|
20
|
+
@column_style = column_style
|
|
21
|
+
@default_cell_style = default_cell_style
|
|
22
|
+
|
|
23
|
+
set_default_attributes!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# rubocop:disable Naming/PredicateName
|
|
27
|
+
def has_styles?(column_style, default_cell_style)
|
|
28
|
+
self.column_style == column_style && self.default_cell_style == default_cell_style
|
|
29
|
+
end
|
|
30
|
+
# rubocop:enable Naming/PredicateName
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def set_default_attributes!
|
|
35
|
+
set_attribute('style-name', column_style.style_name)
|
|
36
|
+
set_attribute('default-cell-style-name', default_cell_style.style_name)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require 'berkeley_library/util/arrays'
|
|
2
|
+
require 'berkeley_library/util/ods/xml/element_node'
|
|
3
|
+
require 'berkeley_library/util/ods/xml/table/repeatable'
|
|
4
|
+
require 'berkeley_library/util/ods/xml/table/table_cell'
|
|
5
|
+
|
|
6
|
+
module BerkeleyLibrary
|
|
7
|
+
module Util
|
|
8
|
+
module ODS
|
|
9
|
+
module XML
|
|
10
|
+
module Table
|
|
11
|
+
class TableRow < Repeatable
|
|
12
|
+
|
|
13
|
+
# ------------------------------------------------------------
|
|
14
|
+
# Accessors
|
|
15
|
+
|
|
16
|
+
attr_reader :row_style
|
|
17
|
+
attr_reader :default_cell_style
|
|
18
|
+
|
|
19
|
+
# ------------------------------------------------------------
|
|
20
|
+
# Initializer
|
|
21
|
+
|
|
22
|
+
# @param table [Table] the table
|
|
23
|
+
# @param default_cell_style [Style::CellStyle] the default cell style
|
|
24
|
+
def initialize(row_style, number_repeated = 1, table:, default_cell_style: nil)
|
|
25
|
+
super('table-row', 'number-rows-repeated', number_repeated, table: table)
|
|
26
|
+
@row_style = row_style
|
|
27
|
+
@default_cell_style = default_cell_style
|
|
28
|
+
|
|
29
|
+
set_default_attributes!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# ------------------------------------------------------------
|
|
33
|
+
# Public utility methods
|
|
34
|
+
|
|
35
|
+
def set_value_at(column_index, value = nil, cell_style = nil)
|
|
36
|
+
explicit_cells[column_index] = TableCell.new(value, cell_style || default_cell_style, table: table)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def get_value_at(column_index)
|
|
40
|
+
(cell = explicit_cells[column_index]) && cell.value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# ------------------------------------------------------------
|
|
44
|
+
# Public XML::ElementNode overrides
|
|
45
|
+
|
|
46
|
+
def add_child(child)
|
|
47
|
+
return add_table_cell(child) if child.is_a?(TableCell)
|
|
48
|
+
|
|
49
|
+
child.tap { |c| other_children << c }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# ------------------------------------------------------------
|
|
53
|
+
# Protected methods
|
|
54
|
+
|
|
55
|
+
protected
|
|
56
|
+
|
|
57
|
+
# ----------------------------------------
|
|
58
|
+
# Protected utility methods
|
|
59
|
+
|
|
60
|
+
def add_table_cell(cell)
|
|
61
|
+
return cell.tap { |c| explicit_cells << c } if explicit_cell_count < table.column_count
|
|
62
|
+
|
|
63
|
+
raise ArgumentError, "Can't add cell at column index #{explicit_cell_count} to table with only #{table.column_count} columns"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# ----------------------------------------
|
|
67
|
+
# Protected XML::ElementNode overrides
|
|
68
|
+
|
|
69
|
+
def children
|
|
70
|
+
[].tap do |cc|
|
|
71
|
+
each_cell { |c| cc << c }
|
|
72
|
+
cc.concat(other_children)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# ------------------------------------------------------------
|
|
77
|
+
# Private methods
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def column_count_actual
|
|
82
|
+
[table.column_count, Table::MIN_COLUMNS].max
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def explicit_cells
|
|
86
|
+
@explicit_cells ||= []
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def set_default_attributes!
|
|
90
|
+
set_attribute('style-name', row_style.style_name)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def other_children
|
|
94
|
+
@other_children ||= []
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def explicit_cell_count
|
|
98
|
+
explicit_cells.size
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def each_cell(columns_yielded = 0, remaining = explicit_cells, &block)
|
|
102
|
+
columns_yielded, remaining = yield_while_non_nil(columns_yielded, remaining, &block)
|
|
103
|
+
columns_yielded, remaining = yield_while_nil(columns_yielded, remaining, &block)
|
|
104
|
+
each_cell(columns_yielded, remaining, &block) unless remaining.empty?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def yield_while_non_nil(columns_yielded, remaining, &block)
|
|
108
|
+
non_nil_cells = remaining.take_while { |c| !c.nil? }
|
|
109
|
+
non_nil_cells.each(&block)
|
|
110
|
+
non_nil_cell_count = non_nil_cells.size
|
|
111
|
+
|
|
112
|
+
[columns_yielded + non_nil_cell_count, remaining[non_nil_cell_count..]]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def yield_while_nil(columns_yielded, remaining, &block)
|
|
116
|
+
nil_cell_count = Arrays.count_while(values: remaining, &:nil?)
|
|
117
|
+
remaining = remaining[nil_cell_count..]
|
|
118
|
+
|
|
119
|
+
empty_required = remaining.empty? ? (column_count_actual - columns_yielded) : nil_cell_count
|
|
120
|
+
yield_repeat_empty(empty_required, &block)
|
|
121
|
+
columns_yielded += empty_required
|
|
122
|
+
|
|
123
|
+
[columns_yielded, remaining]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def yield_repeat_empty(num_repeats, &block)
|
|
127
|
+
empty_cell = TableCell.repeat_empty(num_repeats, default_cell_style, table: table)
|
|
128
|
+
block.call(empty_cell)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'berkeley_library/util/ods/xml/element_node'
|
|
2
|
+
|
|
3
|
+
module BerkeleyLibrary
|
|
4
|
+
module Util
|
|
5
|
+
module ODS
|
|
6
|
+
module XML
|
|
7
|
+
module Text
|
|
8
|
+
class P < XML::ElementNode
|
|
9
|
+
|
|
10
|
+
# ------------------------------------------------------------
|
|
11
|
+
# Constant
|
|
12
|
+
|
|
13
|
+
ESCAPABLE = ["\t", "\n", ' '].freeze
|
|
14
|
+
|
|
15
|
+
# ------------------------------------------------------------
|
|
16
|
+
# Accessors
|
|
17
|
+
|
|
18
|
+
attr_reader :text
|
|
19
|
+
|
|
20
|
+
# ------------------------------------------------------------
|
|
21
|
+
# Initializer
|
|
22
|
+
|
|
23
|
+
def initialize(text, doc:)
|
|
24
|
+
super(:text, 'p', doc: doc)
|
|
25
|
+
|
|
26
|
+
@text = text
|
|
27
|
+
add_default_children!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# ------------------------------------------------------------
|
|
31
|
+
# Private methods
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def add_default_children!
|
|
36
|
+
each_child_element_or_string { |c| children << c }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def each_child_element_or_string(last_char = nil, text_remaining = text, &block)
|
|
40
|
+
last_char, text_remaining = yield_while_escaped(last_char, text_remaining, &block)
|
|
41
|
+
last_char, text_remaining = yield_while_unescaped(last_char, text_remaining, &block)
|
|
42
|
+
each_child_element_or_string(last_char, text_remaining, &block) unless text_remaining.empty?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def yield_while_unescaped(last_char, text_remaining, &block)
|
|
46
|
+
unescaped, last_char = take_while_unescaped(last_char, text_remaining)
|
|
47
|
+
unless unescaped.empty?
|
|
48
|
+
block.call(unescaped)
|
|
49
|
+
text_remaining = text_remaining[unescaped.size..]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
[last_char, text_remaining]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def yield_while_escaped(last_char, text_remaining, &block)
|
|
56
|
+
escaped_char_count = 0
|
|
57
|
+
text_remaining.each_char do |c|
|
|
58
|
+
break unless escape?(c, last_char)
|
|
59
|
+
|
|
60
|
+
# TODO: collapse contiguous spaces with attribute 'text:c'
|
|
61
|
+
# https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#attribute-text_c
|
|
62
|
+
block.call(escape_element_for(c))
|
|
63
|
+
escaped_char_count += 1
|
|
64
|
+
last_char = c
|
|
65
|
+
end
|
|
66
|
+
text_remaining = text_remaining[escaped_char_count..] unless escaped_char_count == 0
|
|
67
|
+
|
|
68
|
+
[last_char, text_remaining]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def take_while_unescaped(last_char, text_remaining)
|
|
72
|
+
unescaped = text_remaining.each_char.with_object('') do |c, result|
|
|
73
|
+
break result if escape?(c, last_char)
|
|
74
|
+
|
|
75
|
+
result << c
|
|
76
|
+
last_char = c
|
|
77
|
+
end
|
|
78
|
+
[unescaped, last_char]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def escape?(c, last_char)
|
|
82
|
+
ESCAPABLE.include?(c) && (last_char == ' ' || c != ' ')
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def escape_element_for(c)
|
|
86
|
+
raise ArgumentError, "Not an escapable character: #{c}" unless ESCAPABLE.include?(c)
|
|
87
|
+
|
|
88
|
+
return S.new(doc: doc) if c == ' '
|
|
89
|
+
return Tab.new(doc: doc) if c == "\t"
|
|
90
|
+
return LineBreak.new(doc: doc) if c == "\n"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# ------------------------------------------------------------
|
|
95
|
+
# Helper classes
|
|
96
|
+
|
|
97
|
+
class S < XML::ElementNode
|
|
98
|
+
def initialize(doc:)
|
|
99
|
+
super(:text, 's', doc: doc)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class Tab < XML::ElementNode
|
|
104
|
+
def initialize(doc:)
|
|
105
|
+
super(:text, 'tab', doc: doc)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class LineBreak < XML::ElementNode
|
|
110
|
+
def initialize(doc:)
|
|
111
|
+
super(:text, 'line-break', doc: doc)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|