saxon-rb 0.6.0-java → 0.8.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -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
- # Error raised when a Ruby class has no equivalent XDM type to be converted
10
- # into
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 = Hash[{
58
- 'anyAtomicType' => :ANY_ATOMIC_VALUE,
59
- 'anyURI' => :ANY_URI,
60
- 'base64Binary' => :BASE64_BINARY,
61
- 'boolean' => :BOOLEAN,
62
- 'byte' => :BYTE,
63
- 'date' => :DATE,
64
- 'dateTime' => :DATE_TIME,
65
- 'dateTimeStamp' => :DATE_TIME_STAMP,
66
- 'dayTimeDuration' => :DAY_TIME_DURATION,
67
- 'decimal' => :DECIMAL,
68
- 'double' => :DOUBLE,
69
- 'duration' => :DURATION,
70
- 'ENTITY' => :ENTITY,
71
- 'float' => :FLOAT,
72
- 'gDay' => :G_DAY,
73
- 'gMonth' => :G_MONTH,
74
- 'gMonthDay' => :G_MONTH_DAY,
75
- 'gYear' => :G_YEAR,
76
- 'gYearMonth' => :G_YEAR_MONTH,
77
- 'hexBinary' => :HEX_BINARY,
78
- 'ID' => :ID,
79
- 'IDREF' => :IDREF,
80
- 'int' => :INT,
81
- 'integer' => :INTEGER,
82
- 'language' => :LANGUAGE,
83
- 'long' => :LONG,
84
- 'Name' => :NAME,
85
- 'NCName' => :NCNAME,
86
- 'negativeInteger' => :NEGATIVE_INTEGER,
87
- 'NMTOKEN' => :NMTOKEN,
88
- 'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
89
- 'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
90
- 'normalizedString' => :NORMALIZED_STRING,
91
- 'NOTATION' => :NOTATION,
92
- 'numeric' => :NUMERIC,
93
- 'positiveInteger' => :POSITIVE_INTEGER,
94
- 'QName' => :QNAME,
95
- 'short' => :SHORT,
96
- 'string' => :STRING,
97
- 'time' => :TIME,
98
- 'token' => :TOKEN,
99
- 'unsignedByte' => :UNSIGNED_BYTE,
100
- 'unsignedInt' => :UNSIGNED_INT,
101
- 'unsignedLong' => :UNSIGNED_LONG,
102
- 'unsignedShort' => :UNSIGNED_SHORT,
103
- 'untypedAtomic' => :UNTYPED_ATOMIC,
104
- 'yearMonthDuration' => :YEAR_MONTH_DURATION
105
- }.map { |local_name, constant|
106
- qname = Saxon::QName.create({
107
- prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
108
- local_name: local_name
109
- })
110
- [qname, constant]
111
- }].freeze
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
- 'array(*)' => :ANY_ARRAY,
116
- 'item()' => :ANY_ITEM,
117
- 'map(*)' => :ANY_MAP,
118
- 'node()' => :ANY_NODE
119
- }.merge(
120
- Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
121
- ).freeze
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 = Hash[
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
- ].freeze
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 = Hash[
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
- ].freeze
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