saxon-rb 0.6.0-java → 0.8.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/.circleci/config.yml +623 -44
- data/.rspec-jar-loading +2 -0
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/README.md +1 -1
- data/Rakefile +10 -4
- data/docs/templates/plugin.rb +73 -0
- data/lib/saxon-rb.rb +0 -1
- data/lib/saxon/configuration.rb +15 -13
- data/lib/saxon/document_builder.rb +216 -5
- data/lib/saxon/feature_flags.rb +11 -0
- 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/item_type.rb +116 -71
- 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/jruby_bug_6197_workaround.rb +5 -0
- data/lib/saxon/loader.rb +56 -43
- data/lib/saxon/processor.rb +6 -4
- data/lib/saxon/s9api.rb +17 -17
- data/lib/saxon/version.rb +7 -1
- data/lib/saxon/version/library.rb +89 -0
- data/lib/saxon/xdm/atomic_value.rb +16 -9
- 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 +22 -14
@@ -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
|
data/lib/saxon/item_type.rb
CHANGED
@@ -6,8 +6,49 @@ require_relative 'item_type/value_to_ruby'
|
|
6
6
|
module Saxon
|
7
7
|
# Represent XDM types abstractly
|
8
8
|
class ItemType
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# lazy-loading Hash so we can avoid eager-loading saxon Jars, which prevents
|
10
|
+
# using external Saxon Jars unless the user is more careful than they should
|
11
|
+
# have to be.
|
12
|
+
class LazyReadOnlyHash
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
attr_reader :loaded_hash, :load_mutex, :init_block
|
16
|
+
private :loaded_hash, :load_mutex, :init_block
|
17
|
+
|
18
|
+
def initialize(&init_block)
|
19
|
+
@init_block = init_block
|
20
|
+
@load_mutex = Mutex.new
|
21
|
+
@loaded_hash = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
ensure_loaded!
|
26
|
+
loaded_hash[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch(*args, &block)
|
30
|
+
ensure_loaded!
|
31
|
+
loaded_hash.fetch(*args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def each(&block)
|
35
|
+
ensure_loaded!
|
36
|
+
loaded_hash.each(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def ensure_loaded!
|
42
|
+
return true unless loaded_hash.nil?
|
43
|
+
load_mutex.synchronize do
|
44
|
+
return true unless loaded_hash.nil?
|
45
|
+
@loaded_hash = init_block.call
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Error raised when a Ruby class has no equivalent XDM type to
|
51
|
+
# be converted into
|
11
52
|
class UnmappedRubyTypeError < StandardError
|
12
53
|
def initialize(class_name)
|
13
54
|
@class_name = class_name
|
@@ -54,85 +95,89 @@ module Saxon
|
|
54
95
|
}.freeze
|
55
96
|
|
56
97
|
# A mapping of QNames to XDM type constants
|
57
|
-
QNAME_MAPPING =
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
98
|
+
QNAME_MAPPING = LazyReadOnlyHash.new do
|
99
|
+
{
|
100
|
+
'anyAtomicType' => :ANY_ATOMIC_VALUE,
|
101
|
+
'anyURI' => :ANY_URI,
|
102
|
+
'base64Binary' => :BASE64_BINARY,
|
103
|
+
'boolean' => :BOOLEAN,
|
104
|
+
'byte' => :BYTE,
|
105
|
+
'date' => :DATE,
|
106
|
+
'dateTime' => :DATE_TIME,
|
107
|
+
'dateTimeStamp' => :DATE_TIME_STAMP,
|
108
|
+
'dayTimeDuration' => :DAY_TIME_DURATION,
|
109
|
+
'decimal' => :DECIMAL,
|
110
|
+
'double' => :DOUBLE,
|
111
|
+
'duration' => :DURATION,
|
112
|
+
'ENTITY' => :ENTITY,
|
113
|
+
'float' => :FLOAT,
|
114
|
+
'gDay' => :G_DAY,
|
115
|
+
'gMonth' => :G_MONTH,
|
116
|
+
'gMonthDay' => :G_MONTH_DAY,
|
117
|
+
'gYear' => :G_YEAR,
|
118
|
+
'gYearMonth' => :G_YEAR_MONTH,
|
119
|
+
'hexBinary' => :HEX_BINARY,
|
120
|
+
'ID' => :ID,
|
121
|
+
'IDREF' => :IDREF,
|
122
|
+
'int' => :INT,
|
123
|
+
'integer' => :INTEGER,
|
124
|
+
'language' => :LANGUAGE,
|
125
|
+
'long' => :LONG,
|
126
|
+
'Name' => :NAME,
|
127
|
+
'NCName' => :NCNAME,
|
128
|
+
'negativeInteger' => :NEGATIVE_INTEGER,
|
129
|
+
'NMTOKEN' => :NMTOKEN,
|
130
|
+
'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
|
131
|
+
'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
|
132
|
+
'normalizedString' => :NORMALIZED_STRING,
|
133
|
+
'NOTATION' => :NOTATION,
|
134
|
+
'numeric' => :NUMERIC,
|
135
|
+
'positiveInteger' => :POSITIVE_INTEGER,
|
136
|
+
'QName' => :QNAME,
|
137
|
+
'short' => :SHORT,
|
138
|
+
'string' => :STRING,
|
139
|
+
'time' => :TIME,
|
140
|
+
'token' => :TOKEN,
|
141
|
+
'unsignedByte' => :UNSIGNED_BYTE,
|
142
|
+
'unsignedInt' => :UNSIGNED_INT,
|
143
|
+
'unsignedLong' => :UNSIGNED_LONG,
|
144
|
+
'unsignedShort' => :UNSIGNED_SHORT,
|
145
|
+
'untypedAtomic' => :UNTYPED_ATOMIC,
|
146
|
+
'yearMonthDuration' => :YEAR_MONTH_DURATION
|
147
|
+
}.map { |local_name, constant|
|
148
|
+
qname = Saxon::QName.create({
|
149
|
+
prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
|
150
|
+
local_name: local_name
|
151
|
+
})
|
152
|
+
[qname, constant]
|
153
|
+
}.to_h.freeze
|
154
|
+
end
|
112
155
|
|
113
156
|
# A mapping of type names/QNames to XDM type constants
|
114
|
-
STR_MAPPING =
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
157
|
+
STR_MAPPING = LazyReadOnlyHash.new do
|
158
|
+
{
|
159
|
+
'array(*)' => :ANY_ARRAY,
|
160
|
+
'item()' => :ANY_ITEM,
|
161
|
+
'map(*)' => :ANY_MAP,
|
162
|
+
'node()' => :ANY_NODE
|
163
|
+
}.merge(
|
164
|
+
Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
|
165
|
+
).freeze
|
166
|
+
end
|
122
167
|
|
123
168
|
# convertors to generate lexical strings for a given {ItemType}, as a hash keyed on the ItemType
|
124
|
-
ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS =
|
169
|
+
ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = LazyReadOnlyHash.new do
|
125
170
|
LexicalStringConversion::Convertors.constants.map { |const|
|
126
171
|
[S9API::ItemType.const_get(const), LexicalStringConversion::Convertors.const_get(const)]
|
127
|
-
}
|
128
|
-
|
172
|
+
}.to_h.freeze
|
173
|
+
end
|
129
174
|
|
130
175
|
# convertors from {XDM::AtomicValue} to a ruby primitve value, as a hash keyed on the ItemType
|
131
|
-
ATOMIC_VALUE_TO_RUBY_CONVERTORS =
|
176
|
+
ATOMIC_VALUE_TO_RUBY_CONVERTORS = LazyReadOnlyHash.new do
|
132
177
|
ValueToRuby::Convertors.constants.map { |const|
|
133
178
|
[S9API::ItemType.const_get(const), ValueToRuby::Convertors.const_get(const)]
|
134
|
-
}
|
135
|
-
|
179
|
+
}.to_h.freeze
|
180
|
+
end
|
136
181
|
|
137
182
|
class << self
|
138
183
|
# Get an appropriate {ItemType} for a Ruby type or given a type name as a
|
@@ -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
|