saxon-rb 0.4.0-java → 0.7.2-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 +429 -42
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/Gemfile +2 -2
- data/README.md +358 -10
- data/Rakefile +237 -7
- data/docs/templates/plugin.rb +73 -0
- data/lib/net/sf/saxon/Saxon-HE/{9.9.1-5/Saxon-HE-9.9.1-5.jar → 9.9.1-6/Saxon-HE-9.9.1-6.jar} +0 -0
- data/lib/saxon-rb.rb +0 -0
- data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
- data/lib/saxon.rb +13 -0
- data/lib/saxon/axis_iterator.rb +8 -1
- data/lib/saxon/configuration.rb +16 -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 +129 -89
- data/lib/saxon/item_type/lexical_string_conversion.rb +214 -59
- data/lib/saxon/item_type/value_to_ruby.rb +25 -0
- data/lib/saxon/loader.rb +6 -1
- data/lib/saxon/nokogiri.rb +78 -0
- data/lib/saxon/occurrence_indicator.rb +32 -3
- data/lib/saxon/processor.rb +50 -5
- data/lib/saxon/qname.rb +37 -2
- data/lib/saxon/s9api.rb +5 -0
- data/lib/saxon/sequence_type.rb +131 -0
- data/lib/saxon/serializer.rb +3 -137
- data/lib/saxon/serializer/destination.rb +80 -0
- data/lib/saxon/serializer/object.rb +93 -0
- data/lib/saxon/serializer/output_properties.rb +83 -0
- data/lib/saxon/source.rb +207 -71
- data/lib/saxon/version.rb +7 -1
- data/lib/saxon/version/library.rb +89 -0
- data/lib/saxon/xdm.rb +7 -0
- data/lib/saxon/xdm/array.rb +16 -0
- data/lib/saxon/xdm/atomic_value.rb +10 -2
- data/lib/saxon/xdm/empty_sequence.rb +13 -0
- data/lib/saxon/xdm/external_object.rb +1 -0
- data/lib/saxon/xdm/function_item.rb +1 -0
- data/lib/saxon/xdm/item.rb +7 -0
- data/lib/saxon/xdm/map.rb +38 -0
- data/lib/saxon/xdm/node.rb +50 -1
- data/lib/saxon/xdm/sequence_like.rb +15 -0
- data/lib/saxon/xdm/value.rb +21 -5
- data/lib/saxon/xpath.rb +9 -0
- data/lib/saxon/xpath/compiler.rb +37 -2
- data/lib/saxon/xpath/executable.rb +53 -28
- data/lib/saxon/xpath/static_context.rb +25 -40
- data/lib/saxon/xpath/variable_declaration.rb +16 -49
- data/lib/saxon/xslt.rb +12 -0
- data/lib/saxon/xslt/compiler.rb +75 -6
- data/lib/saxon/xslt/evaluation_context.rb +30 -4
- data/lib/saxon/xslt/executable.rb +206 -29
- data/lib/saxon/xslt/invocation.rb +97 -0
- data/saxon-rb.gemspec +3 -3
- metadata +22 -10
- data/saxon.gemspec +0 -30
@@ -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,13 +6,55 @@ 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
|
14
55
|
end
|
15
56
|
|
57
|
+
# error message including class name no type equivalent found for
|
16
58
|
def to_s
|
17
59
|
"Ruby class <#{@class_name}> has no XDM type equivalent"
|
18
60
|
end
|
@@ -26,29 +68,14 @@ module Saxon
|
|
26
68
|
@type_str = type_str
|
27
69
|
end
|
28
70
|
|
71
|
+
# error message including type string with no matching built-in type
|
29
72
|
def to_s
|
30
73
|
"'#{@type_str}' is not recognised as an XSD built-in type"
|
31
74
|
end
|
32
75
|
end
|
33
76
|
|
34
|
-
class Factory
|
35
|
-
DEFAULT_SEMAPHORE = Mutex.new
|
36
|
-
|
37
|
-
attr_reader :processor
|
38
|
-
|
39
|
-
def initialize(processor)
|
40
|
-
@processor = processor
|
41
|
-
end
|
42
|
-
|
43
|
-
def s9_factory
|
44
|
-
return @s9_factory if instance_variable_defined?(:@s9_factory)
|
45
|
-
DEFAULT_SEMAPHORE.synchronize do
|
46
|
-
@s9_factory = S9API::ItemTypeFactory.new(processor.to_java)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
77
|
TYPE_CACHE_MUTEX = Mutex.new
|
78
|
+
private_constant :TYPE_CACHE_MUTEX
|
52
79
|
# A mapping of Ruby types to XDM type constants
|
53
80
|
TYPE_MAPPING = {
|
54
81
|
'String' => :STRING,
|
@@ -61,88 +88,96 @@ module Saxon
|
|
61
88
|
'Time' => :DATE_TIME,
|
62
89
|
'BigDecimal' => :DECIMAL,
|
63
90
|
'Integer' => :INTEGER,
|
91
|
+
'Fixnum' => :INTEGER, # Fixnum/Bignum needed for JRuby 9.1/Ruby 2.3
|
92
|
+
'Bignum' => :INTEGER,
|
64
93
|
'Float' => :FLOAT,
|
65
94
|
'Numeric' => :NUMERIC
|
66
95
|
}.freeze
|
67
96
|
|
68
97
|
# A mapping of QNames to XDM type constants
|
69
|
-
QNAME_MAPPING =
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
124
155
|
|
125
156
|
# A mapping of type names/QNames to XDM type constants
|
126
|
-
STR_MAPPING =
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
167
|
+
|
168
|
+
# convertors to generate lexical strings for a given {ItemType}, as a hash keyed on the ItemType
|
169
|
+
ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = LazyReadOnlyHash.new do
|
136
170
|
LexicalStringConversion::Convertors.constants.map { |const|
|
137
171
|
[S9API::ItemType.const_get(const), LexicalStringConversion::Convertors.const_get(const)]
|
138
|
-
}
|
139
|
-
|
172
|
+
}.to_h.freeze
|
173
|
+
end
|
140
174
|
|
141
|
-
|
175
|
+
# convertors from {XDM::AtomicValue} to a ruby primitve value, as a hash keyed on the ItemType
|
176
|
+
ATOMIC_VALUE_TO_RUBY_CONVERTORS = LazyReadOnlyHash.new do
|
142
177
|
ValueToRuby::Convertors.constants.map { |const|
|
143
178
|
[S9API::ItemType.const_get(const), ValueToRuby::Convertors.const_get(const)]
|
144
|
-
}
|
145
|
-
|
179
|
+
}.to_h.freeze
|
180
|
+
end
|
146
181
|
|
147
182
|
class << self
|
148
183
|
# Get an appropriate {ItemType} for a Ruby type or given a type name as a
|
@@ -155,6 +190,7 @@ module Saxon
|
|
155
190
|
# @overload get_type(type_name)
|
156
191
|
# Get the {ItemType} for the name
|
157
192
|
# @param type_name [String] name of the built-in {ItemType} to fetch
|
193
|
+
# (e.g. +xs:string+ or +element()+)
|
158
194
|
# @overload get_type(item_type)
|
159
195
|
# Given an instance of {ItemType}, simply return the instance
|
160
196
|
# @param item_type [Saxon::ItemType] an existing ItemType instance
|
@@ -182,6 +218,8 @@ module Saxon
|
|
182
218
|
|
183
219
|
def get_s9_type(arg)
|
184
220
|
case arg
|
221
|
+
when S9API::ItemType
|
222
|
+
arg
|
185
223
|
when Saxon::QName
|
186
224
|
get_s9_qname_mapped_type(arg)
|
187
225
|
when Class
|
@@ -250,6 +288,8 @@ module Saxon
|
|
250
288
|
|
251
289
|
alias_method :eql?, :==
|
252
290
|
|
291
|
+
# Return a hash code so this can be used as a key in a {::Hash}.
|
292
|
+
# @return [Fixnum] the hash code
|
253
293
|
def hash
|
254
294
|
@hash ||= s9_item_type.hashCode
|
255
295
|
end
|
@@ -5,12 +5,26 @@ module Saxon
|
|
5
5
|
# A collection of lamba-like objects for converting Ruby values into
|
6
6
|
# lexical strings for specific XSD datatypes
|
7
7
|
module LexicalStringConversion
|
8
|
+
# Simple validation helper that checks if a value string matches an
|
9
|
+
# allowed lexical string pattern space or not.
|
10
|
+
#
|
11
|
+
# @param value [Object] the value whose to_s representation should be
|
12
|
+
# checked
|
13
|
+
# @param item_type [Saxon::ItemType] the ItemType whose lexical pattern
|
14
|
+
# space should be checked against
|
15
|
+
# @param pattern [Regexp] the lexical pattern space Regexp to use in the
|
16
|
+
# checking
|
17
|
+
# @return [String] the lexical string for the value and type
|
18
|
+
# @raise [Errors::BadRubyValue] if the ruby value doesn't produce a string
|
19
|
+
# which validates against the allowed pattern
|
8
20
|
def self.validate(value, item_type, pattern)
|
9
21
|
str = value.to_s
|
10
|
-
raise Errors::BadRubyValue.new(value, item_type)
|
22
|
+
raise Errors::BadRubyValue.new(value, item_type) if str.match(pattern).nil?
|
11
23
|
str
|
12
24
|
end
|
13
25
|
|
26
|
+
# Helper class for performing conversion and validation to XDM integer
|
27
|
+
# types from Ruby's Fixnum/Bignum/Integer classes
|
14
28
|
class IntegerConversion
|
15
29
|
attr_reader :min, :max
|
16
30
|
|
@@ -18,20 +32,40 @@ module Saxon
|
|
18
32
|
@min, @max = min, max
|
19
33
|
end
|
20
34
|
|
35
|
+
# Returns whether the Ruby integer is within the range allowed for the
|
36
|
+
# XDM type
|
37
|
+
# @param integer_value [Integer] the ruby integer to check
|
38
|
+
# @return [Boolean] whether the value is within bounds
|
21
39
|
def in_bounds?(integer_value)
|
22
40
|
gte_min?(integer_value) && lte_max?(integer_value)
|
23
41
|
end
|
24
42
|
|
43
|
+
# Returns whether the Ruby integer is >= the lower bound of the range
|
44
|
+
# allowed for the XDM type
|
45
|
+
# @param integer_value [Integer] the ruby integer to check
|
46
|
+
# @return [Boolean] whether the value is okay
|
25
47
|
def gte_min?(integer_value)
|
26
48
|
return true if min.nil?
|
27
49
|
integer_value >= min
|
28
50
|
end
|
29
51
|
|
52
|
+
# Returns whether the Ruby integer is <= the upper bound of the range
|
53
|
+
# allowed for the XDM type
|
54
|
+
# @param integer_value [Integer] the ruby integer to check
|
55
|
+
# @return [Boolean] whether the value is okay
|
30
56
|
def lte_max?(integer_value)
|
31
57
|
return true if max.nil?
|
32
58
|
integer_value <= max
|
33
59
|
end
|
34
60
|
|
61
|
+
# Check a value against our type constraints, and return the lexical
|
62
|
+
# string representation if it's okay.
|
63
|
+
#
|
64
|
+
# @param value [Integer] the ruby value
|
65
|
+
# @param item_type [Saxon::ItemType] the item type
|
66
|
+
# @return [String] the lexical string representation of the value
|
67
|
+
# @raise [Errors::RubyValueOutOfBounds] if the value is outside the
|
68
|
+
# type's permitted bounds
|
35
69
|
def call(value, item_type)
|
36
70
|
integer_value = case value
|
37
71
|
when ::Numeric
|
@@ -44,15 +78,42 @@ module Saxon
|
|
44
78
|
end
|
45
79
|
end
|
46
80
|
|
81
|
+
# Helper class for performing conversion and validation to XDM
|
82
|
+
# Floating-point types from Ruby's Float class
|
47
83
|
class FloatConversion
|
48
84
|
def initialize(size = :double)
|
49
85
|
@double = size == :double
|
50
86
|
end
|
51
87
|
|
88
|
+
# Check a value against our type constraints, and return the lexical
|
89
|
+
# string representation if it's okay.
|
90
|
+
#
|
91
|
+
# @param value [Float] the ruby value
|
92
|
+
# @param item_type [Saxon::ItemType] the item type
|
93
|
+
# @return [String] the lexical string representation of the value
|
94
|
+
def call(value, item_type)
|
95
|
+
case value
|
96
|
+
when ::Float::INFINITY
|
97
|
+
'INF'
|
98
|
+
when -::Float::INFINITY
|
99
|
+
'-INF'
|
100
|
+
when Numeric
|
101
|
+
float_value(value).to_s
|
102
|
+
else
|
103
|
+
LexicalStringConversion.validate(value, item_type, Patterns::FLOAT)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Is this a double-precision XDM float?
|
110
|
+
# @return [Boolean] true if we're converting a double-precision float
|
52
111
|
def double?
|
53
112
|
@double
|
54
113
|
end
|
55
114
|
|
115
|
+
# Return the float as either a double-precision or single-precision
|
116
|
+
# float as needed
|
56
117
|
def float_value(float_value)
|
57
118
|
return float_value if double?
|
58
119
|
convert_to_single_precision(float_value)
|
@@ -61,30 +122,72 @@ module Saxon
|
|
61
122
|
def convert_to_single_precision(float_value)
|
62
123
|
[float_value].pack('f').unpack('f').first
|
63
124
|
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Convert a value in seconds into an XDM Duration string
|
128
|
+
class DurationConversion
|
129
|
+
attr_reader :pattern
|
64
130
|
|
131
|
+
def initialize(pattern)
|
132
|
+
@pattern = pattern
|
133
|
+
end
|
134
|
+
|
135
|
+
# Produce a lexical Duration string from a numeric Ruby value
|
136
|
+
# representing seconds
|
65
137
|
def call(value, item_type)
|
138
|
+
return numeric(value) if Numeric === value
|
139
|
+
LexicalStringConversion.validate(value, item_type, pattern)
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def numeric(value)
|
145
|
+
sign = value.negative? ? '-' : ''
|
66
146
|
case value
|
67
|
-
when
|
68
|
-
|
69
|
-
when
|
70
|
-
'
|
71
|
-
when Numeric
|
72
|
-
float_value(value).to_s
|
147
|
+
when Integer
|
148
|
+
"#{sign}PT#{value.abs}S"
|
149
|
+
when BigDecimal
|
150
|
+
"#{sign}PT#{value.abs.to_s('F')}S"
|
73
151
|
else
|
74
|
-
|
152
|
+
sprintf("%sPT%0.9fS", sign, value.abs)
|
75
153
|
end
|
76
154
|
end
|
77
155
|
end
|
78
156
|
|
157
|
+
# Helper class for creating convertors for the various G* Date-related
|
158
|
+
# types that allow single values (GDay, GMonth, GYear).
|
79
159
|
class GDateConversion
|
80
160
|
attr_reader :bounds, :integer_formatter, :validation_pattern
|
81
161
|
|
82
|
-
|
162
|
+
# @param args [Hash]
|
163
|
+
# @option args [Range] :bounds the integer bounds for values of this type
|
164
|
+
# @option args [Regexp] :validation_pattern the pattern used to validate the
|
165
|
+
# value when it's a String not an Integer
|
166
|
+
# @option args [Proc] :integer_formatter a proc/lambda that will produce a
|
167
|
+
# correctly-formatted lexical string from an Integer value
|
168
|
+
def initialize(args = {})
|
83
169
|
@bounds = args.fetch(:bounds)
|
84
170
|
@validation_pattern = args.fetch(:validation_pattern)
|
85
171
|
@integer_formatter = args.fetch(:integer_formatter)
|
86
172
|
end
|
87
173
|
|
174
|
+
# @param value [String, Integer] the value to convert
|
175
|
+
# @param item_type [XDM::ItemType] the type being converted to
|
176
|
+
# @return [String] a correctly formatted String
|
177
|
+
def call(value, item_type)
|
178
|
+
case value
|
179
|
+
when Integer
|
180
|
+
check_value_bounds!(value, item_type)
|
181
|
+
sprintf(integer_formatter.call(value), value)
|
182
|
+
else
|
183
|
+
formatted_value = LexicalStringConversion.validate(value, item_type, validation_pattern)
|
184
|
+
extract_and_check_value_bounds!(formatted_value, item_type)
|
185
|
+
formatted_value
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
88
191
|
def extract_value_from_validated_format(formatted_value)
|
89
192
|
Integer(formatted_value.gsub(validation_pattern, '\1'), 10)
|
90
193
|
end
|
@@ -97,24 +200,31 @@ module Saxon
|
|
97
200
|
def extract_and_check_value_bounds!(formatted_value, item_type)
|
98
201
|
check_value_bounds!(extract_value_from_validated_format(formatted_value), item_type)
|
99
202
|
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Convert Bytes. Idiomatically, Ruby uses +ASCII_8BIT+ encoded strings to
|
206
|
+
# represent bytes, and so a single character represents a single byte. XDM
|
207
|
+
# uses the decimal value of a signed or unsigned 8 bit integer
|
208
|
+
class ByteConversion
|
209
|
+
attr_reader :unpack_format
|
210
|
+
|
211
|
+
def initialize(kind = :signed)
|
212
|
+
@unpack_format = kind == :unsigned ? 'C' : 'c'
|
213
|
+
end
|
100
214
|
|
101
215
|
def call(value, item_type)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
sprintf(integer_formatter.call(value), value)
|
106
|
-
else
|
107
|
-
formatted_value = LexicalStringConversion.validate(value, item_type, validation_pattern)
|
108
|
-
extract_and_check_value_bounds!(formatted_value, item_type)
|
109
|
-
formatted_value
|
110
|
-
end
|
216
|
+
raise Errors::RubyValueOutOfBounds.new(value, item_type) if value.bytesize != 1
|
217
|
+
value = value.to_s.force_encoding(Encoding::ASCII_8BIT)
|
218
|
+
value.unpack(unpack_format).first.to_s
|
111
219
|
end
|
112
220
|
end
|
113
221
|
|
222
|
+
# Pattern fragments that can be combined to help create the lexical space
|
223
|
+
# patterns in {Patterns}
|
114
224
|
module PatternFragments
|
225
|
+
# The time part of the XSD Duration format allows T0H1M1S, T1H1M, T1M1S, T1H1S, T1H, T1M, T1S
|
115
226
|
TIME_DURATION = /(?:T
|
116
227
|
(?:
|
117
|
-
# The time part of the format allows T0H1M1S, T1H1M, T1M1S, T1H1S, T1H, T1M, T1S
|
118
228
|
[0-9]+[HM]|
|
119
229
|
[0-9]+(?:\.[0-9]+)?S|
|
120
230
|
[0-9]+H[0-9]+M|
|
@@ -123,43 +233,78 @@ module Saxon
|
|
123
233
|
[0-9]+H[0-9]+M[0-9]+(?:\.[0-9]+)?S
|
124
234
|
)
|
125
235
|
)?/x
|
236
|
+
# XSD Date
|
126
237
|
DATE = /-?[0-9]{4}-[0-9]{2}-[0-9]{2}/
|
238
|
+
# XSD DateTime Time
|
127
239
|
TIME = /[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?/
|
240
|
+
# XSD DateTime Timezone
|
128
241
|
TIME_ZONE = /(?:[\-+][0-9]{2}:[0-9]{2}|Z)?/
|
242
|
+
# Valid first characers in an NCName
|
129
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
|
130
245
|
NAME_START_CHAR = ":|" + NCNAME_START_CHAR
|
246
|
+
# Valid characters within an NCName
|
131
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
|
132
249
|
NAME_CHAR = ":|" + NCNAME_CHAR
|
133
250
|
end
|
134
251
|
|
252
|
+
# A collection of lexical space patterns for XDM types
|
135
253
|
module Patterns
|
254
|
+
# Construct a Regexp from an array of patterns
|
255
|
+
# @param patterns [Array<String>]
|
256
|
+
# @return [Regexp]
|
136
257
|
def self.build(*patterns)
|
137
258
|
Regexp.new((['\A'] + patterns.map(&:to_s) + ['\z']).join(''))
|
138
259
|
end
|
260
|
+
# @see https://www.w3.org/TR/xmlschema-2/#date-lexical-representation
|
139
261
|
DATE = build(PatternFragments::DATE, PatternFragments::TIME_ZONE)
|
262
|
+
# @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
|
140
263
|
DATE_TIME = build(PatternFragments::DATE, 'T', PatternFragments::TIME, PatternFragments::TIME_ZONE)
|
264
|
+
# @see https://www.w3.org/TR/xmlschema-2/#time-lexical-repr
|
141
265
|
TIME = build(PatternFragments::TIME, PatternFragments::TIME_ZONE)
|
266
|
+
# @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
|
142
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
|
143
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
|
144
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
|
145
273
|
G_DAY = build(/---([0-9]{2})/, PatternFragments::TIME_ZONE)
|
274
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gMonth-lexical-repr
|
146
275
|
G_MONTH = build(/--([0-9]{2})/, PatternFragments::TIME_ZONE)
|
276
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gYear-lexical-repr
|
147
277
|
G_YEAR = build(/(-?[0-9]{4,})/, PatternFragments::TIME_ZONE)
|
278
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gYearMonth-lexical-repr
|
148
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
|
149
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
|
150
283
|
INTEGER = /\A[+-]?[0-9]+\z/
|
284
|
+
# @see https://www.w3.org/TR/xmlschema-2/#decimal-lexical-representation
|
151
285
|
DECIMAL = /\A[+-]?[0-9]+(?:\.[0-9]+)?\z/
|
286
|
+
# @see https://www.w3.org/TR/xmlschema-2/#float-lexical-representation
|
152
287
|
FLOAT = /\A(?:[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][0-9]+)?|-?INF|NaN)\z/
|
288
|
+
# @see https://www.w3.org/TR/xmlschema-2/#NCName
|
153
289
|
NCNAME = build("(?:#{PatternFragments::NCNAME_START_CHAR})", "(?:#{PatternFragments::NCNAME_CHAR})*")
|
290
|
+
# @see https://www.w3.org/TR/xmlschema-2/#Name
|
154
291
|
NAME = build("(?:#{PatternFragments::NAME_START_CHAR})", "(?:#{PatternFragments::NAME_CHAR})*")
|
292
|
+
# @see https://www.w3.org/TR/xmlschema-2/#token
|
155
293
|
TOKEN = /\A[^\u0020\u000A\u000D\u0009]+(?: [^\u0020\u000A\u000D\u0009]+)*\z/
|
294
|
+
# @see https://www.w3.org/TR/xmlschema-2/#normalizedString
|
156
295
|
NORMALIZED_STRING = /\A[^\u000A\u000D\u0009]+\z/
|
296
|
+
# @see https://www.w3.org/TR/xmlschema-2/#NMTOKEN
|
157
297
|
NMTOKEN = build("(?:#{PatternFragments::NAME_CHAR})+")
|
298
|
+
# @see https://www.w3.org/TR/xmlschema-2/#language
|
158
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
|
159
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/
|
160
302
|
end
|
161
303
|
|
304
|
+
# Convertors from Ruby values to lexical string representations for a
|
305
|
+
# particular XDM type
|
162
306
|
module Convertors
|
307
|
+
# @see https://www.w3.org/TR/xmlschema-2/#anyURI
|
163
308
|
ANY_URI = ->(value, item_type) {
|
164
309
|
uri_classes = [URI::Generic]
|
165
310
|
case value
|
@@ -173,17 +318,17 @@ module Saxon
|
|
173
318
|
end
|
174
319
|
end
|
175
320
|
}
|
321
|
+
# @see https://www.w3.org/TR/xmlschema-2/#base64Binary
|
176
322
|
BASE64_BINARY = ->(value, item_type) {
|
177
323
|
Base64.strict_encode64(value.to_s.force_encoding(Encoding::ASCII_8BIT))
|
178
324
|
}
|
325
|
+
# @see https://www.w3.org/TR/xmlschema-2/#boolean
|
179
326
|
BOOLEAN = ->(value, item_type) {
|
180
327
|
value ? 'true' : 'false'
|
181
328
|
}
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
value.unpack('c').first.to_s
|
186
|
-
}
|
329
|
+
# @see https://www.w3.org/TR/xmlschema-2/#byte
|
330
|
+
BYTE = ByteConversion.new
|
331
|
+
# @see https://www.w3.org/TR/xmlschema-2/#date
|
187
332
|
DATE = ->(value, item_type) {
|
188
333
|
if value.respond_to?(:strftime)
|
189
334
|
value.strftime('%F')
|
@@ -191,6 +336,7 @@ module Saxon
|
|
191
336
|
LexicalStringConversion.validate(value, item_type, Patterns::DATE)
|
192
337
|
end
|
193
338
|
}
|
339
|
+
# @see https://www.w3.org/TR/xmlschema-2/#dateTime
|
194
340
|
DATE_TIME = ->(value, item_type) {
|
195
341
|
if value.respond_to?(:strftime)
|
196
342
|
value.strftime('%FT%T%:z')
|
@@ -198,25 +344,15 @@ module Saxon
|
|
198
344
|
LexicalStringConversion.validate(value, item_type, Patterns::DATE_TIME)
|
199
345
|
end
|
200
346
|
}
|
347
|
+
# @see https://www.w3.org/TR/xmlschema-2/#time
|
201
348
|
TIME = ->(value, item_type) {
|
202
349
|
LexicalStringConversion.validate(value, item_type, Patterns::TIME)
|
203
350
|
}
|
351
|
+
# @see https://www.w3.org/TR/xmlschema-2/#dateTime
|
204
352
|
DATE_TIME_STAMP = DATE_TIME
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
sign = value.negative? ? '-' : ''
|
209
|
-
"#{sign}PT#{value.abs}S"
|
210
|
-
when BigDecimal
|
211
|
-
sign = value.negative? ? '-' : ''
|
212
|
-
"#{sign}PT#{value.abs.to_s('F')}S"
|
213
|
-
when Numeric
|
214
|
-
sign = value.negative? ? '-' : ''
|
215
|
-
sprintf("%sPT%0.9fS", sign, value.abs)
|
216
|
-
else
|
217
|
-
LexicalStringConversion.validate(value, item_type, Patterns::DAY_TIME_DURATION)
|
218
|
-
end
|
219
|
-
}
|
353
|
+
# @see https://www.w3.org/TR/xmlschema-2/#duration
|
354
|
+
DAY_TIME_DURATION = DurationConversion.new(Patterns::DAY_TIME_DURATION)
|
355
|
+
# @see https://www.w3.org/TR/xmlschema-2/#decimal
|
220
356
|
DECIMAL = ->(value, item_type) {
|
221
357
|
case value
|
222
358
|
when ::Integer
|
@@ -229,33 +365,25 @@ module Saxon
|
|
229
365
|
LexicalStringConversion.validate(value, item_type, Patterns::DECIMAL)
|
230
366
|
end
|
231
367
|
}
|
368
|
+
# @see https://www.w3.org/TR/xmlschema-2/#double
|
232
369
|
DOUBLE = FloatConversion.new(:single)
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
sign = value.negative? ? '-' : ''
|
237
|
-
"#{sign}PT#{value.abs}S"
|
238
|
-
when BigDecimal
|
239
|
-
sign = value.negative? ? '-' : ''
|
240
|
-
"#{sign}PT#{value.abs.to_s('F')}S"
|
241
|
-
when Numeric
|
242
|
-
sign = value.negative? ? '-' : ''
|
243
|
-
sprintf("%sPT%0.9fS", sign, value.abs)
|
244
|
-
else
|
245
|
-
LexicalStringConversion.validate(value, item_type, Patterns::DURATION)
|
246
|
-
end
|
247
|
-
}
|
370
|
+
# @see https://www.w3.org/TR/xmlschema-2/#duration
|
371
|
+
DURATION = DurationConversion.new(Patterns::DURATION)
|
372
|
+
# @see https://www.w3.org/TR/xmlschema-2/#float
|
248
373
|
FLOAT = FloatConversion.new
|
374
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gDay
|
249
375
|
G_DAY = GDateConversion.new({
|
250
376
|
bounds: 1..31,
|
251
377
|
validation_pattern: Patterns::G_DAY,
|
252
378
|
integer_formatter: ->(value) { '---%02d' }
|
253
379
|
})
|
380
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gMonth
|
254
381
|
G_MONTH = GDateConversion.new({
|
255
382
|
bounds: 1..12,
|
256
383
|
validation_pattern: Patterns::G_MONTH,
|
257
384
|
integer_formatter: ->(value) { '--%02d' }
|
258
385
|
})
|
386
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gMonthDay
|
259
387
|
G_MONTH_DAY = ->(value, item_type) {
|
260
388
|
month_days = {
|
261
389
|
1 => 31,
|
@@ -278,6 +406,7 @@ module Saxon
|
|
278
406
|
raise Errors::RubyValueOutOfBounds.new(value, item_type) if day > month_days[month]
|
279
407
|
formatted_value
|
280
408
|
}
|
409
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gYear
|
281
410
|
G_YEAR = GDateConversion.new({
|
282
411
|
bounds: ->(value) { value != 0 },
|
283
412
|
validation_pattern: Patterns::G_YEAR,
|
@@ -285,6 +414,7 @@ module Saxon
|
|
285
414
|
value.negative? ? '%05d' : '%04d'
|
286
415
|
}
|
287
416
|
})
|
417
|
+
# @see https://www.w3.org/TR/xmlschema-2/#gYearMonth
|
288
418
|
G_YEAR_MONTH = ->(value, item_type) {
|
289
419
|
formatted_value = LexicalStringConversion.validate(value, item_type, Patterns::G_YEAR_MONTH)
|
290
420
|
year, month = Patterns::G_YEAR_MONTH.match(formatted_value).captures.take(2).map { |i|
|
@@ -295,52 +425,74 @@ module Saxon
|
|
295
425
|
end
|
296
426
|
value
|
297
427
|
}
|
428
|
+
# @see https://www.w3.org/TR/xmlschema-2/#hexBinary
|
298
429
|
HEX_BINARY = ->(value, item_type) {
|
299
430
|
value.to_s.force_encoding(Encoding::ASCII_8BIT).each_byte.map { |b| b.to_s(16) }.join
|
300
431
|
}
|
432
|
+
# @see https://www.w3.org/TR/xmlschema-2/#int
|
301
433
|
INT = IntegerConversion.new(-2147483648, 2147483647)
|
434
|
+
# @see https://www.w3.org/TR/xmlschema-2/#integer
|
302
435
|
INTEGER = IntegerConversion.new(nil, nil)
|
436
|
+
# @see https://www.w3.org/TR/xmlschema-2/#language
|
303
437
|
LANGUAGE = ->(value, item_type) {
|
304
438
|
LexicalStringConversion.validate(value, item_type, Patterns::LANGUAGE)
|
305
439
|
}
|
440
|
+
# @see https://www.w3.org/TR/xmlschema-2/#long
|
306
441
|
LONG = IntegerConversion.new(-9223372036854775808, 9223372036854775807)
|
442
|
+
# @see https://www.w3.org/TR/xmlschema-2/#Name
|
307
443
|
NAME = ->(value, item_type) {
|
308
444
|
LexicalStringConversion.validate(value, item_type, Patterns::NAME)
|
309
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
|
310
450
|
ID = IDREF = ENTITY = NCNAME = ->(value, item_type) {
|
311
451
|
LexicalStringConversion.validate(value, item_type, Patterns::NCNAME)
|
312
452
|
}
|
453
|
+
# @see https://www.w3.org/TR/xmlschema-2/#negativeInteger
|
313
454
|
NEGATIVE_INTEGER = IntegerConversion.new(nil, -1)
|
455
|
+
# @see https://www.w3.org/TR/xmlschema-2/#NMTOKEN
|
314
456
|
NMTOKEN = ->(value, item_type) {
|
315
457
|
LexicalStringConversion.validate(value, item_type, Patterns::NMTOKEN)
|
316
458
|
}
|
459
|
+
# @see https://www.w3.org/TR/xmlschema-2/#nonNegativeInteger
|
317
460
|
NON_NEGATIVE_INTEGER = IntegerConversion.new(0, nil)
|
461
|
+
# @see https://www.w3.org/TR/xmlschema-2/#nonPositiveInteger
|
318
462
|
NON_POSITIVE_INTEGER = IntegerConversion.new(nil, 0)
|
463
|
+
# @see https://www.w3.org/TR/xmlschema-2/#normalizedString
|
319
464
|
NORMALIZED_STRING = ->(value, item_type) {
|
320
465
|
LexicalStringConversion.validate(value, item_type, Patterns::NORMALIZED_STRING)
|
321
466
|
}
|
467
|
+
# @see https://www.w3.org/TR/xmlschema-2/#positiveInteger
|
322
468
|
POSITIVE_INTEGER = IntegerConversion.new(1, nil)
|
469
|
+
# @see https://www.w3.org/TR/xmlschema-2/#short
|
323
470
|
SHORT = IntegerConversion.new(-32768, 32767)
|
324
471
|
# STRING (It's questionable whether anything needs doing here)
|
472
|
+
# @see https://www.w3.org/TR/xmlschema-2/#token
|
325
473
|
TOKEN = ->(value, item_type) {
|
326
474
|
LexicalStringConversion.validate(value, item_type, Patterns::TOKEN)
|
327
475
|
}
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
value.unpack('C').first.to_s
|
332
|
-
}
|
476
|
+
# @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
|
477
|
+
UNSIGNED_BYTE = ByteConversion.new(:unsigned)
|
478
|
+
# @see https://www.w3.org/TR/xmlschema-2/#unsignedInt
|
333
479
|
UNSIGNED_INT = IntegerConversion.new(0, 4294967295)
|
480
|
+
# @see https://www.w3.org/TR/xmlschema-2/#unsignedLong
|
334
481
|
UNSIGNED_LONG = IntegerConversion.new(0, 18446744073709551615)
|
482
|
+
# @see https://www.w3.org/TR/xmlschema-2/#unsignedShort
|
335
483
|
UNSIGNED_SHORT = IntegerConversion.new(0, 65535)
|
484
|
+
# @see https://www.w3.org/TR/xmlschema-2/#duration
|
336
485
|
YEAR_MONTH_DURATION = ->(value, item_type) {
|
337
486
|
LexicalStringConversion.validate(value, item_type, Patterns::YEAR_MONTH_DURATION)
|
338
487
|
}
|
488
|
+
# @see https://www.w3.org/TR/xmlschema-2/#QName
|
489
|
+
# @see https://www.w3.org/TR/xmlschema-2/#NOTATION
|
339
490
|
QNAME = NOTATION = ->(value, item_type) {
|
340
491
|
raise Errors::UnconvertableNamespaceSensitveItemType
|
341
492
|
}
|
342
493
|
end
|
343
494
|
|
495
|
+
# Conversion process error classes
|
344
496
|
module Errors
|
345
497
|
# Raised during conversion from Ruby value to XDM Type lexical string
|
346
498
|
# when the ruby value does not conform to the Type's string
|
@@ -352,6 +504,8 @@ module Saxon
|
|
352
504
|
@value, @item_type = value, item_type
|
353
505
|
end
|
354
506
|
|
507
|
+
# error message includes Ruby value and the XDM type conversion was
|
508
|
+
# being attempted to
|
355
509
|
def to_s
|
356
510
|
"Ruby value #{value.inspect} cannot be converted to an XDM #{item_type.type_name.to_s}"
|
357
511
|
end
|
@@ -368,6 +522,7 @@ module Saxon
|
|
368
522
|
@value, @item_type = value, item_type
|
369
523
|
end
|
370
524
|
|
525
|
+
# error message includes Ruby value and the XDM type whose bounds it is outside of
|
371
526
|
def to_s
|
372
527
|
"Ruby value #{value.inspect} is outside the allowed bounds of an XDM #{item_type.type_name.to_s}"
|
373
528
|
end
|