saxon-rb 0.6.0-java → 0.7.0-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e9afbc5a2644034bc285b3c3cd4e78f852b715d7a096ff8f7a6386730501598
4
- data.tar.gz: fe9c135af6356690fcca7b4f1dc2d9656cce44018806c3b5f6c9f4b6407c576d
3
+ metadata.gz: 11d57a8924dff1a6f260b5496fcbc0c693d3bc4818b782acf0329950952ad1c6
4
+ data.tar.gz: 67353b0bd7ffc1a2d350019ee20c41713d69e1f5a5bbfa4c42a0d420456401c1
5
5
  SHA512:
6
- metadata.gz: c59e0f986b8bbf4de77b18feba99a61c8e724df537679b0e674bc142a0d2b476026bb01579dcd2ec6e9b4f246493db3b892fbe7e997848eb06bf5c7bd8e2f5ea
7
- data.tar.gz: 182d383aced898a376d37eaedb60ff80461910d3451e119f2bf6e97ebe37d8c64e600563ba968efec22f49ae02f025ec3725f00a2cb9af10813af8e20fc13100
6
+ metadata.gz: 26f99187a484b68f55bb9fd7e0e49cbd4dd87bf2d1b366ed178140c24965a9ef9d61185e26f904b5f4aaea2cf9722bd3a842995e32fda036c9b17e38c55d5cac
7
+ data.tar.gz: 837e3e4f0e4bbdd70f88ed21c595ea4b5416a615362ad663f84a4b23956349bbce813528b20e783f5b2664560337be30b964bceb756e1c5554656939dd71ecbf
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- jruby-9.2.6.0
1
+ jruby-9.2.9.0
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -e docs/templates/plugin.rb
data/README.md CHANGED
@@ -78,7 +78,7 @@ result_2 = transformer.call_template('main-template')
78
78
  processor = Saxon::Processor.create
79
79
  xpath = processor.xpath_compiler.compile('//element[@attr = $a:var]')
80
80
 
