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