berkeley_library-tind 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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