saxon-rb 0.6.0-java → 0.7.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|