saxon-rb 0.4.0-java → 0.7.2-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 +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
|