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.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +388 -0
  4. data/.idea/inspectionProfiles/Project_Default.xml +20 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/tind.iml +138 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.rubocop.yml +334 -0
  10. data/.ruby-version +1 -0
  11. data/.simplecov +8 -0
  12. data/.yardopts +1 -0
  13. data/CHANGES.md +58 -0
  14. data/Dockerfile +57 -0
  15. data/Gemfile +3 -0
  16. data/Jenkinsfile +18 -0
  17. data/LICENSE.md +21 -0
  18. data/README.md +73 -0
  19. data/Rakefile +20 -0
  20. data/berkeley_library-tind.gemspec +50 -0
  21. data/bin/tind-export +14 -0
  22. data/docker-compose.yml +15 -0
  23. data/lib/berkeley_library/tind.rb +3 -0
  24. data/lib/berkeley_library/tind/api.rb +1 -0
  25. data/lib/berkeley_library/tind/api/api.rb +132 -0
  26. data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
  27. data/lib/berkeley_library/tind/api/collection.rb +82 -0
  28. data/lib/berkeley_library/tind/api/date_range.rb +67 -0
  29. data/lib/berkeley_library/tind/api/format.rb +32 -0
  30. data/lib/berkeley_library/tind/api/search.rb +100 -0
  31. data/lib/berkeley_library/tind/config.rb +103 -0
  32. data/lib/berkeley_library/tind/export.rb +1 -0
  33. data/lib/berkeley_library/tind/export/column.rb +54 -0
  34. data/lib/berkeley_library/tind/export/column_group.rb +144 -0
  35. data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
  36. data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
  37. data/lib/berkeley_library/tind/export/config.rb +154 -0
  38. data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
  39. data/lib/berkeley_library/tind/export/export.rb +47 -0
  40. data/lib/berkeley_library/tind/export/export_command.rb +168 -0
  41. data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
  42. data/lib/berkeley_library/tind/export/export_format.rb +67 -0
  43. data/lib/berkeley_library/tind/export/exporter.rb +105 -0
  44. data/lib/berkeley_library/tind/export/filter.rb +52 -0
  45. data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
  46. data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
  47. data/lib/berkeley_library/tind/export/row.rb +24 -0
  48. data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
  49. data/lib/berkeley_library/tind/export/table.rb +175 -0
  50. data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
  51. data/lib/berkeley_library/tind/marc.rb +1 -0
  52. data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
  53. data/lib/berkeley_library/tind/module_info.rb +14 -0
  54. data/lib/berkeley_library/util/arrays.rb +178 -0
  55. data/lib/berkeley_library/util/logging.rb +1 -0
  56. data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
  57. data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
  58. data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
  59. data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
  60. data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
  61. data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
  62. data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
  63. data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
  64. data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
  65. data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
  66. data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
  67. data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
  68. data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
  69. data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
  70. data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
  71. data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
  72. data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
  73. data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
  74. data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
  75. data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
  76. data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
  77. data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
  78. data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
  79. data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
  80. data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
  81. data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
  82. data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
  83. data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
  84. data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
  85. data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
  86. data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
  87. data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
  88. data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
  89. data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
  90. data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
  91. data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
  92. data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
  93. data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
  94. data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
  95. data/lib/berkeley_library/util/paths.rb +111 -0
  96. data/lib/berkeley_library/util/stringios.rb +30 -0
  97. data/lib/berkeley_library/util/strings.rb +42 -0
  98. data/lib/berkeley_library/util/sys_exits.rb +15 -0
  99. data/lib/berkeley_library/util/times.rb +22 -0
  100. data/lib/berkeley_library/util/uris.rb +44 -0
  101. data/lib/berkeley_library/util/uris/appender.rb +162 -0
  102. data/lib/berkeley_library/util/uris/requester.rb +62 -0
  103. data/lib/berkeley_library/util/uris/validator.rb +32 -0
  104. data/rakelib/bundle.rake +8 -0
  105. data/rakelib/coverage.rake +11 -0
  106. data/rakelib/gem.rake +54 -0
  107. data/rakelib/rubocop.rake +18 -0
  108. data/rakelib/spec.rake +2 -0
  109. data/spec/.rubocop.yml +40 -0
  110. data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
  111. data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
  112. data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
  113. data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
  114. data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
  115. data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
  116. data/spec/berkeley_library/tind/config_spec.rb +86 -0
  117. data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
  118. data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
  119. data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
  120. data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
  121. data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
  122. data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
  123. data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
  124. data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
  125. data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
  126. data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
  127. data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
  128. data/spec/berkeley_library/util/arrays_spec.rb +340 -0
  129. data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
  130. data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
  131. data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
  132. data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
  133. data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
  134. data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
  135. data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
  136. data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
  137. data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
  138. data/spec/berkeley_library/util/paths_spec.rb +90 -0
  139. data/spec/berkeley_library/util/stringios_spec.rb +34 -0
  140. data/spec/berkeley_library/util/strings_spec.rb +27 -0
  141. data/spec/berkeley_library/util/times_spec.rb +39 -0
  142. data/spec/berkeley_library/util/uris_spec.rb +118 -0
  143. data/spec/data/collection-names.txt +438 -0
  144. data/spec/data/collections.json +4827 -0
  145. data/spec/data/disjoint-records.xml +187 -0
  146. data/spec/data/record-184453.xml +58 -0
  147. data/spec/data/record-184458.xml +63 -0
  148. data/spec/data/record-187888.xml +78 -0
  149. data/spec/data/records-api-search-cjk-p1.xml +6381 -0
  150. data/spec/data/records-api-search-cjk-p2.xml +5 -0
  151. data/spec/data/records-api-search-p1.xml +4506 -0
  152. data/spec/data/records-api-search-p2.xml +4509 -0
  153. data/spec/data/records-api-search-p3.xml +4506 -0
  154. data/spec/data/records-api-search-p4.xml +4509 -0
  155. data/spec/data/records-api-search-p5.xml +4506 -0
  156. data/spec/data/records-api-search-p6.xml +2436 -0
  157. data/spec/data/records-api-search-p7.xml +5 -0
  158. data/spec/data/records-api-search.xml +234 -0
  159. data/spec/data/records-manual-search.xml +547 -0
  160. data/spec/spec_helper.rb +30 -0
  161. data/test/profile/table_from_records_profile.rb +46 -0
  162. metadata +585 -0