81
- matches = xpath.run(document_node)
81
+ matches = xpath.evaluate(document_node)
82
82
  ```
83
83
 
84
84
  ## Migrating from `saxon-xslt` (or Nokogiri)
@@ -0,0 +1,73 @@
1
+ include YARD
2
+ include Templates
3
+
4
+ module JavadocHtmlHelper
5
+ JAVA_TYPE_MATCHER = /\A(?:[a-z_$](?:[a-z0-9_$]*)\.)+[A-Z][A-Za-z_$]*/
6
+ RUBY_COLLECTION_TYPE_MATCHER = /\A(?:[A-Z][A-Za-z0-9_])(?:::[A-Z][A-Za-z0-9_]*)*</
7
+ SAXON_TYPE_MATCHER = /\A(?:net\.sf\.saxon|com\.saxonica)/
8
+
9
+ def format_types(typelist, brackets = true)
10
+ return unless typelist.is_a?(Array)
11
+ list = typelist.map { |type|
12
+ case type
13
+ when JAVA_TYPE_MATCHER
14
+ format_java_type(type)
15
+ else
16
+ super([type], false)
17
+ end
18
+ }
19
+ list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", "))
20
+ end
21
+
22
+ def format_java_type(type)
23
+ "<tt>" + linkify_saxon_type(type) + "</tt>"
24
+ end
25
+
26
+ def linkify_saxon_type(type)
27
+ case type
28
+ when SAXON_TYPE_MATCHER
29
+ link = url_for_java_object(type)
30
+ else
31
+ link = nil
32
+ end
33
+ link ? link_url(link, type, :title => h(type)) : type
34
+ end
35
+
36
+ def linkify(*args)
37
+ if args.first.is_a?(String)
38
+ case args.first
39
+ when JAVA_TYPE_MATCHER
40
+ link = url_for_java_object(args.first)
41
+ title = args.first
42
+ link ? link_url(link, title, :title => h(title)) : title
43
+ else
44
+ super
45
+ end
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def url_for(obj, anchor = nil, relative = true)
52
+ case obj
53
+ when JAVA_TYPE_MATCHER
54
+ url_for_java_object(obj, anchor, relative)
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ def url_for_java_object(obj, anchor = nil, relative = nil)
61
+ case obj
62
+ when SAXON_TYPE_MATCHER
63
+ package, _, klass = obj.rpartition(".")
64
+ "http://saxonica.com/documentation/index.html#!javadoc/#{package}/#{klass}"
65
+ else
66
+ path = obj.split(".").join("/")
67
+ "https://docs.oracle.com/javase/8/docs/api/index.html?#{path}.html"
68
+ end
69
+ end
70
+ end
71
+
72
+ Template.extra_includes << proc { |opts| JavadocHtmlHelper if opts.format == :html }
73
+ # Engine.register_template_path(File.dirname(__FILE__))
@@ -2,12 +2,13 @@ require 'saxon/s9api'
2
2
  require 'saxon/parse_options'
3
3
 
4
4
  module Saxon
5
- # Wraps the <tt>net.saxon.Configuration</tt> class. See
6
- # http://saxonica.com/documentation9.5/javadoc/net/sf/saxon/Configuration.html
7
- # for details of what configuration options are available and what values
8
- # they accept. See
9
- # http://saxonica.com/documentation9.5/javadoc/net/sf/saxon/lib/FeatureKeys.html
10
- # for details of the constant names used to access the values
5
+ # Wraps the <tt>net.sf.saxon.Configuration</tt> class.
6
+ #
7
+ # See {net.sf.saxon.Configuration} for details of what configuration options
8
+ # are available and what values they accept.
9
+ #
10
+ # See {net.sf.saxon.lib.FeatureKeys} for details of the constant names used to
11
+ # access the values
11
12
  class Configuration
12
13
  DEFAULT_SEMAPHORE = Mutex.new
13
14
  private_constant :DEFAULT_SEMAPHORE
@@ -60,22 +61,23 @@ module Saxon
60
61
  end
61
62
 
62
63
  # Get a configuration option value
63
- # See https://www.saxonica.com/html/documentation/javadoc/net/sf/saxon/lib/FeatureKeys.html
64
- # for details of the available options. Use the constant name as a string
65
- # or symbol as the option
64
+ #
65
+ # See {net.sf.saxon.lib.FeatureKeys} for details of the available options.
66
+ # Use the constant name as a string or symbol as the option, e.g.
67
+ # +:allow_multhreading+, +'ALLOW_MULTITHREADING'+, +'allow_multithreading'+.
66
68
  #
67
69
  # @param option [String, Symbol]
68
70
  # @return [Object] the value of the configuration option
69
71
  # @raise [NameError] if the option name does not exist
72
+ # @see net.sf.saxon.lib.FeatureKeys
70
73
  def [](option)
71
74
  @config.getConfigurationProperty(option_url(option))
72
75
  end
73
76
 
74
- # Get a configuration option value
75
- # See http://saxonica.com/documentation9.5/javadoc/net/sf/saxon/lib/FeatureKeys.html
76
- # for details of the available options. Use the constant name as a string
77
- # or symbol as the option
77
+ # Set a configuration option value. See {#[]} for details about the option
78
+ # names.
78
79
  #
80
+ # @see #[]
79
81
  # @param option [String, Symbol]
80
82
  # @param value [Object] the value of the configuration option
81
83
  # @return [Object] the value you passed in
@@ -4,11 +4,217 @@ module Saxon
4
4
  # Builds XDM objects from XML sources, for use in XSLT or for query and
5
5
  # access
6
6
  class DocumentBuilder
7
+ # Provides a simple configuraion DSL for DocumentBuilders.
8
+ # @see DocumentBuilder.create
9
+ class ConfigurationDSL
10
+ # @api private
11
+ #
12
+ # Create a new instance and +instance_exec+ the passed-in block against it
13
+ def self.define(document_builder, block)
14
+ new(document_builder).instance_exec(&block)
15
+ end
16
+
17
+ # @api private
18
+ def initialize(document_builder)
19
+ @document_builder = document_builder
20
+ end
21
+
22
+ # Sets line numbering on or off
23
+ #
24
+ # @see DocumentBuilder#line_numbering=
25
+ #
26
+ # @param value [Boolean] on (true) or off (false)
27
+ def line_numbering(value)
28
+ @document_builder.line_numbering = value
29
+ end
30
+
31
+ # Sets the base URI of documents created using this instance.
32
+ #
33
+ # @see DocumentBuilder.base_uri=
34
+ #
35
+ # @param value [String, URI::File, URI::HTTP] The (absolute) base URI to use
36
+ def base_uri(value)
37
+ @document_builder.base_uri = value
38
+ end
39
+
40
+ # Sets the base URI of documents created using this instance.
41
+ #
42
+ # @see DocumentBuilder.base_uri=
43
+ #
44
+ # @param value [String, URI::File, URI::HTTP] The (absolute) base URI to use
45
+ def whitespace_stripping_policy(value)
46
+ @document_builder.whitespace_stripping_policy = value
47
+ end
48
+
49
+ # Sets the base URI of documents created using this instance.
50
+ #
51
+ # @see DocumentBuilder.base_uri=
52
+ #
53
+ # @param value [String, URI::File, URI::HTTP] The (absolute) base URI to use
54
+ def dtd_validation(value)
55
+ @document_builder.dtd_validation = value
56
+ end
57
+ end
58
+
59
+
60
+ # Create a new DocumentBuilder that can be used to build new XML documents
61
+ # with the passed-in {Saxon::Processor}. If a block is passed in it's
62
+ # executed as a DSL for configuring the builder instance.
63
+ #
64
+ # @param processor [Saxon::Processor] the Processor
65
+ # @yield An DocumentBuilder configuration DSL block
66
+ # @return [Saxon::DocumentBuilder] the new instance
67
+ def self.create(processor, &block)
68
+ new(processor.to_java.newDocumentBuilder, &block)
69
+ end
70
+
71
+ attr_reader :s9_document_builder
72
+ private :s9_document_builder
73
+
7
74
  # @api private
8
75
  # @param [net.sf.saxon.s9api.DocumentBuilder] s9_document_builder The
9
- # Saxon DocumentBuilder instance to wrap
10
- def initialize(s9_document_builder)
76
+ # Saxon DocumentBuilder instance to wrap
77
+ def initialize(s9_document_builder, &block)
11
78
  @s9_document_builder = s9_document_builder
79
+ if block_given?
80
+ ConfigurationDSL.define(self, block)
81
+ end
82
+ end
83
+
84
+ # Report whether documents created using this instance will keep track of
85
+ # the line and column numbers of elements.
86
+ #
87
+ # @return [Boolean] whether line numbering will be tracked
88
+ def line_numbering?
89
+ s9_document_builder.isLineNumbering
90
+ end
91
+
92
+
93
+ # Switch tracking of line and column numbers for elements in documents
94
+ # created by this instance on or off
95
+ #
96
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setLineNumbering
97
+ #
98
+ # @param on_or_not [Boolean] whether or not to track line numbering
99
+ def line_numbering=(on_or_not)
100
+ s9_document_builder.setLineNumbering(on_or_not)
101
+ end
102
+
103
+ # Return the default base URI to be used when building documents using this
104
+ # instance. This value will be ignored if the source being parsed has an
105
+ # intrinsic base URI (e.g. a File).
106
+ #
107
+ # Returns +nil+ if no URI is set (the default).
108
+ #
109
+ # @return [nil, URI::File, URI::HTTP] the default base URI (or nil)
110
+ def base_uri
111
+ uri = s9_document_builder.getBaseURI
112
+ uri.nil? ? uri : URI(uri.to_s)
113
+ end
114
+
115
+ # Set the base URI of documents created using this instance. This value will
116
+ # be ignored if the source being parsed has an intrinsic base URI (e.g. a
117
+ # File)
118
+ #
119
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setBaseURI
120
+ #
121
+ # @param uri [String, URI::File, URI::HTTP] The (absolute) base URI to use
122
+ def base_uri=(uri)
123
+ s9_document_builder.setBaseURI(java.net.URI.new(uri.to_s))
124
+ end
125
+
126
+ # Return the Whitespace stripping policy for this instance. Returns one of
127
+ # the standard policy names as a symbol, or the custom Java
128
+ # WhitespaceStrippingPolicy if one was defined using
129
+ # +#whitespace_stripping_policy = ->(qname) { ... }+. (See
130
+ # {#whitespace_stripping_policy=} for more.)
131
+ #
132
+ # +:all+: All whitespace-only nodes will be discarded
133
+ #
134
+ # +:none+: No whitespace-only nodes will be discarded (the default if DTD or
135
+ # schema validation is not in effect)
136
+ #
137
+ # +:ignorable+: Whitespace-only nodes inside elements defined as
138
+ # element-only in the DTD or schema being used will be discarded (the
139
+ # default if DTD or schema validation is in effect)
140
+ #
141
+ # +:unspecified+: the default, which in practice means :ignorable if DTD or
142
+ # schema validation is in effect, and :none otherwise.
143
+ #
144
+ # @return [:all, :none, :ignorable, :unspecified, Proc]
145
+ def whitespace_stripping_policy
146
+ s9_policy = s9_document_builder.getWhitespaceStrippingPolicy
147
+ case s9_policy
148
+ when Saxon::S9API::WhitespaceStrippingPolicy::UNSPECIFIED
149
+ :unspecified
150
+ when Saxon::S9API::WhitespaceStrippingPolicy::NONE
151
+ :none
152
+ when Saxon::S9API::WhitespaceStrippingPolicy::IGNORABLE
153
+ :ignorable
154
+ when Saxon::S9API::WhitespaceStrippingPolicy::ALL
155
+ :all
156
+ else
157
+ s9_policy
158
+ end
159
+ end
160
+
161
+ # Set the whitespace stripping policy to be used for documents built with
162
+ # this instance.
163
+ #
164
+ # Possible values are:
165
+ #
166
+ # * One of the standard policies, as a symbol (+:all+, +:none+,
167
+ # +:ignorable+, +:unspecified+, see {#whitespace_stripping_policy}).
168
+ # * A Java +net.sf.saxon.s9api.WhitesapceStrippingPolicy+ instance
169
+ # * A Proc/lambda that is handed an element name as a {Saxon::QName}, and
170
+ # should return true (if whitespace should be stripped for this element)
171
+ # or false (it should not).
172
+ # @example
173
+ # whitespace_stripping_policy = ->(element_qname) {
174
+ # element_qname == Saxon::QName.clark("{http://example.org/}element-name")
175
+ # }
176
+ #
177
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setWhitespaceStrippingPolicy
178
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/WhitespaceStrippingPolicy
179
+ # @param policy [Symbol, Proc, Saxon::S9API::WhitespaceStrippingPolicy] the
180
+ # policy to use
181
+ def whitespace_stripping_policy=(policy)
182
+ case policy
183
+ when :unspecified, :none, :ignorable, :all
184
+ s9_policy = Saxon::S9API::WhitespaceStrippingPolicy.const_get(policy.to_s.upcase.to_sym)
185
+ when Proc
186
+ wrapped_policy = ->(s9_qname) {
187
+ policy.call(Saxon::QName.new(s9_qname))
188
+ }
189
+ s9_policy = Saxon::S9API::WhitespaceStrippingPolicy.makeCustomPolicy(wrapped_policy)
190
+ when Saxon::S9API::WhitespaceStrippingPolicy
191
+ s9_policy = policy
192
+ else
193
+ raise InvalidWhitespaceStrippingPolicyError, "#{policy.inspect} is not one of the allowed Symbols, or a custom policy"
194
+ end
195
+ s9_document_builder.setWhitespaceStrippingPolicy(s9_policy)
196
+ end
197
+
198
+ # @return [Boolean] whether DTD Validation is enabled
199
+ def dtd_validation?
200
+ s9_document_builder.isDTDValidation
201
+ end
202
+
203
+ # Switches DTD validation on or off.
204
+ #
205
+ # It's important to note that DTD validation only applies to documents that
206
+ # contain a +<!doctype>+, but switching DTD validation off doesn't stop the
207
+ # XML parser Saxon uses from trying to retrieve the DTD that's referenced,
208
+ # which can mean network requests. By default, the SAX parser Saxon uses
209
+ # (Xerces) doesn't make use of XML catalogs, which causes problems when documents reference a DTD with a relative path as in:
210
+ # <!DOCTYPE root-element SYSTEM "example.dtd">
211
+ # This can be controlled through a configuration option, however.
212
+ #
213
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setDTDValidation
214
+ # @see https://www.saxonica.com/documentation9.9/index.html#!sourcedocs/controlling-parsing
215
+ # @param on [Boolean] whether DTD Validation should be enabled
216
+ def dtd_validation=(on)
217
+ s9_document_builder.setDTDValidation(on)
12
218
  end
13
219
 
14
220
  # @param [Saxon::Source] source The Saxon::Source containing the source
@@ -16,13 +222,18 @@ module Saxon
16
222
  # @return [Saxon::XDM::Node] The Saxon::XDM::Node representing the root of the
17
223
  # document tree
18
224
  def build(source)
19
- XDM::Node.new(@s9_document_builder.build(source.to_java))
225
+ XDM::Node.new(s9_document_builder.build(source.to_java))
20
226
  end
21
227
 
22
- # @return [net.sf.saxon.s9api.DocumentBuilder] The underlying Java Saxon
228
+ # @return [Java::NetSfSaxonS9api::DocumentBuilder] The underlying Java Saxon
23
229
  # DocumentBuilder instance
24
230
  def to_java
25
- @s9_document_builder
231
+ s9_document_builder
26
232
  end
27
233
  end
234
+
235
+ # Error raised when someone tries to set an invalid whitespace stripping
236
+ # policy on a {DocumentBuilder}
237
+ class InvalidWhitespaceStrippingPolicyError < RuntimeError
238
+ end
28
239
  end
@@ -0,0 +1,8 @@
1
+ module Saxon
2
+ module FeatureFlags
3
+ # Error raised if a feature is not available under the loaded version of
4
+ # Saxon
5
+ class UnavailableInThisSaxonVersionError < StandardError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module Saxon
2
+ module FeatureFlags
3
+ # Helper methods to create feature restrictions in the library. To be mixed
4
+ # in to library classes.
5
+ module Helpers
6
+ # specify that the method named can only run if the version constraint is satisfied
7
+ #
8
+ # @param method_name [Symbol] the name of the method
9
+ # @param version_constraint [String] the version constraint (<tt>'>= 9.9'</tt>)
10
+ def requires_saxon_version(method_name, version_constraint)
11
+ Saxon::FeatureFlags::Version.create(self, method_name, version_constraint)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,100 @@
1
+ require_relative '../version/library'
2
+ require_relative 'errors'
3
+
4
+ module Saxon
5
+ module FeatureFlags
6
+ # Restrict a specific method to only work with the specified Saxon version
7
+ class Version
8
+ # Modify the method so that it will only run if the version constraint is
9
+ # satisfied.
10
+ #
11
+ # We can't know what version of Saxon is in use at code load time, only
12
+ # once {Saxon::Loader#load!} has been called, either explicitly or by
13
+ # calling a method which requires a Saxon Java object. Therefore, we have
14
+ # to check this the first time someone calls the method.
15
+ #
16
+ # Creating this check replaces the method on the target class with one
17
+ # that checks the version, and runs the original version if its constraint
18
+ # is satisfied.
19
+ #
20
+ # To avoid performing the check every time the method is called, once we
21
+ # know whether or not the constraint is satisfied, we assume that the
22
+ # verion of Saxon cannot be unloaded and a new one loaded in its place and
23
+ # replace our version checking method with the original (if the constraint
24
+ # is passed), or with one that simply +raise+s the constraint error.
25
+ #
26
+ # @param klass [Class] the class the method lives on
27
+ # @param method_name [Symbol] the name of the method to be constrained
28
+ # @param version_constraint [String] the version constraint
29
+ # (<tt>'>9.9.1.7'</tt> or <tt>'>= 9.9'</tt>)
30
+ def self.create(klass, method_name, version_constraint)
31
+ method = klass.instance_method(method_name)
32
+ version_check = new(version_constraint)
33
+
34
+
35
+ klass.send(:define_method, method_name, ->(*args) {
36
+ if version_check.ok?
37
+ klass.send(:define_method, method_name, method)
38
+ method.bind(self).call(*args)
39
+ else
40
+ klass.send(:define_method, method_name, ->(*args) {
41
+ raise UnavailableInThisSaxonVersionError
42
+ })
43
+ raise UnavailableInThisSaxonVersionError
44
+ end
45
+ })
46
+ end
47
+
48
+ # @return [String] the complete constraint string
49
+ attr_reader :constraint_string
50
+ # @return [Symbol] the extracted comparison operator
51
+ attr_reader :comparison_operator
52
+ # @return [String] the extracted version string
53
+ attr_reader :version_string
54
+
55
+ # Create a version constraint check from the supplied constraint string
56
+ #
57
+ # @param constraint_string [String] the version constraint
58
+ def initialize(constraint_string)
59
+ @constraint_string = constraint_string
60
+ @comparison_operator, @version_string = parse_version_constraint(constraint_string)
61
+ end
62
+
63
+ # Reports if the version constraint is satisfied or not.
64
+ #
65
+ # @return [Boolean] true if the constraint is satisfied, false otherwise
66
+ def ok?
67
+ Saxon::Version::Library.loaded_version.send(comparison_operator, constraint_version)
68
+ end
69
+
70
+ # Generates a {Saxon::Version::Library} representing the version specified
71
+ # in the constraint that can be compared with the loaded Saxon version
72
+ #
73
+ # @return [Saxon::Version::Library] the constraint version
74
+ def constraint_version
75
+ @constraint_version ||= begin
76
+ components = version_string.split('.').map { |n| Integer(n, 10) }
77
+ Saxon::Version::Library.new(version_string, components, 'HE')
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def parse_version_constraint(version_constraint)
84
+ op_string, version_string = version_constraint.split
85
+
86
+ comparison_operator = parse_operator_string(op_string)
87
+ [comparison_operator, version_string]
88
+ end
89
+
90
+ def parse_operator_string(op_string)
91
+ case op_string
92
+ when '~>'
93
+ :pessimistic_compare
94
+ else
95
+ op_string.to_sym
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'feature_flags/version'
2
+ require_relative 'feature_flags/helpers'
3
+
4
+ module Saxon
5
+ # Allows saxon-rb features to be switched off if they can't be used under one
6
+ # of the otherwise-supported Saxon versions
7
+ #
8
+ # @api private
9
+ module FeatureFlags
10
+ end
11
+ end
@@ -222,9 +222,9 @@ module Saxon
222
222
  # Pattern fragments that can be combined to help create the lexical space
223
223
  # patterns in {Patterns}
224
224
  module PatternFragments
225
+ # The time part of the XSD Duration format allows T0H1M1S, T1H1M, T1M1S, T1H1S, T1H, T1M, T1S
225
226
  TIME_DURATION = /(?:T
226
227
  (?:
227
- # The time part of the format allows T0H1M1S, T1H1M, T1M1S, T1H1S, T1H, T1M, T1S
228
228
  [0-9]+[HM]|
229
229
  [0-9]+(?:\.[0-9]+)?S|
230
230
  [0-9]+H[0-9]+M|
@@ -233,46 +233,78 @@ module Saxon
233
233
  [0-9]+H[0-9]+M[0-9]+(?:\.[0-9]+)?S
234
234
  )
235
235
  )?/x
236
+ # XSD Date
236
237
  DATE = /-?[0-9]{4}-[0-9]{2}-[0-9]{2}/
238
+ # XSD DateTime Time
237
239
  TIME = /[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?/
240
+ # XSD DateTime Timezone
238
241
  TIME_ZONE = /(?:[\-+][0-9]{2}:[0-9]{2}|Z)?/
242
+ # Valid first characers in an NCName
239
243
  NCNAME_START_CHAR = '[A-Z]|_|[a-z]|[\u{C0}-\u{D6}]|[\u{D8}-\u{F6}]|[\u{F8}-\u{2FF}]|[\u{370}-\u{37D}]|[\u{37F}-\u{1FFF}]|[\u{200C}-\u{200D}]|[\u{2070}-\u{218F}]|[\u{2C00}-\u{2FEF}]|[\u{3001}-\u{D7FF}]|[\u{F900}-\u{FDCF}]|[\u{FDF0}-\u{FFFD}]|[\u{10000}-\u{EFFFF}]'
244
+ # Valid first characters in an XML Name
240
245
  NAME_START_CHAR = ":|" + NCNAME_START_CHAR
246
+ # Valid characters within an NCName
241
247
  NCNAME_CHAR = NCNAME_START_CHAR + '|-|\.|[0-9]|\u{B7}|[\u{0300}-\u{036F}]|[\u{203F}-\u{2040}]'
248
+ # Valid characters within an XML Name
242
249
  NAME_CHAR = ":|" + NCNAME_CHAR
243
250
  end
244
251
 
245
252
  # A collection of lexical space patterns for XDM types
246
253
  module Patterns
254
+ # Construct a Regexp from an array of patterns
255
+ # @param patterns [Array<String>]
256
+ # @return [Regexp]
247
257
  def self.build(*patterns)
248
258
  Regexp.new((['\A'] + patterns.map(&:to_s) + ['\z']).join(''))
249
259
  end
260
+ # @see https://www.w3.org/TR/xmlschema-2/#date-lexical-representation
250
261
  DATE = build(PatternFragments::DATE, PatternFragments::TIME_ZONE)
262
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
251
263
  DATE_TIME = build(PatternFragments::DATE, 'T', PatternFragments::TIME, PatternFragments::TIME_ZONE)
264
+ # @see https://www.w3.org/TR/xmlschema-2/#time-lexical-repr
252
265
  TIME = build(PatternFragments::TIME, PatternFragments::TIME_ZONE)
266
+ # @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
253
267
  DURATION = build(/-?P(?!\z)(?:[0-9]+Y)?(?:[0-9]+M)?(?:[0-9]+D)?/, PatternFragments::TIME_DURATION)
268
+ # @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
254
269
  DAY_TIME_DURATION = build(/-?P(?!\z)(?:[0-9]+D)?/, PatternFragments::TIME_DURATION)
270
+ # @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
255
271
  YEAR_MONTH_DURATION = /\A-?P(?!\z)(?:[0-9]+Y)?(?:[0-9]+M)?\z/
272
+ # @see https://www.w3.org/TR/xmlschema-2/#gDay-lexical-repr
256
273
  G_DAY = build(/---([0-9]{2})/, PatternFragments::TIME_ZONE)
274
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonth-lexical-repr
257
275
  G_MONTH = build(/--([0-9]{2})/, PatternFragments::TIME_ZONE)
276
+ # @see https://www.w3.org/TR/xmlschema-2/#gYear-lexical-repr
258
277
  G_YEAR = build(/(-?[0-9]{4,})/, PatternFragments::TIME_ZONE)
278
+ # @see https://www.w3.org/TR/xmlschema-2/#gYearMonth-lexical-repr
259
279
  G_YEAR_MONTH = build(/-?([0-9]{4,})-([0-9]{2})/, PatternFragments::TIME_ZONE)
280
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonthDay-lexical-repr
260
281
  G_MONTH_DAY = build(/--([0-9]{2})-([0-9]{2})/, PatternFragments::TIME_ZONE)
282
+ # @see https://www.w3.org/TR/xmlschema-2/#integer-lexical-representation
261
283
  INTEGER = /\A[+-]?[0-9]+\z/
284
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal-lexical-representation
262
285
  DECIMAL = /\A[+-]?[0-9]+(?:\.[0-9]+)?\z/
286
+ # @see https://www.w3.org/TR/xmlschema-2/#float-lexical-representation
263
287
  FLOAT = /\A(?:[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][0-9]+)?|-?INF|NaN)\z/
288
+ # @see https://www.w3.org/TR/xmlschema-2/#NCName
264
289
  NCNAME = build("(?:#{PatternFragments::NCNAME_START_CHAR})", "(?:#{PatternFragments::NCNAME_CHAR})*")
290
+ # @see https://www.w3.org/TR/xmlschema-2/#Name
265
291
  NAME = build("(?:#{PatternFragments::NAME_START_CHAR})", "(?:#{PatternFragments::NAME_CHAR})*")
292
+ # @see https://www.w3.org/TR/xmlschema-2/#token
266
293
  TOKEN = /\A[^\u0020\u000A\u000D\u0009]+(?: [^\u0020\u000A\u000D\u0009]+)*\z/
294
+ # @see https://www.w3.org/TR/xmlschema-2/#normalizedString
267
295
  NORMALIZED_STRING = /\A[^\u000A\u000D\u0009]+\z/
296
+ # @see https://www.w3.org/TR/xmlschema-2/#NMTOKEN
268
297
  NMTOKEN = build("(?:#{PatternFragments::NAME_CHAR})+")
298
+ # @see https://www.w3.org/TR/xmlschema-2/#language
269
299
  LANGUAGE = /\A[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\z/
300
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
270
301
  BASE64_BINARY = /\A(?:(?:[A-Za-z0-9+\/] ?){4})*(?:(?:[A-Za-z0-9+\/] ?){3}[A-Za-z0-9+\/]|(?:[A-Za-z0-9+\/] ?){2}[AEIMQUYcgkosw048] ?=|[A-Za-z0-9+\/] ?[AQgw] ?= ?=)?\z/
271
302
  end
272
303
 
273
304
  # Convertors from Ruby values to lexical string representations for a
274
305
  # particular XDM type
275
306
  module Convertors
307
+ # @see https://www.w3.org/TR/xmlschema-2/#anyURI
276
308
  ANY_URI = ->(value, item_type) {
277
309
  uri_classes = [URI::Generic]
278
310
  case value
@@ -286,13 +318,17 @@ module Saxon
286
318
  end
287
319
  end
288
320
  }
321
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
289
322
  BASE64_BINARY = ->(value, item_type) {
290
323
  Base64.strict_encode64(value.to_s.force_encoding(Encoding::ASCII_8BIT))
291
324
  }
325
+ # @see https://www.w3.org/TR/xmlschema-2/#boolean
292
326
  BOOLEAN = ->(value, item_type) {
293
327
  value ? 'true' : 'false'
294
328
  }
329
+ # @see https://www.w3.org/TR/xmlschema-2/#byte
295
330
  BYTE = ByteConversion.new
331
+ # @see https://www.w3.org/TR/xmlschema-2/#date
296
332
  DATE = ->(value, item_type) {
297
333
  if value.respond_to?(:strftime)
298
334
  value.strftime('%F')
@@ -300,6 +336,7 @@ module Saxon
300
336
  LexicalStringConversion.validate(value, item_type, Patterns::DATE)
301
337
  end
302
338
  }
339
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
303
340
  DATE_TIME = ->(value, item_type) {
304
341
  if value.respond_to?(:strftime)
305
342
  value.strftime('%FT%T%:z')
@@ -307,11 +344,15 @@ module Saxon
307
344
  LexicalStringConversion.validate(value, item_type, Patterns::DATE_TIME)
308
345
  end
309
346
  }
347
+ # @see https://www.w3.org/TR/xmlschema-2/#time
310
348
  TIME = ->(value, item_type) {
311
349
  LexicalStringConversion.validate(value, item_type, Patterns::TIME)
312
350
  }
351
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
313
352
  DATE_TIME_STAMP = DATE_TIME
353
+ # @see https://www.w3.org/TR/xmlschema-2/#duration
314
354
  DAY_TIME_DURATION = DurationConversion.new(Patterns::DAY_TIME_DURATION)
355
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal
315
356
  DECIMAL = ->(value, item_type) {
316
357
  case value
317
358
  when ::Integer
@@ -324,19 +365,25 @@ module Saxon
324
365
  LexicalStringConversion.validate(value, item_type, Patterns::DECIMAL)
325
366
  end
326
367
  }
368
+ # @see https://www.w3.org/TR/xmlschema-2/#double
327
369
  DOUBLE = FloatConversion.new(:single)
370
+ # @see https://www.w3.org/TR/xmlschema-2/#duration
328
371
  DURATION = DurationConversion.new(Patterns::DURATION)
372
+ # @see https://www.w3.org/TR/xmlschema-2/#float
329
373
  FLOAT = FloatConversion.new
374
+ # @see https://www.w3.org/TR/xmlschema-2/#gDay
330
375
  G_DAY = GDateConversion.new({
331
376
  bounds: 1..31,
332
377
  validation_pattern: Patterns::G_DAY,
333
378
  integer_formatter: ->(value) { '---%02d' }
334
379
  })
380
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonth
335
381
  G_MONTH = GDateConversion.new({
336
382
  bounds: 1..12,
337
383
  validation_pattern: Patterns::G_MONTH,
338
384
  integer_formatter: ->(value) { '--%02d' }
339
385
  })
386
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonthDay
340
387
  G_MONTH_DAY = ->(value, item_type) {
341
388
  month_days = {
342
389
  1 => 31,
@@ -359,6 +406,7 @@ module Saxon
359
406
  raise Errors::RubyValueOutOfBounds.new(value, item_type) if day > month_days[month]
360
407
  formatted_value
361
408
  }
409
+ # @see https://www.w3.org/TR/xmlschema-2/#gYear
362
410
  G_YEAR = GDateConversion.new({
363
411
  bounds: ->(value) { value != 0 },
364
412
  validation_pattern: Patterns::G_YEAR,
@@ -366,6 +414,7 @@ module Saxon
366
414
  value.negative? ? '%05d' : '%04d'
367
415
  }
368
416
  })
417
+ # @see https://www.w3.org/TR/xmlschema-2/#gYearMonth
369
418
  G_YEAR_MONTH = ->(value, item_type) {
370
419
  formatted_value = LexicalStringConversion.validate(value, item_type, Patterns::G_YEAR_MONTH)
371
420
  year, month = Patterns::G_YEAR_MONTH.match(formatted_value).captures.take(2).map { |i|
@@ -376,43 +425,68 @@ module Saxon
376
425
  end
377
426
  value
378
427
  }
428
+ # @see https://www.w3.org/TR/xmlschema-2/#hexBinary
379
429
  HEX_BINARY = ->(value, item_type) {
380
430
  value.to_s.force_encoding(Encoding::ASCII_8BIT).each_byte.map { |b| b.to_s(16) }.join
381
431
  }
432
+ # @see https://www.w3.org/TR/xmlschema-2/#int
382
433
  INT = IntegerConversion.new(-2147483648, 2147483647)
434
+ # @see https://www.w3.org/TR/xmlschema-2/#integer
383
435
  INTEGER = IntegerConversion.new(nil, nil)
436
+ # @see https://www.w3.org/TR/xmlschema-2/#language
384
437
  LANGUAGE = ->(value, item_type) {
385
438
  LexicalStringConversion.validate(value, item_type, Patterns::LANGUAGE)
386
439
  }
440
+ # @see https://www.w3.org/TR/xmlschema-2/#long
387
441
  LONG = IntegerConversion.new(-9223372036854775808, 9223372036854775807)
442
+ # @see https://www.w3.org/TR/xmlschema-2/#Name
388
443
  NAME = ->(value, item_type) {
389
444
  LexicalStringConversion.validate(value, item_type, Patterns::NAME)
390
445
  }
446
+ # @see https://www.w3.org/TR/xmlschema-2/#ID
447
+ # @see https://www.w3.org/TR/xmlschema-2/#IDREF
448
+ # @see https://www.w3.org/TR/xmlschema-2/#ENTITY
449
+ # @see https://www.w3.org/TR/xmlschema-2/#NCName
391
450
  ID = IDREF = ENTITY = NCNAME = ->(value, item_type) {
392
451
  LexicalStringConversion.validate(value, item_type, Patterns::NCNAME)
393
452
  }
453
+ # @see https://www.w3.org/TR/xmlschema-2/#negativeInteger
394
454
  NEGATIVE_INTEGER = IntegerConversion.new(nil, -1)
455
+ # @see https://www.w3.org/TR/xmlschema-2/#NMTOKEN
395
456
  NMTOKEN = ->(value, item_type) {
396
457
  LexicalStringConversion.validate(value, item_type, Patterns::NMTOKEN)
397
458
  }
459
+ # @see https://www.w3.org/TR/xmlschema-2/#nonNegativeInteger
398
460
  NON_NEGATIVE_INTEGER = IntegerConversion.new(0, nil)
461
+ # @see https://www.w3.org/TR/xmlschema-2/#nonPositiveInteger
399
462
  NON_POSITIVE_INTEGER = IntegerConversion.new(nil, 0)
463
+ # @see https://www.w3.org/TR/xmlschema-2/#normalizedString
400
464
  NORMALIZED_STRING = ->(value, item_type) {
401
465
  LexicalStringConversion.validate(value, item_type, Patterns::NORMALIZED_STRING)
402
466
  }
467
+ # @see https://www.w3.org/TR/xmlschema-2/#positiveInteger
403
468
  POSITIVE_INTEGER = IntegerConversion.new(1, nil)
469
+ # @see https://www.w3.org/TR/xmlschema-2/#short
404
470
  SHORT = IntegerConversion.new(-32768, 32767)
405
471
  # STRING (It's questionable whether anything needs doing here)
472
+ # @see https://www.w3.org/TR/xmlschema-2/#token
406
473
  TOKEN = ->(value, item_type) {
407
474
  LexicalStringConversion.validate(value, item_type, Patterns::TOKEN)
408
475
  }
476
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
409
477
  UNSIGNED_BYTE = ByteConversion.new(:unsigned)
478
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedInt
410
479
  UNSIGNED_INT = IntegerConversion.new(0, 4294967295)
480
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedLong
411
481
  UNSIGNED_LONG = IntegerConversion.new(0, 18446744073709551615)
482
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedShort
412
483
  UNSIGNED_SHORT = IntegerConversion.new(0, 65535)
484
+ # @see https://www.w3.org/TR/xmlschema-2/#duration
413
485
  YEAR_MONTH_DURATION = ->(value, item_type) {
414
486
  LexicalStringConversion.validate(value, item_type, Patterns::YEAR_MONTH_DURATION)
415
487
  }
488
+ # @see https://www.w3.org/TR/xmlschema-2/#QName
489
+ # @see https://www.w3.org/TR/xmlschema-2/#NOTATION
416
490
  QNAME = NOTATION = ->(value, item_type) {
417
491
  raise Errors::UnconvertableNamespaceSensitveItemType
418
492
  }
@@ -430,6 +504,8 @@ module Saxon
430
504
  @value, @item_type = value, item_type
431
505
  end
432
506
 
507
+ # error message includes Ruby value and the XDM type conversion was
508
+ # being attempted to
433
509
  def to_s
434
510
  "Ruby value #{value.inspect} cannot be converted to an XDM #{item_type.type_name.to_s}"
435
511
  end
@@ -446,6 +522,7 @@ module Saxon
446
522
  @value, @item_type = value, item_type
447
523
  end
448
524
 
525
+ # error message includes Ruby value and the XDM type whose bounds it is outside of
449
526
  def to_s
450
527
  "Ruby value #{value.inspect} is outside the allowed bounds of an XDM #{item_type.type_name.to_s}"
451
528
  end
@@ -8,6 +8,7 @@ module Saxon
8
8
  # Regexp patterns to assist in converting XDM typed values into Ruby
9
9
  # native types
10
10
  module Patterns
11
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
11
12
  DATE_TIME = /\A(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)(Z|[-+][0-9]{2}:[0-9]{2})?\z/
12
13
  end
13
14
 
@@ -54,34 +55,45 @@ module Saxon
54
55
  # A collection of Lambdas (or objects responding to +#call+) for
55
56
  # converting XDM typed values to native Ruby types.
56
57
  module Convertors
58
+ # @see https://www.w3.org/TR/xmlschema-2/#integer
57
59
  INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
58
60
  POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
59
61
  xdm_atomic_value.to_java.getValue
60
62
  }
63
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal
61
64
  DECIMAL = ->(xdm_atomic_value) {
62
65
  BigDecimal(xdm_atomic_value.to_s)
63
66
  }
67
+ # @see https://www.w3.org/TR/xmlschema-2/#float
64
68
  FLOAT = DOUBLE = ->(xdm_atomic_value) {
65
69
  xdm_atomic_value.to_java.getValue
66
70
  }
71
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
67
72
  DATE_TIME = DATE_TIME_STAMP = ValueToRuby::DateTimeConvertor
73
+ # @see https://www.w3.org/TR/xmlschema-2/#boolean
68
74
  BOOLEAN = ->(xdm_atomic_value) {
69
75
  xdm_atomic_value.to_java.getValue
70
76
  }
77
+ # @see https://www.w3.org/TR/xmlschema-2/#NOTATION
78
+ # @see https://www.w3.org/TR/xmlschema-2/#QName
71
79
  NOTATION = QNAME = ->(xdm_atomic_value) {
72
80
  Saxon::QName.new(xdm_atomic_value.to_java.getQNameValue)
73
81
  }
82
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
74
83
  BASE64_BINARY = ->(xdm_atomic_value) {
75
84
  Base64.decode64(xdm_atomic_value.to_s)
76
85
  }
86
+ # @see https://www.w3.org/TR/xmlschema-2/#hexBinary
77
87
  HEX_BINARY = ->(xdm_atomic_value) {
78
88
  bytes = []
79
89
  xdm_atomic_value.to_s.scan(/../) { |x| bytes << x.hex.chr(Encoding::ASCII_8BIT) }
80
90
  bytes.join
81
91
  }
92
+ # @see https://www.w3.org/TR/xmlschema-2/#byte
82
93
  BYTE = ->(xdm_atomic_value) {
83
94
  [xdm_atomic_value.to_java.getLongValue].pack('c')
84
95
  }
96
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
85
97
  UNSIGNED_BYTE = ->(xdm_atomic_value) {
86
98
  [xdm_atomic_value.to_java.getLongValue].pack('C')
87
99
  }
data/lib/saxon/loader.rb CHANGED
@@ -19,6 +19,7 @@ module Saxon
19
19
  @path = path
20
20
  end
21
21
 
22
+ # returns an error message including the missing path
22
23
  def to_s
23
24
  "The path ('#{@path}') you supplied for the Saxon .jar files doesn't exist, sorry"
24
25
  end
@@ -31,6 +32,7 @@ module Saxon
31
32
  @path = path
32
33
  end
33
34
 
35
+ # returns an error message including the path looked in and jars we were looking for
34
36
  def to_s
35
37
  "One of saxon9he.jar, saxon9pe.jar, or saxon9ee.jar must be present in the path ('#{@path}') you supplied, sorry"
36
38
  end
@@ -46,12 +46,14 @@ module Saxon
46
46
  @s9_processor = s9_processor
47
47
  end
48
48
 
49
- # Generate a new DocumentBuilder that uses this Processor.
50
- # Sharing DocumentBuilders across threads is not recommended/
49
+ # Generate a new DocumentBuilder that uses this Processor. Sharing
50
+ # DocumentBuilders across threads is not safe.
51
51
  #
52
+ # @yield A DocumentBuilder configuration DSL block, see
53
+ # {Saxon::DocumentBuilder.create}
52
54
  # @return [Saxon::DocumentBuilder] A new Saxon::DocumentBuilder
53
- def document_builder
54
- Saxon::DocumentBuilder.new(@s9_processor.newDocumentBuilder)
55
+ def document_builder(&block)
56
+ Saxon::DocumentBuilder.create(self, &block)
55
57
  end
56
58
 
57
59
  # Declare custom collations for use by XSLT, XPath, and XQuery processors
@@ -0,0 +1,89 @@
1
+ require 'saxon/s9api'
2
+
3
+ module Saxon
4
+ module Version
5
+ # The version of the underlying Saxon library, which we need to discover at
6
+ # runtime based on what version is on the Classpath
7
+ class Library
8
+ # The loaded version of the Saxon Java library
9
+ #
10
+ # @return [Saxon::Version::Library] the version of the loaded library
11
+ def self.loaded_version
12
+ Saxon::Loader.load!
13
+
14
+ sv = Java::net.sf.saxon.Version
15
+ new(sv.getProductVersion, sv.getStructuredVersionNumber, sv.softwareEdition)
16
+ end
17
+
18
+ include Comparable
19
+
20
+ # @return [String] the version string (e.g. '9.9.1.6', '10.0')
21
+ attr_reader :version
22
+ # @return [String] the version components (e.g. <tt>[9, 9, 1, 6]</tt>, <tt>[10, 0]</tt>)
23
+ attr_reader :components
24
+ # @return [Symbol] the edition (+:he+, +:pe+, or +:ee+)
25
+ attr_reader :edition
26
+
27
+ # @param version [String] the version string
28
+ # @param components [Array<Integer>] the version components separated
29
+ # @param edition [String, Symbol] the name of the Saxon edition (e.g. +:he+, +'HE'+)
30
+ def initialize(version, components, edition)
31
+ @version = version.dup.freeze
32
+ @components = components.dup.freeze
33
+ @edition = edition.downcase.to_sym
34
+ end
35
+
36
+ # Comparison against another instance
37
+ #
38
+ # @param other [Saxon::Version::Library] the other version to compare against
39
+ # @return [Integer] -1 for less, 1 for greater, 0 for equal
40
+ def <=>(other)
41
+ return false unless other.is_a?(self.class)
42
+
43
+ n_components = [self.components.length, other.components.length].max
44
+ (0..(n_components - 1)).reduce(0, &comparator(other))
45
+ end
46
+
47
+ # Pessimistic comparison à la rubygems +~>+: do I satisfy the other
48
+ # version if considered as a pessimistic version constraint
49
+ #
50
+ # @param pessimistic_version [Saxon::Version::Library] the version to
51
+ # compare pessimistically
52
+ # @return [Boolean] do I satisfy the constraint?
53
+ def pessimistic_compare(pessimistic_version)
54
+ pessimistic_components = pessimistic_version.components
55
+ pessimistic_components = pessimistic_components + [0] if pessimistic_components.length == 1
56
+ locked = pessimistic_components[0..-2]
57
+ locked = locked.zip(components[0..locked.length])
58
+ variable = [pessimistic_components[-1], components[locked.length]]
59
+
60
+ locked_ok = locked.all? { |check, mine|
61
+ check == mine
62
+ }
63
+
64
+ return false unless locked_ok
65
+
66
+ check, mine = variable
67
+ mine >= check
68
+ end
69
+
70
+ # @return [String] the version string
71
+ def to_s
72
+ version
73
+ end
74
+
75
+ private
76
+
77
+ def comparator(other)
78
+ ->(cmp, i) {
79
+ return cmp unless cmp == 0
80
+
81
+ mine = self.components[i].nil? ? 0 : self.components[i]
82
+ theirs = other.components[i].nil? ? 0 : other.components[i]
83
+
84
+ mine <=> theirs
85
+ }
86
+ end
87
+ end
88
+ end
89
+ end
data/lib/saxon/version.rb CHANGED
@@ -1,3 +1,9 @@
1
+ require 'saxon/s9api'
2
+
1
3
  module Saxon
2
- VERSION = "0.6.0"
4
+ # Provides the saxon-rb and underlying Saxon library versions
5
+ module Version
6
+ # The version of the saxon-rb gem (not of Saxon itself)
7
+ WRAPPER = "0.7.0"
8
+ end
3
9
  end
@@ -20,6 +20,7 @@ module Saxon
20
20
  # them imlpicitly through the XDM::AtomicValue creation process doesn't really
21
21
  # work. They need to be created explicitly and then handed in to be wrapped.
22
22
  class CannotCreateQNameFromString < StandardError
23
+ # returns an error message
23
24
  def to_s
24
25
  "QName XDM::AtomicValues must be created using an instance of Saxon::QName, not a string like 'prefix:name': Prefix URI binding is undefined at this point"
25
26
  end
@@ -29,7 +30,8 @@ module Saxon
29
30
  # isn't a way to create these outside of parsing an XML document within
30
31
  # Saxon, so attempting to do so raises this error.
31
32
  class NotationCannotBeDirectlyCreated < StandardError
32
- def to_s
33
+ # returns an error message
34
+ def to_s
33
35
  "xs:NOTATION XDM::AtomicValues cannot be directly created outside of XML parsing."
34
36
  end
35
37
  end
@@ -85,6 +85,36 @@ module Saxon
85
85
  def axis_iterator(axis)
86
86
  AxisIterator.new(self, axis)
87
87
  end
88
+
89
+ # Use Saxon's naive XDM Node serialisation to serialize the node and its
90
+ # descendants. Saxon uses a new Serializer with default options to
91
+ # serialize the node. In particular, if the Node was produced by an XSLT
92
+ # that used +<xsl:character-map>+ through +<xsl:output>+ to modify the
93
+ # contents, then they *WILL* *NOT* have been applied.
94
+ #
95
+ # +<xsl:output>+ has its effect at serialization time, not at XDM tree
96
+ # creation time, so it won't be applied until you serialize the document.
97
+ #
98
+ # Even then, unless you use a {Serializer} configured with the XSLT's
99
+ # +<xsl:output>+. We make a properly configured serializer available in
100
+ # the result of any XSLT transform (a {Saxon::XSLT::Invocation}), e.g.:
101
+ #
102
+ # result = xslt.apply_templates(input)
103
+ # result.to_s
104
+ # # or
105
+ # result.serialize('/path/to/output.xml')
106
+ #
107
+ # You can also get that {Serializer} directly from {XSLT::Executable},
108
+ # should you want to use the serialization options from a particular XSLT
109
+ # to serialize arbitrary XDM values:
110
+ #
111
+ # serializer = xslt.serializer
112
+ # serializer.serialize(an_xdm_value)
113
+ #
114
+ # @see http://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/XdmNode@toString
115
+ def to_s
116
+ s9_xdm_node.toString
117
+ end
88
118
  end
89
119
  end
90
120
  end
@@ -40,7 +40,7 @@ module Saxon
40
40
  # static context.
41
41
  #
42
42
  # @param processor [Saxon::Processor] the {Saxon::Processor} to use
43
- # @yield An XPath compiler DSL block
43
+ # @yield An XPath::StaticContext::DSL block
44
44
  # @return [Saxon::XPath::Compiler] the new compiler instance
45
45
  def self.create(processor, &block)
46
46
  static_context = XPath::StaticContext.define(block)
@@ -79,7 +79,7 @@ module Saxon
79
79
  # Compiler's static context. As with {.create}, passing a block gives
80
80
  # access to a DSL for setting up the compiler's static context.
81
81
  #
82
- # @yield An {XPath::StaticContext::DSL} block
82
+ # @yield An XPath::StaticContext::DSL block
83
83
  # @return [Saxon::XPath::Compiler] the new compiler instance
84
84
  def create(&block)
85
85
  new_static_context = static_context.define(block)
@@ -117,7 +117,12 @@ module Saxon
117
117
  DSL.define(block)
118
118
  end
119
119
 
120
- attr_reader :declared_variables, :declared_namespaces, :default_collation
120
+ # @return [String] The default collation URI as a String
121
+ attr_reader :default_collation
122
+ # @return [Hash<Saxon::QName => Saxon::XPath::VariableDeclaration] the declared variables
123
+ attr_reader :declared_variables
124
+ # @return [Hash<String => String>] the declared namespaces, as a prefix => uri hash
125
+ attr_reader :declared_namespaces
121
126
 
122
127
  # @return [Saxon::QName]
123
128
  # @overload resolve_variable_qname(qname)
@@ -143,7 +143,17 @@ module Saxon
143
143
  DSL.define(block)
144
144
  end
145
145
 
146
- attr_reader :default_collation, :static_parameters, :global_parameters, :initial_template_parameters, :initial_template_tunnel_parameters
146
+
147
+ # @return [String] The default collation URI as a String
148
+ attr_reader :default_collation
149
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the static parameters
150
+ attr_reader :static_parameters
151
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the global parameters
152
+ attr_reader :global_parameters
153
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the initial template parameters
154
+ attr_reader :initial_template_parameters
155
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the initial template parameters with tunnelling = "yes"
156
+ attr_reader :initial_template_tunnel_parameters
147
157
 
148
158
  # @api private
149
159
  # When passed a Proc, create a new EvaluationContext based on this one, with the same DSL available as in {.define}.
@@ -4,6 +4,7 @@ require_relative 'invocation'
4
4
  require_relative '../serializer'
5
5
  require_relative '../xdm'
6
6
  require_relative '../qname'
7
+ require_relative '../feature_flags'
7
8
 
8
9
  module Saxon
9
10
  module XSLT
@@ -53,6 +54,7 @@ module Saxon
53
54
  # prefix, you must use an explicit {Saxon::QName} to refer to it.
54
55
  class Executable
55
56
  extend Forwardable
57
+ extend Saxon::FeatureFlags::Helpers
56
58
 
57
59
  attr_reader :evaluation_context
58
60
  private :evaluation_context
@@ -162,6 +164,15 @@ module Saxon
162
164
  transformation(opts.reject { |k, v| k == :args }).call_function(function_name, args)
163
165
  end
164
166
 
167
+ # Create a {Serializer::Object} configured using the options that were set
168
+ # by +<xsl:output>+.
169
+ #
170
+ # @return [Saxon::Serializer::Object] the Serializer
171
+ def serializer
172
+ Saxon::Serializer::Object.new(@s9_xslt_executable.load30.newSerializer)
173
+ end
174
+ requires_saxon_version :serializer, '>= 9.9'
175
+
165
176
  # @return [net.sf.saxon.s9api.XsltExecutable] the underlying Saxon
166
177
  # +XsltExecutable+
167
178
  def to_java
@@ -203,6 +214,7 @@ module Saxon
203
214
  # Represents a loaded XSLT transformation ready to be applied against a
204
215
  # context node.
205
216
  class Transformation
217
+ # A list of valid option names for the transform
206
218
  VALID_OPTS = [:raw, :mode, :global_context_item, :global_parameters, :initial_template_parameters, :initial_template_tunnel_parameters]
207
219
 
208
220
  attr_reader :s9_transformer, :opts
@@ -297,11 +309,11 @@ module Saxon
297
309
  end
298
310
 
299
311
  def initial_template_parameters(parameters)
300
- s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters) , false)
312
+ s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters), false)
301
313
  end
302
314
 
303
315
  def initial_template_tunnel_parameters(parameters)
304
- s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters) , true)
316
+ s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters), true)
305
317
  end
306
318
  end
307
319
 
@@ -10,6 +10,7 @@ module Saxon
10
10
  # IO.
11
11
  class Invocation
12
12
  attr_reader :s9_transformer, :invocation_lambda
13
+ private :s9_transformer, :invocation_lambda
13
14
 
14
15
  # @api private
15
16
  def initialize(s9_transformer, invocation_lambda, raw)
@@ -65,7 +66,7 @@ module Saxon
65
66
 
66
67
  # Send the result of the transformation to the supplied Destination
67
68
  #
68
- # @param destination [net.sf.saxon.s9api.Destination] the Saxon
69
+ # @param s9_destination [net.sf.saxon.s9api.Destination] the Saxon
69
70
  # destination to use
70
71
  # @return [nil]
71
72
  def to_destination(s9_destination)
data/saxon-rb.gemspec CHANGED
@@ -5,7 +5,7 @@ require 'saxon/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'saxon-rb'
8
- spec.version = Saxon::VERSION
8
+ spec.version = Saxon::Version::WRAPPER
9
9
  spec.authors = ['Matt Patterson']
10
10
  spec.email = ['matt@werkstatt.io']
11
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saxon-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: java
6
6
  authors:
7
7
  - Matt Patterson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-19 00:00:00.000000000 Z
11
+ date: 2020-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,7 @@ files:
164
164
  - ".gitignore"
165
165
  - ".rspec"
166
166
  - ".ruby-version"
167
+ - ".yardopts"
167
168
  - CODE_OF_CONDUCT.md
168
169
  - Gemfile
169
170
  - LICENSE.txt
@@ -171,6 +172,7 @@ files:
171
172
  - Rakefile
172
173
  - bin/console
173
174
  - bin/setup
175
+ - docs/templates/plugin.rb
174
176
  - lib/net/sf/saxon/Saxon-HE/9.9.1-6/Saxon-HE-9.9.1-6.jar
175
177
  - lib/saxon-rb.rb
176
178
  - lib/saxon-rb_jars.rb
@@ -178,6 +180,10 @@ files:
178
180
  - lib/saxon/axis_iterator.rb
179
181
  - lib/saxon/configuration.rb
180
182
  - lib/saxon/document_builder.rb
183
+ - lib/saxon/feature_flags.rb
184
+ - lib/saxon/feature_flags/errors.rb
185
+ - lib/saxon/feature_flags/helpers.rb
186
+ - lib/saxon/feature_flags/version.rb
181
187
  - lib/saxon/item_type.rb
182
188
  - lib/saxon/item_type/lexical_string_conversion.rb
183
189
  - lib/saxon/item_type/value_to_ruby.rb
@@ -196,6 +202,7 @@ files:
196
202
  - lib/saxon/serializer/output_properties.rb
197
203
  - lib/saxon/source.rb
198
204
  - lib/saxon/version.rb
205
+ - lib/saxon/version/library.rb
199
206
  - lib/saxon/xdm.rb
200
207
  - lib/saxon/xdm/array.rb
201
208
  - lib/saxon/xdm/atomic_value.rb
@@ -238,8 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
245
  version: '0'
239
246
  requirements:
240
247
  - jar net.sf.saxon, Saxon-HE, 9.9.1-6
241
- rubyforge_project:
242
- rubygems_version: 2.7.10
248
+ rubygems_version: 3.1.3
243
249
  signing_key:
244
250
  specification_version: 4
245
251
  summary: Saxon 9.9 for JRuby, with an idiomatic Ruby API