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 +4 -4
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/README.md +1 -1
- data/docs/templates/plugin.rb +73 -0
- data/lib/saxon/configuration.rb +15 -13
- data/lib/saxon/document_builder.rb +216 -5
- data/lib/saxon/feature_flags/errors.rb +8 -0
- data/lib/saxon/feature_flags/helpers.rb +15 -0
- data/lib/saxon/feature_flags/version.rb +100 -0
- data/lib/saxon/feature_flags.rb +11 -0
- data/lib/saxon/item_type/lexical_string_conversion.rb +78 -1
- data/lib/saxon/item_type/value_to_ruby.rb +12 -0
- data/lib/saxon/loader.rb +2 -0
- data/lib/saxon/processor.rb +6 -4
- data/lib/saxon/version/library.rb +89 -0
- data/lib/saxon/version.rb +7 -1
- data/lib/saxon/xdm/atomic_value.rb +3 -1
- data/lib/saxon/xdm/node.rb +30 -0
- data/lib/saxon/xpath/compiler.rb +2 -2
- data/lib/saxon/xpath/static_context.rb +6 -1
- data/lib/saxon/xslt/evaluation_context.rb +11 -1
- data/lib/saxon/xslt/executable.rb +14 -2
- data/lib/saxon/xslt/invocation.rb +2 -1
- data/saxon-rb.gemspec +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11d57a8924dff1a6f260b5496fcbc0c693d3bc4818b782acf0329950952ad1c6
|
4
|
+
data.tar.gz: 67353b0bd7ffc1a2d350019ee20c41713d69e1f5a5bbfa4c42a0d420456401c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f99187a484b68f55bb9fd7e0e49cbd4dd87bf2d1b366ed178140c24965a9ef9d61185e26f904b5f4aaea2cf9722bd3a842995e32fda036c9b17e38c55d5cac
|
7
|
+
data.tar.gz: 837e3e4f0e4bbdd70f88ed21c595ea4b5416a615362ad663f84a4b23956349bbce813528b20e783f5b2664560337be30b964bceb756e1c5554656939dd71ecbf
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
jruby-9.2.
|
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.
|
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__))
|
data/lib/saxon/configuration.rb
CHANGED
@@ -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.
|
6
|
-
#
|
7
|
-
# for details of what configuration options
|
8
|
-
# they accept.
|
9
|
-
#
|
10
|
-
# for details of the constant names used to
|
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
|
-
#
|
64
|
-
# for details of the available options.
|
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
|
-
#
|
75
|
-
#
|
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
|
-
#
|
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(
|
225
|
+
XDM::Node.new(s9_document_builder.build(source.to_java))
|
20
226
|
end
|
21
227
|
|
22
|
-
# @return [
|
228
|
+
# @return [Java::NetSfSaxonS9api::DocumentBuilder] The underlying Java Saxon
|
23
229
|
# DocumentBuilder instance
|
24
230
|
def to_java
|
25
|
-
|
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,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
|
data/lib/saxon/processor.rb
CHANGED
@@ -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
|
-
#
|
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.
|
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
@@ -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
|
-
|
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
|
data/lib/saxon/xdm/node.rb
CHANGED
@@ -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
|
data/lib/saxon/xpath/compiler.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
-
|
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)
|
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)
|
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
|
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
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.
|
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-
|
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
|
-
|
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
|