@@ -0,0 +1,26 @@
1
+ require 'nokogiri'
2
+ require 'berkeley_library/util/ods/xml/document_node'
3
+ require 'berkeley_library/util/ods/xml/office/document_content'
4
+
5
+ module BerkeleyLibrary
6
+ module Util
7
+ module ODS
8
+ module XML
9
+ class ContentDoc < DocumentNode
10
+
11
+ def initialize
12
+ super('content.xml')
13
+ end
14
+
15
+ def root_element_node
16
+ document_content
17
+ end
18
+
19
+ def document_content
20
+ @document_content ||= Office::DocumentContent.new(doc: doc)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ require 'nokogiri'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ class DocumentNode
8
+
9
+ ENCODING = 'UTF-8'.freeze
10
+
11
+ attr_reader :path
12
+
13
+ # Initializes a new DocumentNode
14
+ # @param path [String] the path to this document in the container
15
+ def initialize(path)
16
+ @path = path
17
+ end
18
+
19
+ def to_xml(out = nil, compact: true)
20
+ return write_xml_to_string(compact: compact) unless out
21
+ return write_xml_to_stream(out, compact: compact) if out.respond_to?(:write)
22
+
23
+ write_xml_to_file(out, compact: compact)
24
+ end
25
+
26
+ def doc
27
+ @doc ||= Nokogiri::XML::Document.new.tap do |doc|
28
+ doc.encoding = ENCODING
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def write_xml_to_stream(out, compact:)
35
+ doc.root ||= root_element_node.element
36
+ if compact
37
+ doc.write_to(out, encoding: ENCODING, save_with: 0)
38
+ else
39
+ doc.write_to(out, encoding: ENCODING)
40
+ end
41
+ end
42
+
43
+ def write_xml_to_string(compact:)
44
+ StringIO.new.tap do |out|
45
+ out.binmode
46
+ write_xml_to_stream(out, compact: compact)
47
+ end.string
48
+ end
49
+
50
+ def write_xml_to_file(path, compact:)
51
+ File.open(path, 'wb') { |f| write_xml_to_stream(f, compact: compact) }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,106 @@
1
+ require 'nokogiri'
2
+ require 'berkeley_library/util/logging'
3
+ require 'berkeley_library/util/ods/xml/namespace'
4
+
5
+ module BerkeleyLibrary
6
+ module Util
7
+ module ODS
8
+ module XML
9
+ class ElementNode
10
+ include BerkeleyLibrary::Logging
11
+
12
+ # @return [Nokogiri::XML::Document] the document containing this element
13
+ attr_reader :doc
14
+
15
+ # @return [Namespace] the namespace for this element
16
+ attr_reader :namespace
17
+
18
+ # @return [String] the name of this element
19
+ attr_reader :element_name
20
+
21
+ # @param namespace [String, Symbol, Namespace] the element namespace
22
+ # @param element_name [String] the element name
23
+ # @param doc [Nokogiri::XML::Document] the document containing this element
24
+ def initialize(namespace, element_name, doc:)
25
+ @namespace = ensure_namespace(namespace)
26
+ @element_name = element_name
27
+ @doc = doc
28
+ end
29
+
30
+ def prefix
31
+ namespace.prefix
32
+ end
33
+
34
+ def element
35
+ ensure_element!
36
+ end
37
+
38
+ # Finalize this XML element and prepare for output.
39
+ def ensure_element!
40
+ @element ||= create_element
41
+ end
42
+
43
+ # rubocop:disable Style/OptionalArguments
44
+ def set_attribute(namespace = prefix, name, value)
45
+ attr_name = prefixed_attr_name(namespace, name)
46
+ attributes[attr_name] = value.to_s
47
+ end
48
+ # rubocop:enable Style/OptionalArguments
49
+
50
+ # rubocop:disable Style/OptionalArguments
51
+ def clear_attribute(namespace = prefix, name)
52
+ attr_name = prefixed_attr_name(namespace, name)
53
+ attributes.delete(attr_name)
54
+ end
55
+ # rubocop:enable Style/OptionalArguments
56
+
57
+ def add_child(child)
58
+ raise ArgumentError, "Not text or an element: #{child.inspect}" unless child.is_a?(ElementNode) || child.is_a?(String)
59
+
60
+ child.tap { |c| children << c }
61
+ end
62
+
63
+ protected
64
+
65
+ def prefixed_attr_name(ns, name)
66
+ return "xmlns:#{name}" if ns.to_s == 'xmlns'
67
+
68
+ "#{ensure_namespace(ns).prefix}:#{name}"
69
+ end
70
+
71
+ def create_element
72
+ doc.create_element("#{prefix}:#{element_name}", attributes).tap do |element|
73
+ children.each do |child|
74
+ next element.add_child(child.element) if child.is_a?(ElementNode)
75
+
76
+ text_node = doc.create_text_node(child.to_s)
77
+ element.add_child(text_node)
78
+ end
79
+ end
80
+ end
81
+
82
+ # @return [Hash<String, String>] the attributes, as a map from name to value
83
+ def attributes
84
+ # noinspection RubyYardReturnMatch
85
+ @attributes ||= {}
86
+ end
87
+
88
+ # @return [Array<ElementNode>] the child elements
89
+ # TODO: replace this with :each_child and a protected default array
90
+ def children
91
+ @children ||= []
92
+ end
93
+
94
+ private
95
+
96
+ def ensure_namespace(ns)
97
+ return ns if ns.is_a?(Namespace)
98
+ raise ArgumentError, "Not a recognized namespace: #{ns.inspect}" unless (ns_for_prefix = Namespace.for_prefix(ns.to_s.downcase))
99
+
100
+ ns_for_prefix
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,26 @@
1
+ require 'berkeley_library/util/ods/xml/element_node'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module LOExt
8
+ class TableProtection < ElementNode
9
+ def initialize(doc:)
10
+ super(:loext, 'table-protection', doc: doc)
11
+
12
+ set_default_attributes!
13
+ end
14
+
15
+ private
16
+
17
+ def set_default_attributes!
18
+ set_attribute('select-protected-cells', 'true')
19
+ set_attribute('select-unprotected-cells', 'true')
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ require 'berkeley_library/util/ods/xml/element_node'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Manifest
8
+ class FileEntry < XML::ElementNode
9
+
10
+ attr_reader :full_path
11
+ attr_reader :media_type
12
+ attr_reader :manifest
13
+
14
+ def initialize(full_path, media_type = nil, manifest:)
15
+ super(:manifest, 'file-entry', doc: manifest.doc)
16
+
17
+ @full_path = full_path
18
+ @media_type = media_type || media_type_for(full_path)
19
+ @manifest = manifest
20
+
21
+ set_default_attributes!
22
+ end
23
+
24
+ private
25
+
26
+ def media_type_for(path)
27
+ return 'application/vnd.oasis.opendocument.spreadsheet' if path == '/'
28
+ return 'text/xml' if path.end_with?('.xml')
29
+
30
+ raise ArgumentError, "Can't determine media type for path: #{path.inspect}"
31
+ end
32
+
33
+ def set_default_attributes!
34
+ set_attribute('full-path', full_path)
35
+ set_attribute('media-type', media_type)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,73 @@
1
+ require 'berkeley_library/util/ods/xml/element_node'
2
+ require 'berkeley_library/util/ods/xml/manifest/file_entry'
3
+
4
+ module BerkeleyLibrary
5
+ module Util
6
+ module ODS
7
+ module XML
8
+ module Manifest
9
+ class Manifest < XML::ElementNode
10
+ REQUIRED_NAMESPACES = [:manifest].freeze
11
+ MANIFEST_VERSION = '1.2'.freeze
12
+
13
+ def initialize(doc:)
14
+ super(:manifest, 'manifest', doc: doc)
15
+
16
+ set_default_attributes!
17
+ add_file_entry(self_file_entry)
18
+ end
19
+
20
+ def version
21
+ MANIFEST_VERSION
22
+ end
23
+
24
+ # Adds a document to the manifest
25
+ # @param doc [XML::DocumentNode] the document to add
26
+ def add_entry_for(doc)
27
+ FileEntry.new(doc.path, manifest: self).tap { |e| add_file_entry(e) }
28
+ end
29
+
30
+ def add_child(child)
31
+ return add_file_entry(child) if child.is_a?(FileEntry)
32
+
33
+ child.tap { |c| other_children << c }
34
+ end
35
+
36
+ def children
37
+ [file_entries, other_children].flatten
38
+ end
39
+
40
+ private
41
+
42
+ def self_file_entry
43
+ FileEntry.new('/', manifest: self).tap do |entry|
44
+ entry.set_attribute('version', version)
45
+ end
46
+ end
47
+
48
+ def file_entries
49
+ @file_entries ||= []
50
+ end
51
+
52
+ def other_children
53
+ @other_children ||= []
54
+ end
55
+
56
+ def required_namespaces
57
+ @required_namespaces ||= REQUIRED_NAMESPACES.map { |p| Namespace.for_prefix(p) }
58
+ end
59
+
60
+ def add_file_entry(file_entry)
61
+ file_entry.tap { |fe| file_entries << fe }
62
+ end
63
+
64
+ def set_default_attributes!
65
+ required_namespaces.each { |ns| set_attribute(:xmlns, ns.prefix, ns.uri) }
66
+ set_attribute('version', version)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ require 'nokogiri'
2
+ require 'berkeley_library/util/ods/xml/document_node'
3
+ require 'berkeley_library/util/ods/xml/manifest/manifest'
4
+
5
+ module BerkeleyLibrary
6
+ module Util
7
+ module ODS
8
+ module XML
9
+ class ManifestDoc < DocumentNode
10
+
11
+ def initialize
12
+ super('META-INF/manifest.xml')
13
+ end
14
+
15
+ def root_element_node
16
+ manifest
17
+ end
18
+
19
+ def manifest
20
+ @manifest ||= Manifest::Manifest.new(doc: doc)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+ require 'typesafe_enum'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+
8
+ class Namespace < TypesafeEnum::Base
9
+ new :CALCEXT, 'urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0'
10
+ new :CHART, 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'
11
+ new :DR3D, 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'
12
+ new :FIELD, 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0'
13
+ new :FO, 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'
14
+ new :FORM, 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'
15
+ new :LOEXT, 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0'
16
+ new :MANIFEST, 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'
17
+ new :META, 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'
18
+ new :OF, 'urn:oasis:names:tc:opendocument:xmlns:of:1.2'
19
+ new :OFFICE, 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
20
+ new :PRESENTATION, 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0'
21
+ new :SCRIPT, 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'
22
+ new :STYLE, 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'
23
+ new :SVG, 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'
24
+ new :TABLE, 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'
25
+ new :TEXT, 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
26
+
27
+ def prefix
28
+ key.to_s.downcase
29
+ end
30
+
31
+ def uri
32
+ value
33
+ end
34
+
35
+ class << self
36
+ def for_prefix(prefix)
37
+ @by_prefix ||= Namespace.map { |ns| [ns.prefix, ns] }.to_h
38
+ @by_prefix[prefix.to_s]
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,181 @@
1
+ require 'berkeley_library/util/ods/xml/element_node'
2
+ require 'berkeley_library/util/ods/xml/style/cell_style'
3
+ require 'berkeley_library/util/ods/xml/style/column_style'
4
+ require 'berkeley_library/util/ods/xml/style/row_style'
5
+ require 'berkeley_library/util/ods/xml/style/style'
6
+ require 'berkeley_library/util/ods/xml/style/table_style'
7
+
8
+ module BerkeleyLibrary
9
+ module Util
10
+ module ODS
11
+ module XML
12
+ module Office
13
+ class AutomaticStyles < XML::ElementNode
14
+ # ------------------------------------------------------------
15
+ # Initializer
16
+
17
+ def initialize(doc:)
18
+ super(:office, 'automatic-styles', doc: doc)
19
+ end
20
+
21
+ # ------------------------------------------------------------
22
+ # Accessors
23
+
24
+ def default_style(family)
25
+ first_style = styles_for_family(family).first
26
+ return first_style if first_style
27
+
28
+ add_default_style(family)
29
+ end
30
+
31
+ # ------------------------------------------------------------
32
+ # Utility methods
33
+
34
+ # TODO: create a Stylesheet class and move most of this to it
35
+
36
+ # rubocop:disable Style/OptionalBooleanParameter
37
+ def add_cell_style(name = nil, protected = false, color = nil, font_weight: nil, wrap: false)
38
+ name ||= next_name_for(:table_cell)
39
+ add_style(Style::CellStyle.new(name, protected, color, font_weight: font_weight, wrap: wrap, styles: self))
40
+ end
41
+ # rubocop:enable Style/OptionalBooleanParameter
42
+
43
+ def add_column_style(name = nil, width = nil)
44
+ name ||= next_name_for(:table_column)
45
+ add_style(Style::ColumnStyle.new(name, width, styles: self))
46
+ end
47
+
48
+ def add_row_style(name = nil, height = nil)
49
+ name ||= next_name_for(:table_row)
50
+ add_style(Style::RowStyle.new(name, height, styles: self))
51
+ end
52
+
53
+ def add_table_style(name = nil)
54
+ name ||= next_name_for(:table)
55
+ add_style(Style::TableStyle.new(name, styles: self))
56
+ end
57
+
58
+ def add_style(style)
59
+ raise ArgumentError, "Not a style: #{style.inspect}" unless style.is_a?(Style::Style)
60
+
61
+ style.tap { |s| add_or_insert_style(s) }
62
+ end
63
+
64
+ # rubocop:disable Style/OptionalBooleanParameter
65
+ def find_cell_style(protected = false, color: nil, font_weight: nil, wrap: false)
66
+ styles_for_family(:table_cell).find { |s| [s.protected?, s.color, s.font_weight, s.wrap?] == [protected, color, font_weight, wrap] }
67
+ end
68
+ # rubocop:enable Style/OptionalBooleanParameter
69
+
70
+ def find_column_style(width = nil)
71
+ w = width || Style::ColumnStyle::DEFAULT_WIDTH
72
+ styles_for_family(:table_column).find { |s| s.width == w }
73
+ end
74
+
75
+ def find_row_style(height = nil)
76
+ h = height || Style::RowStyle::DEFAULT_HEIGHT
77
+ styles_for_family(:table_row).find { |s| s.height == h }
78
+ end
79
+
80
+ # rubocop:disable Style/OptionalBooleanParameter
81
+ def find_or_create_cell_style(protected = false, color: nil, font_weight: nil, wrap: false)
82
+ existing_style = find_cell_style(protected, color: color, font_weight: font_weight, wrap: wrap)
83
+ return existing_style if existing_style
84
+
85
+ add_cell_style(nil, protected, color, font_weight: font_weight, wrap: wrap)
86
+ end
87
+ # rubocop:enable Style/OptionalBooleanParameter
88
+
89
+ def find_or_create_column_style(width = nil)
90
+ existing_style = find_column_style(width)
91
+ return existing_style if existing_style
92
+
93
+ add_column_style(nil, width)
94
+ end
95
+
96
+ def find_or_create_row_style(height = nil)
97
+ existing_style = find_row_style(height)
98
+ return existing_style if existing_style
99
+
100
+ add_row_style(nil, height)
101
+ end
102
+
103
+ # ------------------------------------------------------------
104
+ # Public XML::ElementNode overrides
105
+
106
+ def add_child(child)
107
+ child.is_a?(Style::Style) ? add_style(child) : child.tap { |c| other_children << c }
108
+ end
109
+
110
+ # ------------------------------------------------------------
111
+ # Protected methods
112
+
113
+ protected
114
+
115
+ # ----------------------------------------
116
+ # Protected XML::ElementNode overrides
117
+
118
+ def children
119
+ [other_children, Style::Family.map { |f| styles_for_family(f) }].flatten
120
+ end
121
+
122
+ def create_element
123
+ Style::Family.each { |f| default_style(f) }
124
+
125
+ super
126
+ end
127
+
128
+ # ------------------------------------------------------------
129
+ # Private methods
130
+
131
+ private
132
+
133
+ def add_or_insert_style(s)
134
+ styles = styles_for_family(s.family)
135
+ insert_index = styles.find_index do |s1|
136
+ raise ArgumentError, "A #{s.family} style named #{s.style_name} already exists" if s1.style_name == s.style_name
137
+
138
+ s1 > s
139
+ end
140
+ insert_index ? styles.insert(insert_index, s) : styles << s
141
+ end
142
+
143
+ def add_default_style(family)
144
+ f = Style::Family.ensure_family(family)
145
+ return add_cell_style if f == Style::Family::TABLE_CELL
146
+ return add_column_style if f == Style::Family::TABLE_COLUMN
147
+ return add_row_style if f == Style::Family::TABLE_ROW
148
+ return add_table_style if f == Style::Family::TABLE
149
+ end
150
+
151
+ def next_name_for(family)
152
+ f = Style::Family.ensure_family(family)
153
+
154
+ max_suffix = styles_for_family(f).inject(0) do |max, s|
155
+ next max unless (n = s.style_name).start_with?(f.prefix)
156
+ next max unless (suffix = n[f.prefix.size..]) =~ /^[0-9]+$/
157
+
158
+ [max, suffix.to_i].max
159
+ end
160
+
161
+ "#{f.prefix}#{max_suffix + 1}"
162
+ end
163
+
164
+ def styles_for_family(family)
165
+ (styles_by_family[Style::Family.ensure_family(family)] ||= [])
166
+ end
167
+
168
+ def styles_by_family
169
+ @styles_by_family ||= {}
170
+ end
171
+
172
+ def other_children
173
+ @other_children ||= []
174
+ end
175
+
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end